first commit
@@ -0,0 +1,16 @@
|
||||
dependencies {
|
||||
implementation(project(":hero-api", configuration = "namedElements"))
|
||||
implementation(project(":datatracker", configuration = "namedElements"))
|
||||
|
||||
modApi(libs.bundles.fabric)
|
||||
modApi(libs.bundles.silk)
|
||||
modApi(libs.bundles.performance)
|
||||
modApi(libs.owolib)
|
||||
modApi(libs.geckolib)
|
||||
modApi(libs.emoteLib)
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath.set(file("src/main/resources/aang.accesswidener"))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package gg.norisk.heroes.aang.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.Box;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin {
|
||||
@ModifyReturnValue(
|
||||
method = "calculateBoundingBox",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private Box airScooterBoxInjection(Box original) {
|
||||
return AirScooterAbility.INSTANCE.handleBox((Entity) (Object) this, original);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package gg.norisk.heroes.aang.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public abstract class LivingEntityMixin {
|
||||
@ModifyExpressionValue(
|
||||
method = "travel",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;isOnGround()Z", ordinal = 2)
|
||||
)
|
||||
private boolean isOnGroundInjection(boolean original) {
|
||||
return AirScooterAbility.INSTANCE.handleDrag((Entity) (Object) this) || original;
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = "getMovementSpeed(F)F",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;isOnGround()Z")
|
||||
)
|
||||
private boolean isOnGroundInjection2(boolean original) {
|
||||
return AirScooterAbility.INSTANCE.handleDrag((Entity) (Object) this) || original;
|
||||
}
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "canWalkOnFluid",
|
||||
at = @At(value = "RETURN")
|
||||
)
|
||||
private boolean canWalkOnFluidInjection(boolean original) {
|
||||
return AirScooterAbility.INSTANCE.handleDrag((Entity) (Object) this) || original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package gg.norisk.heroes.aang.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility;
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility;
|
||||
import gg.norisk.heroes.aang.ability.LevitationAbility;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import gg.norisk.heroes.aang.entity.IAangPlayer;
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity;
|
||||
import gg.norisk.heroes.aang.utils.EntitySpinTracker;
|
||||
import gg.norisk.heroes.aang.utils.PlayerRotationTracker;
|
||||
import kotlinx.coroutines.Job;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.data.DataTracker;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArgs;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class PlayerEntityMixin extends LivingEntity implements IAangPlayer {
|
||||
@Shadow
|
||||
public abstract void remove(RemovalReason removalReason);
|
||||
|
||||
@Unique
|
||||
private PlayerRotationTracker rotationTracker;
|
||||
@Unique
|
||||
private final List<Job> airScooterTasks = new ArrayList<>();
|
||||
@Unique
|
||||
private final List<Job> tornadoTasks = new ArrayList<>();
|
||||
@Unique
|
||||
private final List<Job> spiritualProjectionTasks = new ArrayList<>();
|
||||
@Unique
|
||||
private TornadoEntity tornadoEntity;
|
||||
@Unique
|
||||
private final EntitySpinTracker entitySpinTracker = new EntitySpinTracker();
|
||||
|
||||
protected PlayerEntityMixin(EntityType<? extends LivingEntity> entityType, World world) {
|
||||
super(entityType, world);
|
||||
}
|
||||
|
||||
@Inject(method = "travel", at = @At("HEAD"), cancellable = true)
|
||||
private void airScooterTravelInjection(Vec3d vec3d, CallbackInfo ci) {
|
||||
var player = (PlayerEntity) (Object) this;
|
||||
if (AirScooterAbility.INSTANCE.isAirScooting(player)) {
|
||||
super.travel(AirScooterAbility.INSTANCE.handleTravel(player, vec3d));
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/PlayerEntity;noClip:Z", shift = At.Shift.AFTER))
|
||||
private void handleAangTickAfterNoClip(CallbackInfo ci) {
|
||||
SpiritualProjectionAbility.INSTANCE.handleTick((PlayerEntity) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "tick", at = @At("TAIL"))
|
||||
private void handleAangTick(CallbackInfo ci) {
|
||||
AirBallAbility.INSTANCE.handleTick((PlayerEntity) (Object) this);
|
||||
LevitationAbility.INSTANCE.handleTick((PlayerEntity) (Object) this);
|
||||
}
|
||||
|
||||
@Inject(method = "handleFallDamage", at = @At("HEAD"), cancellable = true)
|
||||
private void handleFallAangInjection(float f, float g, DamageSource damageSource, CallbackInfoReturnable<Boolean> cir) {
|
||||
AirScooterAbility.INSTANCE.handleFallDamage((PlayerEntity) (Object) this, f, g, damageSource, cir);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PlayerRotationTracker getRotationTracker() {
|
||||
return rotationTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRotationTracker(PlayerRotationTracker rotationTracker) {
|
||||
this.rotationTracker = rotationTracker;
|
||||
}
|
||||
|
||||
// DUMMY PLAYER STUFF
|
||||
|
||||
@ModifyArgs(method = "getDisplayName", at = @At(value = "INVOKE", target = "Lnet/minecraft/scoreboard/Team;decorateName(Lnet/minecraft/scoreboard/AbstractTeam;Lnet/minecraft/text/Text;)Lnet/minecraft/text/MutableText;"))
|
||||
private void getFakeDisplayName(Args args) {
|
||||
SpiritualProjectionAbility.INSTANCE.replaceNameWithOwner((PlayerEntity) (Object) this, args);
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "isPartVisible",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;getDataTracker()Lnet/minecraft/entity/data/DataTracker;")
|
||||
)
|
||||
private DataTracker aang$redirectIsModelPartVisible(PlayerEntity instance, Operation<DataTracker> original) {
|
||||
return SpiritualProjectionAbility.INSTANCE.replaceDataTrackerWithOwner(instance, original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<Job> getAang_airScooterTasks() {
|
||||
return airScooterTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TornadoEntity getAang_tornadoEntity() {
|
||||
return tornadoEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAang_tornadoEntity(@Nullable TornadoEntity tornadoEntity) {
|
||||
this.tornadoEntity = tornadoEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<Job> getAang_tornadoTasks() {
|
||||
return tornadoTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<Job> getAang_spiritualProjectionsTasks() {
|
||||
return spiritualProjectionTasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull EntitySpinTracker getAang_airBallSpinTracker() {
|
||||
return entitySpinTracker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package gg.norisk.heroes.aang.mixin.accessor;
|
||||
|
||||
import net.minecraft.client.render.Camera;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(Camera.class)
|
||||
public interface CameraAccessor {
|
||||
@Invoker("setRotation")
|
||||
void invokeSetRotation(float f, float g);
|
||||
|
||||
@Invoker("setPos")
|
||||
void invokeSetPos(double d, double e, double f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.util.SkinTextures;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(AbstractClientPlayerEntity.class)
|
||||
public abstract class AbstractClientPlayerEntityMixin extends PlayerEntity {
|
||||
public AbstractClientPlayerEntityMixin(World world, BlockPos blockPos, float f, GameProfile gameProfile) {
|
||||
super(world, blockPos, f, gameProfile);
|
||||
}
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "getSkinTextures",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private SkinTextures replaceSkinWithOwner(SkinTextures original) {
|
||||
return SpiritualProjectionAbility.INSTANCE.replaceSkinWithOwner((AbstractClientPlayerEntity) (Object) this, original);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import gg.norisk.heroes.aang.ability.TornadoAbility;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.BlockView;
|
||||
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(Camera.class)
|
||||
public abstract class CameraMixin {
|
||||
@Inject(method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/Camera;setPos(DDD)V", shift = At.Shift.AFTER))
|
||||
private void tornadoCameraMode(BlockView blockView, Entity entity, boolean bl, boolean bl2, float f, CallbackInfo ci) {
|
||||
TornadoAbility.INSTANCE.handleTornadoCamera((Camera) (Object) this, blockView, entity, bl, bl2, f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import gg.norisk.heroes.aang.entity.DummyPlayer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientCommonNetworkHandler;
|
||||
import net.minecraft.client.network.ClientConnectionState;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener;
|
||||
import net.minecraft.network.listener.TickablePacketListener;
|
||||
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ClientPlayNetworkHandler.class)
|
||||
public abstract class ClientPlayNetworkHandlerMixin extends ClientCommonNetworkHandler implements ClientPlayPacketListener, TickablePacketListener {
|
||||
@Shadow private ClientWorld world;
|
||||
|
||||
protected ClientPlayNetworkHandlerMixin(MinecraftClient minecraftClient, ClientConnection clientConnection, ClientConnectionState clientConnectionState) {
|
||||
super(minecraftClient, clientConnection, clientConnectionState);
|
||||
}
|
||||
|
||||
@Inject(method = "createEntity", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false), cancellable = true)
|
||||
private void createFakePlayerInjection(EntitySpawnS2CPacket entitySpawnS2CPacket, CallbackInfoReturnable<Entity> cir) {
|
||||
DummyPlayer.Companion.handleDummyPlayerSpawn(entitySpawnS2CPacket, cir, this.world);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import gg.norisk.heroes.aang.ability.TornadoAbility;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.option.GameOptions;
|
||||
import net.minecraft.client.option.Perspective;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(GameOptions.class)
|
||||
public abstract class GameOptionsMixin {
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "getPerspective",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private Perspective aang$tornadoStaticPerspective(Perspective original) {
|
||||
var player = MinecraftClient.getInstance().player;
|
||||
if (player != null && TornadoAbility.INSTANCE.isTornadoMode(player)) {
|
||||
return Perspective.THIRD_PERSON_BACK;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.joml.Matrix4f;
|
||||
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(value = GameRenderer.class)
|
||||
public abstract class GameRendererMixin {
|
||||
@Inject(method = "renderHand", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V"), cancellable = true)
|
||||
private void renderHandBeforeSpiritual(Camera camera, float f, Matrix4f matrix4f, CallbackInfo ci, @Local MatrixStack matrixStack) {
|
||||
matrixStack.push();
|
||||
if (camera.getFocusedEntity() instanceof PlayerEntity player && SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(player)) {
|
||||
RenderSystem.setShaderColor(1f, 1f, 1f, SpiritualProjectionAbility.INSTANCE.getAlpha(player));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderHand", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/HeldItemRenderer;renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", shift = At.Shift.AFTER))
|
||||
private void renderHandAfterSpiritual(Camera camera, float f, Matrix4f matrix4f, CallbackInfo ci, @Local MatrixStack matrixStack) {
|
||||
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||
matrixStack.pop();
|
||||
}
|
||||
|
||||
@ModifyExpressionValue(
|
||||
method = {"renderWorld", "renderHand"},
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/SimpleOption;getValue()Ljava/lang/Object;", ordinal = 0)
|
||||
)
|
||||
private <T> Object aang$disableViewBobbing(T original) {
|
||||
ClientPlayerEntity player = MinecraftClient.getInstance().player;
|
||||
if (player != null && AirScooterAbility.INSTANCE.isAirScooting(player)) {
|
||||
return false;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.item.HeldItemRenderer;
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
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(HeldItemRenderer.class)
|
||||
public abstract class HeldItemRendererMixin {
|
||||
@Inject(method = "renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/world/World;III)V"))
|
||||
private void renderHandBeforeSpiritual(LivingEntity livingEntity, ItemStack itemStack, ModelTransformationMode modelTransformationMode, boolean bl, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
//matrixStack.push();
|
||||
if (livingEntity instanceof PlayerEntity player && SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(player)) {
|
||||
//RenderSystem.setShaderColor(1f, 1f, 1f, 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/item/ItemRenderer;renderItem(Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;Lnet/minecraft/world/World;III)V", shift = At.Shift.AFTER))
|
||||
private void renderHandAfterSpiritual(LivingEntity livingEntity, ItemStack itemStack, ModelTransformationMode modelTransformationMode, boolean bl, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
//RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||
//matrixStack.pop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import gg.norisk.heroes.aang.client.render.entity.feature.AirScooterFeatureRenderer;
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory;
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer;
|
||||
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.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
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.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
public abstract class LivingEntityRendererMixin<T extends LivingEntity, M extends EntityModel<T>> extends EntityRenderer<T> implements FeatureRendererContext<T, M> {
|
||||
protected LivingEntityRendererMixin(EntityRendererFactory.Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"))
|
||||
private void airballRenderer(T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
}
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean addFeature(FeatureRenderer<T, M> featureRenderer);
|
||||
|
||||
@Shadow
|
||||
protected M model;
|
||||
|
||||
@WrapOperation(
|
||||
method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;getScale()F")
|
||||
)
|
||||
private float redirectGetScaleWithLerpedScale(LivingEntity instance, Operation<Float> original, T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i) {
|
||||
if (instance instanceof TornadoEntity tornado) {
|
||||
return tornado.getLerpedScale(g);
|
||||
} else {
|
||||
return original.call(instance);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void initDataTrackerInjecetion(EntityRendererFactory.Context context, EntityModel<T> entityModel, float f, CallbackInfo ci) {
|
||||
this.addFeature(new AirScooterFeatureRenderer<>(this));
|
||||
}
|
||||
|
||||
@Inject(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/entity/model/EntityModel;riding:Z", shift = At.Shift.AFTER))
|
||||
private void ridingInjection(T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo ci) {
|
||||
if (livingEntity instanceof PlayerEntity player && AirScooterAbility.INSTANCE.isAirScooting(player)) {
|
||||
this.model.riding = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At("STORE"), ordinal = 0)
|
||||
private boolean modifyIsVisibleForAang(boolean original, T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i) {
|
||||
if (livingEntity instanceof PlayerEntity player) {
|
||||
return original && !SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(player);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "render(Lnet/minecraft/entity/LivingEntity;FFLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;I)V", at = @At("STORE"), ordinal = 1)
|
||||
private boolean modifyIsInVisibleToForAang(boolean original, T livingEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i) {
|
||||
if (livingEntity instanceof PlayerEntity player) {
|
||||
return original && SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(player);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package gg.norisk.heroes.aang.mixin.client;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility;
|
||||
import net.minecraft.client.model.ModelPart;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
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.PlayerEntityRenderer;
|
||||
import net.minecraft.client.render.entity.model.PlayerEntityModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(PlayerEntityRenderer.class)
|
||||
public abstract class PlayerEntityRendererMixin extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
|
||||
public PlayerEntityRendererMixin(EntityRendererFactory.Context context, PlayerEntityModel<AbstractClientPlayerEntity> entityModel, float f) {
|
||||
super(context, entityModel, f);
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "renderArm",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayer;getEntitySolid(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;")
|
||||
)
|
||||
private RenderLayer makeSpiritualHandTransparent(Identifier identifier, Operation<RenderLayer> original, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, AbstractClientPlayerEntity abstractClientPlayerEntity, ModelPart modelPart, ModelPart modelPart2) {
|
||||
if (SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(abstractClientPlayerEntity)) {
|
||||
return RenderLayer.getItemEntityTranslucentCull(identifier);
|
||||
} else {
|
||||
return original.call(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@WrapOperation(
|
||||
method = "renderArm",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayer;getEntityTranslucent(Lnet/minecraft/util/Identifier;)Lnet/minecraft/client/render/RenderLayer;")
|
||||
)
|
||||
private RenderLayer makeSpiritualHandTransparent2(Identifier identifier, Operation<RenderLayer> original, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, AbstractClientPlayerEntity abstractClientPlayerEntity, ModelPart modelPart, ModelPart modelPart2) {
|
||||
if (SpiritualProjectionAbility.INSTANCE.isSpiritualTransparent(abstractClientPlayerEntity)) {
|
||||
return RenderLayer.getItemEntityTranslucentCull(identifier);
|
||||
} else {
|
||||
return original.call(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package gg.norisk.heroes.aang
|
||||
|
||||
import gg.norisk.heroes.aang.ability.*
|
||||
import gg.norisk.heroes.aang.registry.*
|
||||
import gg.norisk.heroes.client.renderer.SkinUtils
|
||||
import gg.norisk.heroes.common.hero.Hero
|
||||
import gg.norisk.heroes.common.hero.HeroManager.registerHero
|
||||
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
|
||||
import java.awt.Color
|
||||
|
||||
object AangManager : ModInitializer, ClientModInitializer, DedicatedServerModInitializer {
|
||||
const val MOD_ID = "aang"
|
||||
val logger = LogManager.getLogger(MOD_ID)
|
||||
fun String.toId() = Identifier.of(MOD_ID, this)
|
||||
val aangSkin = "aang.png".toId()
|
||||
val aangOverlaySkin = "aang_overlay.png".toId()
|
||||
override fun onInitialize() {
|
||||
logger.info("Starting $MOD_ID Hero...")
|
||||
EntityRegistry.init()
|
||||
ParticleRegistry.init()
|
||||
EmoteRegistry.init()
|
||||
AirBallAbility.init()
|
||||
SpiritualProjectionAbility.init()
|
||||
SoundRegistry.init()
|
||||
TornadoAbility.init()
|
||||
LevitationAbility.init()
|
||||
}
|
||||
|
||||
override fun onInitializeClient() {
|
||||
SkinUtils.initClient()
|
||||
EntityRendererRegistry.init()
|
||||
ParticleRendererRegistry.init()
|
||||
AirScooterAbility.initClient()
|
||||
AirBallAbility.initClient()
|
||||
TornadoAbility.initClient()
|
||||
SpiritualProjectionAbility.initClient()
|
||||
ClientLifecycleEvents.CLIENT_STARTED.register {
|
||||
registerHeroes()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInitializeServer() {
|
||||
registerHero(Aang)
|
||||
}
|
||||
|
||||
private fun registerHeroes() {
|
||||
registerHero(Aang)
|
||||
}
|
||||
|
||||
val Aang by Hero("Aang") {
|
||||
ability(AirScooterAbility.Ability)
|
||||
ability(AirBallAbility.Ability)
|
||||
ability(SpiritualProjectionAbility.Ability)
|
||||
ability(TornadoAbility.Ability)
|
||||
ability(LevitationAbility.Ability)
|
||||
color = Color.decode("#33C3FFFF").rgb
|
||||
overlaySkin = aangOverlaySkin
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package gg.norisk.heroes.aang.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.heroes.aang.ability.SpiritualProjectionAbility.isUsingSpiritualProjection
|
||||
import gg.norisk.heroes.aang.client.sound.AirBendingCircleSoundInstance
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import gg.norisk.heroes.aang.entity.IAangPlayer
|
||||
import gg.norisk.heroes.aang.entity.aang
|
||||
import gg.norisk.heroes.aang.registry.EmoteRegistry
|
||||
import gg.norisk.heroes.aang.registry.EntityRegistry
|
||||
import gg.norisk.heroes.client.events.ClientEvents
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.common.HeroesManager.client
|
||||
import gg.norisk.heroes.common.ability.NumberProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.ToggleAbility
|
||||
import gg.norisk.heroes.common.networking.Networking.mousePacket
|
||||
import gg.norisk.heroes.common.networking.Networking.mouseScrollPacket
|
||||
import gg.norisk.heroes.common.networking.dto.MousePacket
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
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.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.silkmc.silk.commands.command
|
||||
import net.silkmc.silk.core.entity.directionVector
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literal
|
||||
import kotlin.random.Random
|
||||
|
||||
object AirBallAbility {
|
||||
val AIR_BENDING_KEY = "AangIsAirBending"
|
||||
val CURRENT_AIR_BENDING_KEY = "AangCurrentBendingId"
|
||||
|
||||
val airBallMaxSize = NumberProperty(3.0, 3, "Max Size", AddValueTotal(1.0, 1.0, 3.0)).apply {
|
||||
icon = {
|
||||
Components.item(Items.WIND_CHARGE.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
WorldRenderEvents.END.register(WorldRenderEvents.End { event ->
|
||||
val world = event.world() ?: return@End
|
||||
world.players.filter { it.isAirBending }.forEach { player ->
|
||||
player.spawnAirBendingParticle()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun init() {
|
||||
syncedValueChangeEvent.listen { event ->
|
||||
if (event.key != AIR_BENDING_KEY) return@listen
|
||||
if (!event.entity.world.isClient) return@listen
|
||||
val player = event.entity as? AbstractClientPlayerEntity ?: return@listen
|
||||
if (player.isAirBending) {
|
||||
player.playEmote(EmoteRegistry.AIR_BENDING)
|
||||
} else {
|
||||
player.stopEmote(EmoteRegistry.AIR_BENDING)
|
||||
}
|
||||
}
|
||||
if (!FabricLoader.getInstance().isDevelopmentEnvironment) return
|
||||
command("aang") {
|
||||
literal("toggleairbending") {
|
||||
runs {
|
||||
val player = this.source.playerOrThrow
|
||||
player.isAirBending = !player.isAirBending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PlayerEntity.spawnAirBendingParticle() {
|
||||
if (!world.isClient) return
|
||||
val pos = this.getAirBendingPos()
|
||||
world.addParticle(
|
||||
ParticleTypes.CLOUD,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
|
||||
fun PlayerEntity.getAirBendingPos(): Vec3d {
|
||||
return this.eyePos.add(this.directionVector.normalize().multiply(3.0))
|
||||
}
|
||||
|
||||
fun PlayerEntity.handleTick() {
|
||||
}
|
||||
|
||||
var PlayerEntity.isAirBending: Boolean
|
||||
get() = this.getSyncedData<Boolean>(AIR_BENDING_KEY) ?: false
|
||||
set(value) = this.setSyncedData(AIR_BENDING_KEY, value)
|
||||
|
||||
val PlayerEntity.currentBendingEntity: AirScooterEntity?
|
||||
get() {
|
||||
val id = if (currentBendingEntityId != -1) currentBendingEntityId else return null
|
||||
return world.getEntityById(id) as? AirScooterEntity?
|
||||
}
|
||||
|
||||
var PlayerEntity.currentBendingEntityId: Int
|
||||
get() = this.getSyncedData<Int>(CURRENT_AIR_BENDING_KEY) ?: -1
|
||||
set(value) = this.setSyncedData(CURRENT_AIR_BENDING_KEY, value)
|
||||
|
||||
private fun ServerPlayerEntity.scaleWindCharges(packet: Boolean) {
|
||||
val world = this.serverWorld
|
||||
|
||||
val scale = 0.5
|
||||
val forceStrength = if (packet) scale else -scale
|
||||
|
||||
val windCharges =
|
||||
world.iterateEntities().filterIsInstance<AirScooterEntity>()
|
||||
.filter { it.bendingType == AirScooterEntity.Type.PROJECTILE }
|
||||
.filter { it.ownerId == id }
|
||||
var soundFlag = true
|
||||
windCharges.forEach {
|
||||
val scaleAttribute =
|
||||
it.attributes.getCustomInstance(EntityAttributes.GENERIC_SCALE) ?: return@forEach
|
||||
scaleAttribute.baseValue += forceStrength
|
||||
if (scaleAttribute.baseValue < 0.5) {
|
||||
scaleAttribute.baseValue = 0.5
|
||||
soundFlag = false
|
||||
}
|
||||
if (scaleAttribute.baseValue > airBallMaxSize.getValue(this.uuid)) {
|
||||
scaleAttribute.baseValue = airBallMaxSize.getValue(this.uuid)
|
||||
soundFlag = false
|
||||
}
|
||||
}
|
||||
if (soundFlag) {
|
||||
windCharges.randomOrNull()?.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.1f, Random.nextDouble(0.8, 1.2))
|
||||
}
|
||||
}
|
||||
|
||||
private fun ServerPlayerEntity.launchAnyAirBall(packet: MousePacket) {
|
||||
if (packet.isLeft() && packet.isClicked()) {
|
||||
val windCharges = getLaunchableWindCharges().randomOrNull()
|
||||
windCharges?.damage(this.damageSources.playerAttack(this), 0f)
|
||||
} else if (packet.isMiddle() && packet.isClicked()) {
|
||||
getLaunchableWindCharges().randomOrNull()?.launchBoomerang()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ServerPlayerEntity.getLaunchableWindCharges(): List<AirScooterEntity> {
|
||||
return serverWorld.iterateEntities().filterIsInstance<AirScooterEntity>()
|
||||
.filter { it.bendingType == AirScooterEntity.Type.PROJECTILE }
|
||||
.filter { it.wasBended }
|
||||
.filter { !it.wasLaunched }
|
||||
}
|
||||
|
||||
val Ability = object : ToggleAbility("Air Ball") {
|
||||
init {
|
||||
client {
|
||||
this.keyBind = HeroKeyBindings.firstKeyBind
|
||||
|
||||
ClientEvents.preHotbarScrollEvent.listen { event ->
|
||||
val player = MinecraftClient.getInstance().player ?: return@listen
|
||||
val entity = player.currentBendingEntity
|
||||
if (entity != null && !entity.wasLaunched) {
|
||||
event.isCancelled.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(10.0, 5, AddValueTotal(-1.0, -1.0, -1.0, -1.0, -1.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(5.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
|
||||
|
||||
this.properties = listOf(airBallMaxSize)
|
||||
|
||||
syncedValueChangeEvent.listen {
|
||||
val player = it.entity as? PlayerEntity ?: return@listen
|
||||
if (it.key == AIR_BENDING_KEY && player.world.isClient) {
|
||||
if (player.isAirBending && player == MinecraftClient.getInstance().player) {
|
||||
MinecraftClient.getInstance().soundManager.play(AirBendingCircleSoundInstance(player))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mouseScrollPacket.receiveOnServer { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = false) {
|
||||
context.player.scaleWindCharges(packet)
|
||||
}
|
||||
}
|
||||
|
||||
mousePacket.receiveOnServer { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = false) {
|
||||
context.player.launchAnyAirBall(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.WIND_CHARGE.defaultStack)
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/quartz_block_bottom.png")
|
||||
}
|
||||
|
||||
override fun canUse(player: ServerPlayerEntity): Boolean {
|
||||
return !player.isUsingSpiritualProjection()
|
||||
}
|
||||
|
||||
override fun onTick(player: PlayerEntity) {
|
||||
super.onTick(player)
|
||||
if (player is ServerPlayerEntity) {
|
||||
if (player.isAirBending) {
|
||||
(player as IAangPlayer).aang_airBallSpinTracker.update(player)
|
||||
|
||||
val progress = player.aang.aang_airBallSpinTracker.getSpinProgress().toDouble()
|
||||
player.currentBendingEntity?.getAttributeInstance(EntityAttributes.GENERIC_SCALE)?.baseValue =
|
||||
2 * (progress / 100.0)
|
||||
|
||||
if (player.aang_airBallSpinTracker.hasSpunWildly()) {
|
||||
player.isAirBending = false
|
||||
player.currentBendingEntity?.wasBended = true
|
||||
player.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.2, 1f)
|
||||
(player as IAangPlayer).aang_airBallSpinTracker.clear()
|
||||
}
|
||||
} else {
|
||||
(player as IAangPlayer).aang_airBallSpinTracker.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
val airScooter = EntityRegistry.AIR_SCOOTER.create(player.world) ?: return
|
||||
player.isAirBending = true
|
||||
airScooter.ownerId = player.id
|
||||
airScooter.bendingType = AirScooterEntity.Type.PROJECTILE
|
||||
airScooter.setPosition(player.getAirBendingPos())
|
||||
player.serverWorld.spawnEntity(airScooter)
|
||||
player.currentBendingEntityId = airScooter.id
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
player.stopAirBall(true)
|
||||
}
|
||||
|
||||
private fun PlayerEntity.stopAirBall(forceDiscard: Boolean = false) {
|
||||
if (this is ServerPlayerEntity) {
|
||||
this.isAirBending = false
|
||||
val currentEntity = this.currentBendingEntity
|
||||
if (forceDiscard) {
|
||||
currentEntity?.discard()
|
||||
this.currentBendingEntityId = -1
|
||||
} else if (currentEntity != null && currentEntity.wasBended.not()) {
|
||||
this.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.1, 1.5f)
|
||||
this.currentBendingEntity?.discard()
|
||||
this.currentBendingEntityId = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
super.onEnd(player, abilityEndInformation)
|
||||
player.stopAirBall()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package gg.norisk.heroes.aang.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.datatracker.entity.syncedValueChangeEvent
|
||||
import gg.norisk.emote.network.EmoteNetworking.playEmote
|
||||
import gg.norisk.emote.network.EmoteNetworking.stopEmote
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.currentBendingEntity
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.isAirBending
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility.isUsingSpiritualProjection
|
||||
import gg.norisk.heroes.aang.client.sound.AirScooterSoundInstance
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import gg.norisk.heroes.aang.entity.aang
|
||||
import gg.norisk.heroes.aang.registry.EmoteRegistry
|
||||
import gg.norisk.heroes.aang.registry.EntityRegistry
|
||||
import gg.norisk.heroes.aang.registry.ParticleRegistry
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.client.renderer.Speedlines.showSpeedlines
|
||||
import gg.norisk.heroes.common.HeroesManager.client
|
||||
import gg.norisk.heroes.common.ability.NumberProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.ToggleAbility
|
||||
import gg.norisk.heroes.common.hero.ability.task.abilityCoroutineTask
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.utils.Easing
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import kotlinx.coroutines.cancel
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.silkmc.silk.core.Silk
|
||||
import net.silkmc.silk.core.Silk.server
|
||||
import net.silkmc.silk.core.entity.modifyVelocity
|
||||
import net.silkmc.silk.core.task.infiniteMcCoroutineTask
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.broadcastText
|
||||
import net.silkmc.silk.network.packet.s2cPacket
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
object AirScooterAbility {
|
||||
val airScooterSoundPacketS2C = s2cPacket<Int>("air-scooter-sound".toId())
|
||||
val AIR_SCOOTING_KEY = "AangIsAirScooting"
|
||||
|
||||
val airScooterSpeed = NumberProperty(0.2, 3, "Speed", AddValueTotal(0.1, 0.1, 0.1)).apply {
|
||||
icon = {
|
||||
Components.item(Items.WIND_CHARGE.defaultStack)
|
||||
}
|
||||
}
|
||||
val airScooterStepHeight = NumberProperty(2.0, 3, "Step Height", AddValueTotal(1.0, 1.0, 1.0)).apply {
|
||||
icon = {
|
||||
Components.item(Items.QUARTZ_STAIRS.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
airScooterSoundPacketS2C.receiveOnClient { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = true) {
|
||||
val client = context.client
|
||||
val entity = context.client.world?.getEntityById(packet) ?: return@mcCoroutineTask
|
||||
client.soundManager.play(AirScooterSoundInstance(entity))
|
||||
}
|
||||
}
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { event ->
|
||||
val world = event.world ?: return@EndTick
|
||||
for (player in world.players) {
|
||||
val scooter = world.entities.filterIsInstance<AirScooterEntity>()
|
||||
.filter { it.bendingType == AirScooterEntity.Type.SCOOTER }
|
||||
.filter { it.ownerId == player.id }
|
||||
scooter.forEach {
|
||||
it.setPosition(player.pos.add(0.0, 0.2, 0.0))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var PlayerEntity.isAirScooting: Boolean
|
||||
get() = this.getSyncedData<Boolean>(AIR_SCOOTING_KEY) ?: false
|
||||
set(value) = this.setSyncedData(AIR_SCOOTING_KEY, value)
|
||||
|
||||
fun Entity.handleDrag(): Boolean {
|
||||
return (this is PlayerEntity && this.isAirScooting) || this is AirScooterEntity
|
||||
}
|
||||
|
||||
fun PlayerEntity.spawnAirScooter() {
|
||||
val world = world as? ServerWorld? ?: return
|
||||
val airScooter = EntityRegistry.AIR_SCOOTER.create(world) ?: return
|
||||
airScooter.bendingType = AirScooterEntity.Type.SCOOTER
|
||||
airScooter.ownerId = id
|
||||
airScooter.setPosition(this.pos)
|
||||
world.spawnEntity(airScooter)
|
||||
}
|
||||
|
||||
fun Entity.handleBox(box: Box): Box {
|
||||
if (this is PlayerEntity && this.isAirScooting) {
|
||||
return box.stretch(0.0, -1.0, 0.0)
|
||||
}
|
||||
return box
|
||||
}
|
||||
|
||||
fun PlayerEntity.handleFallDamage(
|
||||
f: Float,
|
||||
g: Float,
|
||||
damageSource: DamageSource,
|
||||
cir: CallbackInfoReturnable<Boolean>
|
||||
) {
|
||||
if (isAirScooting) {
|
||||
cir.returnValue = false
|
||||
}
|
||||
}
|
||||
|
||||
fun PlayerEntity.handleTravel(vec3d: Vec3d): Vec3d {
|
||||
spawnAirScooterDust()
|
||||
val x = sidewaysSpeed * 0.5f
|
||||
val z = 1f
|
||||
|
||||
this.movementSpeed = 0.5f
|
||||
return Vec3d(x.toDouble(), vec3d.y, z.toDouble())
|
||||
}
|
||||
|
||||
private fun Entity.spawnAirScooterDust() {
|
||||
if (!world.isClient) return
|
||||
repeat(5) {
|
||||
val offset = 0.2
|
||||
val randomX = Random.nextDouble(-offset, offset).toFloat()
|
||||
val randomY = Random.nextDouble(-offset, offset).toFloat()
|
||||
val randomZ = Random.nextDouble(-offset, offset).toFloat()
|
||||
world.addParticle(
|
||||
ParticleRegistry.AIR_SCOOTER_DUST,
|
||||
this.x + randomX,
|
||||
this.y + randomY,
|
||||
this.z + randomZ,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun PlayerEntity.stopRidingAirBall() {
|
||||
aang.aang_airScooterTasks.forEach { it.cancel() }
|
||||
if (this is ServerPlayerEntity) {
|
||||
this.isAirScooting = false
|
||||
this.showSpeedlines = false
|
||||
//das hier suckt iwie lieber modifiers usen
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue = 0.6
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)?.baseValue =
|
||||
0.10000000149011612
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue = 0.08
|
||||
this.stopEmote(EmoteRegistry.AIR_SCOOTER_SITTING)
|
||||
this.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.2, 2f)
|
||||
} else if (this == MinecraftClient.getInstance().player) {
|
||||
this.showSpeedlines = false
|
||||
}
|
||||
}
|
||||
|
||||
val Ability = object : ToggleAbility("Air Scooter") {
|
||||
|
||||
init {
|
||||
client {
|
||||
this.keyBind = HeroKeyBindings.secondKeyBind
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(90.0, 4, AddValueTotal(-5.0, -5.0, -5.0, -5.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(5.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
|
||||
|
||||
this.properties = listOf(
|
||||
airScooterSpeed,
|
||||
airScooterStepHeight
|
||||
)
|
||||
|
||||
syncedValueChangeEvent.listen {
|
||||
val player = it.entity as? PlayerEntity ?: return@listen
|
||||
if (it.key == AIR_SCOOTING_KEY) {
|
||||
if (player.isAirScooting) {
|
||||
player.spawnAirScooter()
|
||||
} else {
|
||||
if (player.world.isClient) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun canUse(player: ServerPlayerEntity): Boolean {
|
||||
return !player.isUsingSpiritualProjection()
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.WIND_CHARGE.defaultStack)
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/quartz_block_bottom.png")
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.playEmote(EmoteRegistry.AIR_SCOOTER)
|
||||
player.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.5)
|
||||
player.aang.aang_airScooterTasks += abilityCoroutineTask(
|
||||
sync = true,
|
||||
client = false,
|
||||
delay = 0.6.seconds,
|
||||
executingPlayer = player
|
||||
) {
|
||||
player.modifyVelocity(0.0, 0.55, 0.0)
|
||||
}
|
||||
player.aang.aang_airScooterTasks += abilityCoroutineTask(
|
||||
sync = true,
|
||||
client = false,
|
||||
delay = 0.83.seconds,
|
||||
executingPlayer = player
|
||||
) {
|
||||
//player.modifyVelocity(0.0,1.0,0.0)
|
||||
airScooterSoundPacketS2C.sendToAll(player.id)
|
||||
player.isAirScooting = true
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue =
|
||||
airScooterStepHeight.getValue(player.uuid)
|
||||
val speedAnimation =
|
||||
OldAnimation(
|
||||
0.1f,
|
||||
airScooterSpeed.getValue(player.uuid).toFloat(),
|
||||
1.seconds.toJavaDuration(),
|
||||
Easing.CUBIC_IN
|
||||
)
|
||||
player.aang.aang_airScooterTasks += infiniteMcCoroutineTask(sync = true, client = false) {
|
||||
if (speedAnimation.isDone) cancel()
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)?.baseValue =
|
||||
speedAnimation.get().toDouble()
|
||||
}
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue = 0.02
|
||||
player.playEmote(EmoteRegistry.AIR_SCOOTER_SITTING)
|
||||
}
|
||||
} else if (player == MinecraftClient.getInstance().player) {
|
||||
player.showSpeedlines = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
player.stopRidingAirBall()
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
super.onEnd(player, abilityEndInformation)
|
||||
player.stopRidingAirBall()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package gg.norisk.heroes.aang.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.heroes.aang.ability.SpiritualProjectionAbility.isUsingSpiritualProjection
|
||||
import gg.norisk.heroes.aang.client.sound.VelocityBasedFlyingSoundInstance
|
||||
import gg.norisk.heroes.aang.registry.EmoteRegistry
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.common.HeroesManager.client
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.HoldAbility
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.damage.DamageTypes
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.silkmc.silk.core.event.EntityEvents
|
||||
|
||||
object LevitationAbility {
|
||||
val AIR_LEVITATING_KEY = "AangIsAirLevitating"
|
||||
|
||||
var PlayerEntity.isAirLevitating: Boolean
|
||||
get() = this.getSyncedData<Boolean>(AIR_LEVITATING_KEY) ?: false
|
||||
set(value) = this.setSyncedData(AIR_LEVITATING_KEY, value)
|
||||
|
||||
fun init() {
|
||||
syncedValueChangeEvent.listen { event ->
|
||||
if (event.key != AIR_LEVITATING_KEY) return@listen
|
||||
if (!event.entity.world.isClient) return@listen
|
||||
val player = event.entity as? AbstractClientPlayerEntity ?: return@listen
|
||||
if (player.isAirLevitating) {
|
||||
player.playEmote(EmoteRegistry.LEVITATION)
|
||||
MinecraftClient.getInstance().soundManager.play(VelocityBasedFlyingSoundInstance(player) {
|
||||
(it as? PlayerEntity?)?.isAirLevitating == true
|
||||
})
|
||||
} else {
|
||||
player.stopEmote(EmoteRegistry.LEVITATION)
|
||||
}
|
||||
}
|
||||
EntityEvents.checkInvulnerability.listen { event ->
|
||||
if (event.source.isOf(DamageTypes.FALL)) {
|
||||
val player = event.entity as? PlayerEntity ?: return@listen
|
||||
if (player.isAirLevitating) {
|
||||
event.isInvulnerable.set(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PlayerEntity.handleTick() {
|
||||
if (isAirLevitating) {
|
||||
world.addParticle(
|
||||
ParticleTypes.CLOUD,
|
||||
this.getParticleX(0.5),
|
||||
this.randomBodyY,
|
||||
this.getParticleZ(0.5),
|
||||
0.001,
|
||||
0.001,
|
||||
0.001,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val Ability = object : HoldAbility("Levitation") {
|
||||
init {
|
||||
client {
|
||||
this.keyBind = HeroKeyBindings.fifthKeyBind
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(5.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.FEATHER.defaultStack)
|
||||
}
|
||||
|
||||
override fun canUse(player: ServerPlayerEntity): Boolean {
|
||||
return !player.isUsingSpiritualProjection()
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/quartz_block_bottom.png")
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
private fun cleanUp(player: PlayerEntity) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isAirLevitating = false
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue =
|
||||
EntityAttributes.GENERIC_GRAVITY.value().defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isAirLevitating = true
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue = 0.01
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
super.onEnd(player, abilityEndInformation)
|
||||
cleanUp(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
package gg.norisk.heroes.aang.ability
|
||||
|
||||
import com.llamalad7.mixinextras.injector.wrapoperation.Operation
|
||||
import com.mojang.authlib.GameProfile
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.datatracker.entity.syncedValueChangeEvent
|
||||
import gg.norisk.emote.network.EmoteNetworking.playEmote
|
||||
import gg.norisk.emote.network.EmoteNetworking.stopEmote
|
||||
import gg.norisk.heroes.aang.AangManager.Aang
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.isAirBending
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility.isAirScooting
|
||||
import gg.norisk.heroes.aang.ability.LevitationAbility.isAirLevitating
|
||||
import gg.norisk.heroes.aang.client.sound.AirBendingLevitationSoundInstance
|
||||
import gg.norisk.heroes.aang.client.sound.VelocityBasedFlyingSoundInstance
|
||||
import gg.norisk.heroes.aang.entity.DummyPlayer
|
||||
import gg.norisk.heroes.aang.entity.aang
|
||||
import gg.norisk.heroes.aang.registry.EmoteRegistry
|
||||
import gg.norisk.heroes.aang.registry.EmoteRegistry.toEmote
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.client.renderer.RenderUtils
|
||||
import gg.norisk.heroes.common.HeroesManager.client
|
||||
import gg.norisk.heroes.common.ability.NumberProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
|
||||
import gg.norisk.heroes.common.hero.ability.task.abilityCoroutineTask
|
||||
import gg.norisk.heroes.common.hero.setHero
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
|
||||
import net.fabricmc.fabric.api.event.player.AttackEntityCallback
|
||||
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback
|
||||
import net.fabricmc.fabric.api.event.player.UseItemCallback
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.client.util.SkinTextures
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.data.DataTracker
|
||||
import net.minecraft.entity.effect.StatusEffectInstance
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.TypedActionResult
|
||||
import net.minecraft.world.TeleportTarget
|
||||
import net.silkmc.silk.commands.command
|
||||
import net.silkmc.silk.core.entity.modifyVelocity
|
||||
import net.silkmc.silk.core.kotlin.ticks
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literal
|
||||
import net.silkmc.silk.core.text.literalText
|
||||
import org.apache.commons.lang3.RandomStringUtils
|
||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object SpiritualProjectionAbility {
|
||||
val LEVITATION_KEY = "IsSpiritualLevitating"
|
||||
val OVERLAY = "textures/misc/spiritual_vignette.png".toId()
|
||||
|
||||
fun init() {
|
||||
syncedValueChangeEvent.listen { event ->
|
||||
if (!event.entity.world.isClient) return@listen
|
||||
if (LEVITATION_KEY == event.key) {
|
||||
val player = event.entity as? PlayerEntity? ?: return@listen
|
||||
if (player.isSpiritualLevitating) {
|
||||
MinecraftClient.getInstance().soundManager.play(AirBendingLevitationSoundInstance(player))
|
||||
}
|
||||
}
|
||||
if (event.key == "IsSpiritualTransparent") {
|
||||
val player = event.entity as? PlayerEntity? ?: return@listen
|
||||
if (player.isSpiritualTransparent) {
|
||||
MinecraftClient.getInstance().soundManager.play(VelocityBasedFlyingSoundInstance(player) {
|
||||
(it as? PlayerEntity?)?.isSpiritualTransparent == true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UseBlockCallback.EVENT.register(UseBlockCallback { player, world, hand, hitResult ->
|
||||
if (player.isSpiritualTransparent && !world.isClient) {
|
||||
player.cancelSpiritMode()
|
||||
//TODO
|
||||
return@UseBlockCallback ActionResult.FAIL
|
||||
}
|
||||
return@UseBlockCallback ActionResult.PASS
|
||||
})
|
||||
|
||||
UseItemCallback.EVENT.register(UseItemCallback { player, world, hand ->
|
||||
if (player.isSpiritualTransparent && !world.isClient) {
|
||||
player.cancelSpiritMode()
|
||||
//TODO
|
||||
return@UseItemCallback TypedActionResult.fail(player.getStackInHand(hand))
|
||||
}
|
||||
return@UseItemCallback TypedActionResult.pass(player.getStackInHand(hand))
|
||||
})
|
||||
|
||||
AttackEntityCallback.EVENT.register(AttackEntityCallback { player, world, hand, entity, hitResult ->
|
||||
if (player.isSpiritualTransparent && !world.isClient) {
|
||||
player.cancelSpiritMode()
|
||||
//TODO
|
||||
return@AttackEntityCallback ActionResult.FAIL
|
||||
}
|
||||
return@AttackEntityCallback ActionResult.PASS
|
||||
})
|
||||
|
||||
PlayerBlockBreakEvents.BEFORE.register(PlayerBlockBreakEvents.Before { world, player, pos, state, blockEntity ->
|
||||
if (player.isSpiritualTransparent) {
|
||||
return@Before !player.cancelSpiritMode()
|
||||
}
|
||||
return@Before true
|
||||
})
|
||||
|
||||
if (!FabricLoader.getInstance().isDevelopmentEnvironment) return
|
||||
command("aang") {
|
||||
literal("togglespiritualtransparency") {
|
||||
runs {
|
||||
val player = this.source.playerOrThrow
|
||||
player.isSpiritualTransparent = !player.isSpiritualTransparent
|
||||
}
|
||||
}
|
||||
literal("togglespirituallevitating") {
|
||||
runs {
|
||||
val player = this.source.playerOrThrow
|
||||
player.isSpiritualLevitating = !player.isSpiritualLevitating
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun LivingEntity.getAlpha(): Float {
|
||||
val pulseSpeed = 10.0 // Bestimmt, wie schnell das Overlay pulsiert (höherer Wert = langsameres Pulsieren)
|
||||
return (Math.sin(age / pulseSpeed) * 0.25 + 0.75).toFloat() // Wert zwischen 0.5 und 1.0
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
HudRenderCallback.EVENT.register(HudRenderCallback { drawContext, tickCounter ->
|
||||
val player = MinecraftClient.getInstance().player ?: return@HudRenderCallback
|
||||
if (player.isSpiritualLevitating || player.isSpiritualTransparent) {
|
||||
// Entity age verwenden, um einen kontinuierlichen Wert zu erhalten
|
||||
val pulseSpeed =
|
||||
10.0 // Bestimmt, wie schnell das Overlay pulsiert (höherer Wert = langsameres Pulsieren)
|
||||
val alpha = (Math.sin(player.age / pulseSpeed) * 0.25 + 0.75).toFloat() // Wert zwischen 0.5 und 1.0
|
||||
RenderUtils.renderOverlay(drawContext, OVERLAY, alpha)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun PlayerEntity.isUsingSpiritualProjection(): Boolean {
|
||||
return isSpiritualLevitating || isSpiritualTransparent
|
||||
}
|
||||
|
||||
fun PlayerEntity.replaceNameWithOwner(args: Args) {
|
||||
val owner = world.getEntityById(spiritualOwner) as? PlayerEntity? ?: return
|
||||
args.set(1, owner.gameProfile.name.literal)
|
||||
}
|
||||
|
||||
fun AbstractClientPlayerEntity.replaceSkinWithOwner(original: SkinTextures): SkinTextures {
|
||||
val owner = world.getEntityById(spiritualOwner) as? AbstractClientPlayerEntity? ?: return original
|
||||
return owner.skinTextures
|
||||
}
|
||||
|
||||
fun PlayerEntity.replaceDataTrackerWithOwner(original: Operation<DataTracker>): DataTracker {
|
||||
val owner = world.getEntityById(spiritualOwner) as? PlayerEntity? ?: return original.call(this)
|
||||
return owner.dataTracker
|
||||
}
|
||||
|
||||
fun DummyPlayer.cancelProjection(reason: Entity?) {
|
||||
val owner = world.getEntityById(spiritualOwner) as? ServerPlayerEntity?
|
||||
owner?.isSpiritualLevitating = false
|
||||
owner?.isSpiritualTransparent = false
|
||||
owner?.teleportTo(
|
||||
TeleportTarget(
|
||||
world as ServerWorld,
|
||||
this.pos, velocity, yaw, pitch, TeleportTarget.NO_OP
|
||||
)
|
||||
)
|
||||
owner?.abilities?.flying = false
|
||||
owner?.abilities?.allowFlying = false
|
||||
owner?.sendAbilitiesUpdate()
|
||||
owner?.sound(SoundEvents.BLOCK_BEACON_DEACTIVATE, 0.2f, 2f)
|
||||
discard()
|
||||
}
|
||||
|
||||
private fun ServerPlayerEntity.spawnFakePlayer() {
|
||||
val fakePlayer = DummyPlayer(
|
||||
world, blockPos, pitch, GameProfile(UUID(0, Random.nextLong()), RandomStringUtils.randomAlphabetic(16))
|
||||
)
|
||||
fakePlayer.updatePositionAndAngles(this.pos.x, this.pos.y, this.pos.z, this.yaw, this.pitch)
|
||||
fakePlayer.setNoGravity(true)
|
||||
fakePlayer.spiritualOwner = this.id
|
||||
fakePlayer.isSpiritualLevitating = true
|
||||
fakePlayer.setHero(Aang)
|
||||
world.spawnEntity(fakePlayer)
|
||||
mcCoroutineTask(sync = true, client = false, delay = 1.ticks) {
|
||||
fakePlayer.playEmote("spiritual_projection_loop".toEmote())
|
||||
}
|
||||
}
|
||||
|
||||
fun PlayerEntity.handleTick() {
|
||||
if (isSpiritualTransparent) {
|
||||
noClip = isSpiritualTransparent
|
||||
|
||||
if (!world.isClient) {
|
||||
val body = (this as ServerPlayerEntity).serverWorld
|
||||
.iterateEntities()
|
||||
.filterIsInstance<DummyPlayer>()
|
||||
.filter { it.spiritualOwner == this.id }
|
||||
.randomOrNull()
|
||||
if (body != null) {
|
||||
val distance = body.distanceTo(this)
|
||||
if (distance > projectionMaxDistance.getValue(this.uuid).toFloat()) {
|
||||
sendMessage(Text.translatable("heroes.katara.ability.spiritual_projection.too_far_away"))
|
||||
cancelSpiritMode(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isSpiritualTransparent) {
|
||||
world.addParticle(
|
||||
StatusEffects.LEVITATION.value().createParticle(StatusEffectInstance(StatusEffects.LEVITATION)),
|
||||
this.getParticleX(0.5),
|
||||
this.randomBodyY,
|
||||
this.getParticleZ(0.5),
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun PlayerEntity.cancelSpiritMode(toClear: DummyPlayer? = null): Boolean {
|
||||
if (this.world.isClient) return false
|
||||
val body = toClear ?: (this as ServerPlayerEntity).serverWorld
|
||||
.iterateEntities()
|
||||
.filterIsInstance<DummyPlayer>()
|
||||
.filter { it.spiritualOwner == this.id }
|
||||
.randomOrNull()
|
||||
if (body != null) {
|
||||
Ability.addCooldown(this)
|
||||
body.cancelProjection(null)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var PlayerEntity.isSpiritualTransparent: Boolean
|
||||
get() = this.getSyncedData<Boolean>("IsSpiritualTransparent") ?: false
|
||||
set(value) = this.setSyncedData("IsSpiritualTransparent", value)
|
||||
|
||||
var PlayerEntity.spiritualOwner: Int
|
||||
get() = this.getSyncedData<Int>("SpiritualOwnerId") ?: -1
|
||||
set(value) = this.setSyncedData("SpiritualOwnerId", value)
|
||||
|
||||
var PlayerEntity.isSpiritualLevitating: Boolean
|
||||
get() = this.getSyncedData<Boolean>(LEVITATION_KEY) ?: false
|
||||
set(value) = this.setSyncedData(LEVITATION_KEY, value)
|
||||
|
||||
val projectionMaxDistance = NumberProperty(
|
||||
25.0, 5,
|
||||
"Spiritual Projection Max Distance",
|
||||
AddValueTotal(10.0, 10.0, 10.0, 10.0, 10.0)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(Items.SPYGLASS.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
val Ability = object : PressAbility("Spiritual Projection") {
|
||||
init {
|
||||
client {
|
||||
this.keyBind = HeroKeyBindings.thirdKeyBind
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(10.0, 5, AddValueTotal(-1.0, -1.0, -1.0, -1.0, -1.0))
|
||||
|
||||
this.properties = listOf(projectionMaxDistance)
|
||||
}
|
||||
|
||||
override fun canUse(player: ServerPlayerEntity): Boolean {
|
||||
if (player.isAirScooting) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (player.hasVehicle()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (player.isAirBending) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (player.isAirLevitating) {
|
||||
return false
|
||||
}
|
||||
|
||||
return super.canUse(player)
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.BLUE_STAINED_GLASS.defaultStack)
|
||||
}
|
||||
|
||||
override fun getUnlockCondition(): Text {
|
||||
return literalText {
|
||||
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasUnlocked(player: PlayerEntity): Boolean {
|
||||
return player.isCreative || (LevitationAbility.Ability.cooldownProperty.isMaxed(player.uuid) && LevitationAbility.Ability.maxDurationProperty.isMaxed(
|
||||
player.uuid
|
||||
))
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/quartz_block_bottom.png")
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
player.cancelSpiritMode()
|
||||
player.stopLevitation()
|
||||
}
|
||||
|
||||
private fun PlayerEntity.stopLevitation() {
|
||||
aang.aang_spiritualProjectionsTasks.forEach { it.cancel() }
|
||||
isSpiritualLevitating = false
|
||||
isSpiritualTransparent = false
|
||||
removeStatusEffect(StatusEffects.LEVITATION)
|
||||
(this as? ServerPlayerEntity)?.stopEmote(EmoteRegistry.SPIRITUAL_PROJECTION_START)
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
abilityScope.cancelCooldown()
|
||||
if (player.cancelSpiritMode()) {
|
||||
return
|
||||
}
|
||||
if (!player.isSpiritualLevitating) {
|
||||
player.isSpiritualTransparent = false
|
||||
player.isSpiritualLevitating = true
|
||||
player.abilities.flying = false
|
||||
player.abilities.allowFlying = false
|
||||
player.addStatusEffect(
|
||||
StatusEffectInstance(
|
||||
StatusEffects.LEVITATION,
|
||||
2.12.seconds.inWholeMilliseconds.toInt() / 50
|
||||
)
|
||||
)
|
||||
player.playEmote(EmoteRegistry.SPIRITUAL_PROJECTION_START)
|
||||
player.aang.aang_spiritualProjectionsTasks += abilityCoroutineTask(
|
||||
sync = true,
|
||||
client = false,
|
||||
delay = 2.12.seconds,
|
||||
executingPlayer = player
|
||||
) {
|
||||
player.isSpiritualTransparent = true
|
||||
player.isSpiritualLevitating = false
|
||||
player.abilities.flying = true
|
||||
player.abilities.allowFlying = true
|
||||
player.sendAbilitiesUpdate()
|
||||
player.spawnFakePlayer()
|
||||
player.sound(SoundEvents.BLOCK_BEACON_ACTIVATE, 0.2, 2f)
|
||||
player.modifyVelocity(0.0, 1.0, 0.0)
|
||||
}
|
||||
} else {
|
||||
abilityScope.applyCooldown()
|
||||
player.stopLevitation()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
package gg.norisk.heroes.aang.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility.isUsingSpiritualProjection
|
||||
import gg.norisk.heroes.aang.client.sound.TornadoSoundInstance
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import gg.norisk.heroes.aang.entity.aang
|
||||
import gg.norisk.heroes.aang.mixin.accessor.CameraAccessor
|
||||
import gg.norisk.heroes.aang.registry.EntityRegistry
|
||||
import gg.norisk.heroes.aang.utils.PlayerRotationTracker
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.common.HeroesManager.client
|
||||
import gg.norisk.heroes.common.ability.CooldownProperty
|
||||
import gg.norisk.heroes.common.ability.NumberProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.Camera
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.network.packet.s2c.play.TitleFadeS2CPacket
|
||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.BlockView
|
||||
import net.silkmc.silk.commands.command
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literal
|
||||
import net.silkmc.silk.core.text.literalText
|
||||
import net.silkmc.silk.network.packet.s2cPacket
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object TornadoAbility {
|
||||
val tornadoSoundPacketS2C = s2cPacket<Int>("tornado-sound-packet".toId())
|
||||
var currentYaw: Float = 0f
|
||||
var currentPitch: Float = 0f
|
||||
var rotationAngle = 0f // Variable zum Speichern des Rotationswinkels
|
||||
|
||||
var PlayerEntity.isTornadoMode: Boolean
|
||||
get() = this.getSyncedData<Boolean>("isTornadoMode") ?: false
|
||||
set(value) = this.setSyncedData("isTornadoMode", value)
|
||||
|
||||
fun Camera.handleTornadoCamera(blockView: BlockView, entity: Entity, bl: Boolean, bl2: Boolean, f: Float) {
|
||||
val dummy = (this as CameraAccessor)
|
||||
val player = entity as? PlayerEntity? ?: return
|
||||
val pos = pos.add(20.0, 19.0, 0.0)
|
||||
val (pitch, yaw) = lookAt(pos, entity.pos)
|
||||
if (!player.isTornadoMode) {
|
||||
currentYaw = yaw
|
||||
currentPitch = pitch
|
||||
return
|
||||
}
|
||||
currentYaw = MathHelper.lerp(f * 0.05f, currentYaw, yaw)
|
||||
currentPitch = MathHelper.lerp(f * 0.05f, currentPitch, pitch)
|
||||
invokeSetPos(pos.x, pos.y, pos.z)
|
||||
invokeSetRotation(currentYaw, currentPitch)
|
||||
}
|
||||
|
||||
fun lookAt(currentPos: Vec3d, center: Vec3d): Pair<Float, Float> {
|
||||
val d = center.x - currentPos.x
|
||||
val e = center.y - currentPos.y
|
||||
val f = center.z - currentPos.z
|
||||
val g = sqrt(d * d + f * f)
|
||||
val pitch = MathHelper.wrapDegrees((-(MathHelper.atan2(e, g) * 180.0f / Math.PI.toFloat())).toFloat())
|
||||
val yaw = MathHelper.wrapDegrees((MathHelper.atan2(f, d) * 180.0f / Math.PI.toFloat()).toFloat() - 90.0f)
|
||||
return Pair(pitch, yaw)
|
||||
}
|
||||
|
||||
private fun ServerPlayerEntity.summonTornado() {
|
||||
val tornadoEntity = EntityRegistry.TORNADO.create(this.serverWorld) ?: return
|
||||
aang.aang_tornadoEntity = tornadoEntity
|
||||
tornadoEntity.setPosition(this.pos)
|
||||
tornadoEntity.ownerId = this.id
|
||||
tornadoEntity.rotationTracker = PlayerRotationTracker()
|
||||
tornadoEntity.isGrowingMode = true
|
||||
aang.aang_tornadoTasks += mcCoroutineTask(sync = true, client = false, delay = 5.seconds) {
|
||||
tornadoEntity.isGrowingMode = false
|
||||
tornadoEntity.rotationTracker?.movementIncreaseRate =
|
||||
tornadoIncreaseRateProperty.getValue(this@summonTornado.uuid).toFloat()
|
||||
tornadoEntity.rotationTracker?.onlyDecay = true
|
||||
tornadoEntity.rotationTracker?.movementDecayRate =
|
||||
tornadoDecreaseRateProperty.getValue(this@summonTornado.uuid).toFloat()
|
||||
aang.aang_tornadoTasks += mcCoroutineTask(
|
||||
sync = true,
|
||||
client = false,
|
||||
delay = tornadoMaxDurationProperty.getValue(this@summonTornado.uuid).seconds
|
||||
) {
|
||||
tornadoEntity.disappear(tornadoEntity.controllingPassenger)
|
||||
}
|
||||
}
|
||||
this.networkHandler.sendPacket(TitleS2CPacket("SPIN YOUR MOUSE".literal))
|
||||
this.networkHandler.sendPacket(TitleFadeS2CPacket(5, 20, 5))
|
||||
this.isTornadoMode = true
|
||||
this.serverWorld.spawnEntity(tornadoEntity)
|
||||
this.startRiding(tornadoEntity, true)
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
tornadoSoundPacketS2C.receiveOnClient { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = true) {
|
||||
val client = context.client
|
||||
val entity = context.client.world?.getEntityById(packet) as? TornadoEntity? ?: return@mcCoroutineTask
|
||||
client.soundManager.play(TornadoSoundInstance(entity))
|
||||
}
|
||||
}
|
||||
HudRenderCallback.EVENT.register(HudRenderCallback { drawContext, tickCounter ->
|
||||
val player = MinecraftClient.getInstance().player ?: return@HudRenderCallback
|
||||
val vehicle = player.vehicle as? TornadoEntity? ?: return@HudRenderCallback
|
||||
if (vehicle.ownerId != player.id) return@HudRenderCallback
|
||||
if (!vehicle.isGrowingMode) return@HudRenderCallback
|
||||
val rotationTracker = vehicle.rotationTracker ?: return@HudRenderCallback
|
||||
val scale = rotationTracker.getPercentageBetween(1f, 5f)
|
||||
val speed = rotationTracker.getPercentageBetween(2f, 7f)
|
||||
|
||||
val width = drawContext.scaledWindowWidth / 2
|
||||
val height = drawContext.scaledWindowHeight / 2
|
||||
val matrixStack = drawContext.matrices
|
||||
rotationAngle = (rotationAngle + speed) % 360f
|
||||
matrixStack.push()
|
||||
// Bewege den Ursprungspunkt auf die Mitte des Bildschirms
|
||||
matrixStack.translate(width.toDouble(), height.toDouble(), 0.0)
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
|
||||
// Drehe das Zeichen basierend auf der aktuellen Rotation
|
||||
matrixStack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(rotationAngle))
|
||||
|
||||
// Bewege den Ursprungspunkt zurück, um den Text korrekt zu positionieren
|
||||
matrixStack.translate(-width.toDouble(), -height.toDouble(), 0.0)
|
||||
drawContext.drawText(MinecraftClient.getInstance().textRenderer, "↓".literal, width, height, -1, false)
|
||||
matrixStack.pop()
|
||||
})
|
||||
}
|
||||
|
||||
fun init() {
|
||||
if (!FabricLoader.getInstance().isDevelopmentEnvironment) return
|
||||
command("aang") {
|
||||
literal("toggleplayerrotationtracker") {
|
||||
runs {
|
||||
this.source.playerOrThrow.summonTornado()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val tornadoMaxDurationProperty = CooldownProperty(
|
||||
10.0, 3,
|
||||
"Max Duration",
|
||||
AddValueTotal(5.0, 5.0, 5.0)
|
||||
)
|
||||
val tornadoIncreaseRateProperty = NumberProperty(
|
||||
0.005, 3,
|
||||
"Tornado Increase Rate",
|
||||
AddValueTotal(0.0025, 0.0025, 0.005)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(Items.GLOWSTONE_DUST.defaultStack)
|
||||
}
|
||||
}
|
||||
val tornadoDecreaseRateProperty = NumberProperty(
|
||||
0.2, 3,
|
||||
"Tornado Decrease Rate",
|
||||
AddValueTotal(-0.0025, -0.0025, -0.005)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(Items.REDSTONE.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
val Ability = object : PressAbility("Tornado") {
|
||||
|
||||
init {
|
||||
client {
|
||||
this.keyBind = HeroKeyBindings.fourthKeyBinding
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(130.0, 4, AddValueTotal(-10.0, -10.0, -10.0, -10.0))
|
||||
|
||||
this.properties =
|
||||
listOf(tornadoMaxDurationProperty, tornadoIncreaseRateProperty, tornadoDecreaseRateProperty)
|
||||
}
|
||||
|
||||
override fun canUse(player: ServerPlayerEntity): Boolean {
|
||||
return !player.isUsingSpiritualProjection()
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.WIND_CHARGE.defaultStack)
|
||||
}
|
||||
|
||||
override fun hasUnlocked(player: PlayerEntity): Boolean {
|
||||
return player.isCreative || (AirBallAbility.Ability.cooldownProperty.isMaxed(player.uuid) && AirBallAbility.airBallMaxSize.isMaxed(
|
||||
player.uuid
|
||||
))
|
||||
}
|
||||
|
||||
override fun getUnlockCondition(): Text {
|
||||
return literalText {
|
||||
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/quartz_block_bottom.png")
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
private fun cleanUp(player: PlayerEntity) {
|
||||
player.aang.aang_tornadoTasks.forEach { it.cancel() }
|
||||
player.aang.aang_tornadoEntity?.disappear(player)
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
if (!player.isTornadoMode) {
|
||||
abilityScope.cancelCooldown()
|
||||
player.summonTornado()
|
||||
} else {
|
||||
cleanUp(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package gg.norisk.heroes.aang.client.particle
|
||||
|
||||
import gg.norisk.utils.Easing
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.particle.*
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class AirScooterDustParticle internal constructor(
|
||||
clientWorld: ClientWorld,
|
||||
d: Double,
|
||||
e: Double,
|
||||
f: Double,
|
||||
g: Double,
|
||||
h: Double,
|
||||
i: Double,
|
||||
bl: Boolean
|
||||
) : SpriteBillboardParticle(clientWorld, d, e, f) {
|
||||
val scaleAnimation = OldAnimation(0f, 3.5f, 1.seconds.toJavaDuration(), Easing.LINEAR)
|
||||
|
||||
init {
|
||||
this.setBoundingBoxSpacing(0.25f, 0.25f)
|
||||
this.maxAge = random.nextInt(50)
|
||||
this.gravityStrength = 3.0E-6f
|
||||
this.velocityX = g
|
||||
this.velocityY = h
|
||||
this.velocityZ = i
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
this.prevPosX = this.x
|
||||
this.prevPosY = this.y
|
||||
this.prevPosZ = this.z
|
||||
this.scale = scaleAnimation.get()
|
||||
if (age++ < this.maxAge && !(this.alpha <= 0.0f)) {
|
||||
this.velocityX += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
|
||||
this.velocityZ += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
|
||||
this.velocityY -= gravityStrength.toDouble()
|
||||
this.move(this.velocityX, this.velocityY, this.velocityZ)
|
||||
if (this.age >= this.maxAge - 60 && this.alpha > 0.01f) {
|
||||
this.alpha -= 0.015f
|
||||
}
|
||||
} else {
|
||||
this.markDead()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): ParticleTextureSheet {
|
||||
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class Factory(private val spriteProvider: SpriteProvider) : ParticleFactory<ParticleEffect> {
|
||||
override fun createParticle(
|
||||
defaultParticleType: ParticleEffect,
|
||||
clientWorld: ClientWorld,
|
||||
d: Double,
|
||||
e: Double,
|
||||
f: Double,
|
||||
g: Double,
|
||||
h: Double,
|
||||
i: Double
|
||||
): Particle {
|
||||
val airScooterParticle = AirScooterDustParticle(clientWorld, d, e, f, g, h, i, false)
|
||||
airScooterParticle.setAlpha(0.5f)
|
||||
airScooterParticle.setSprite(this.spriteProvider)
|
||||
return airScooterParticle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package gg.norisk.heroes.aang.client.particle
|
||||
|
||||
import gg.norisk.utils.Easing
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.particle.*
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
class BendingAirParticle internal constructor(
|
||||
clientWorld: ClientWorld,
|
||||
d: Double,
|
||||
e: Double,
|
||||
f: Double,
|
||||
g: Double,
|
||||
h: Double,
|
||||
i: Double,
|
||||
bl: Boolean
|
||||
) : SpriteBillboardParticle(clientWorld, d, e, f) {
|
||||
val scaleAnimation = OldAnimation(0.2f, 0.5f, 1.seconds.toJavaDuration(), Easing.LINEAR)
|
||||
|
||||
init {
|
||||
this.setBoundingBoxSpacing(0.25f, 0.25f)
|
||||
this.maxAge = random.nextInt(50)
|
||||
this.gravityStrength = 3.0E-6f
|
||||
this.velocityX = g
|
||||
this.velocityY = h
|
||||
this.velocityZ = i
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
this.prevPosX = this.x
|
||||
this.prevPosY = this.y
|
||||
this.prevPosZ = this.z
|
||||
this.scale = scaleAnimation.get()
|
||||
if (age++ < this.maxAge && !(this.alpha <= 0.0f)) {
|
||||
this.velocityX += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
|
||||
this.velocityZ += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
|
||||
this.velocityY -= gravityStrength.toDouble()
|
||||
this.move(this.velocityX, this.velocityY, this.velocityZ)
|
||||
if (this.age >= this.maxAge - 60 && this.alpha > 0.01f) {
|
||||
this.alpha -= 0.015f
|
||||
}
|
||||
} else {
|
||||
this.markDead()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): ParticleTextureSheet {
|
||||
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class Factory(private val spriteProvider: SpriteProvider) : ParticleFactory<ParticleEffect> {
|
||||
override fun createParticle(
|
||||
defaultParticleType: ParticleEffect,
|
||||
clientWorld: ClientWorld,
|
||||
d: Double,
|
||||
e: Double,
|
||||
f: Double,
|
||||
g: Double,
|
||||
h: Double,
|
||||
i: Double
|
||||
): Particle {
|
||||
val airScooterParticle = BendingAirParticle(clientWorld, d, e, f, g, h, i, false)
|
||||
airScooterParticle.setAlpha(0.5f)
|
||||
airScooterParticle.setSprite(this.spriteProvider)
|
||||
return airScooterParticle
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package gg.norisk.heroes.aang.client.render.entity
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.client.render.entity.model.AirScooterEntityModel
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import gg.norisk.heroes.aang.registry.EntityRendererRegistry
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.MathHelper
|
||||
|
||||
class AirScooterEntityRenderer(context: EntityRendererFactory.Context) :
|
||||
LivingEntityRenderer<AirScooterEntity, AirScooterEntityModel>(
|
||||
context,
|
||||
AirScooterEntityModel(context.getPart(EntityRendererRegistry.AIR_SCOOTER_LAYER)),
|
||||
0f
|
||||
) {
|
||||
override fun render(
|
||||
abstractWindChargeEntity: AirScooterEntity,
|
||||
f: Float,
|
||||
g: Float,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int
|
||||
) {
|
||||
if (abstractWindChargeEntity.age >= 2 || !(dispatcher.camera.focusedEntity.squaredDistanceTo(
|
||||
abstractWindChargeEntity
|
||||
) < RANDOM_MOJANG_FIELD.toDouble())
|
||||
) {
|
||||
matrixStack.push()
|
||||
val h = abstractWindChargeEntity.age.toFloat() + g
|
||||
val vertexConsumer = vertexConsumerProvider.getBuffer(
|
||||
RenderLayer.getBreezeWind(
|
||||
TEXTURE,
|
||||
getXOffset(h) % 1.0f, 0.0f
|
||||
)
|
||||
)
|
||||
model.setAngles(abstractWindChargeEntity, 0.0f, 0.0f, h, 0.0f, 0.0f)
|
||||
val scale: Float = abstractWindChargeEntity.getLerpedScale(g * 0.05f)
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
model.render(matrixStack, vertexConsumer, i, OverlayTexture.DEFAULT_UV)
|
||||
matrixStack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getTexture(abstractWindChargeEntity: AirScooterEntity): Identifier {
|
||||
return TEXTURE
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getXOffset(f: Float): Float {
|
||||
return f * 0.03f
|
||||
}
|
||||
|
||||
private val RANDOM_MOJANG_FIELD = MathHelper.square(3.5f)
|
||||
val TEXTURE: Identifier = "textures/entity/projectiles/air_scooter.png".toId()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
package gg.norisk.heroes.aang.client.render.entity
|
||||
|
||||
import gg.norisk.heroes.aang.client.render.entity.feature.TornadoWindFeatureRenderer
|
||||
import gg.norisk.heroes.aang.client.render.entity.model.TornadoEntityModel
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory
|
||||
import net.minecraft.client.render.entity.MobEntityRenderer
|
||||
import net.minecraft.client.render.entity.model.EntityModelLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class TornadoEntityRenderer(context: EntityRendererFactory.Context) :
|
||||
MobEntityRenderer<TornadoEntity, TornadoEntityModel>(
|
||||
context, TornadoEntityModel(context.getPart(EntityModelLayers.BREEZE)), 0f
|
||||
) {
|
||||
init {
|
||||
addFeature(TornadoWindFeatureRenderer(context, this))
|
||||
}
|
||||
|
||||
override fun getTexture(entity: TornadoEntity): Identifier {
|
||||
return TEXTURE
|
||||
}
|
||||
|
||||
override fun render(
|
||||
breezeEntity: TornadoEntity,
|
||||
f: Float,
|
||||
g: Float,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int
|
||||
) {
|
||||
matrixStack.push()
|
||||
val model: TornadoEntityModel = getModel()
|
||||
updatePartVisibility(model, model.head, model.rods)
|
||||
model.head.visible = false
|
||||
model.rods.visible = false
|
||||
// Hier wird die neue Rotation berechnet
|
||||
val m = breezeEntity.age.toFloat() + g
|
||||
val rotationAngle = this.getRotationAngle(m)
|
||||
|
||||
// Rotiert die Entität kontinuierlich um die Y-Achse (vertikale Achse)
|
||||
matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(rotationAngle))
|
||||
super.render(breezeEntity, f, g, matrixStack, vertexConsumerProvider, i)
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
private fun getRotationAngle(ageInTicks: Float): Float {
|
||||
val rotationSpeed = 20.0f // Passt die Rotationsgeschwindigkeit an (Winkel pro Tick)
|
||||
return (ageInTicks * rotationSpeed) % 360.0f // Vollständige Rotation (0 bis 360 Grad)
|
||||
}
|
||||
|
||||
override fun setupTransforms(
|
||||
livingEntity: TornadoEntity?,
|
||||
matrixStack: MatrixStack?,
|
||||
f: Float,
|
||||
g: Float,
|
||||
h: Float,
|
||||
i: Float
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TEXTURE: Identifier = Identifier.ofVanilla("textures/entity/breeze/breeze.png")
|
||||
|
||||
fun updatePartVisibility(
|
||||
breezeEntityModel: TornadoEntityModel, vararg modelParts: ModelPart
|
||||
): TornadoEntityModel {
|
||||
breezeEntityModel.head.visible = false
|
||||
breezeEntityModel.eyes.visible = false
|
||||
breezeEntityModel.rods.visible = false
|
||||
breezeEntityModel.windBody.visible = false
|
||||
val var2: Array<out ModelPart> = modelParts
|
||||
val var3 = modelParts.size
|
||||
|
||||
for (var4 in 0 until var3) {
|
||||
val modelPart = var2[var4]
|
||||
modelPart.visible = true
|
||||
}
|
||||
|
||||
return breezeEntityModel
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package gg.norisk.heroes.aang.client.render.entity.feature
|
||||
|
||||
import gg.norisk.heroes.aang.client.render.entity.model.AirScooterEntityModel
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.entity.feature.FeatureRenderer
|
||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext
|
||||
import net.minecraft.client.render.entity.model.EntityModel
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
class AirScooterFeatureRenderer<T : Entity, M : EntityModel<T>>(featureRendererContext: FeatureRendererContext<T, M>) :
|
||||
FeatureRenderer<T, M>(featureRendererContext) {
|
||||
val airBall = AirScooterEntityModel(AirScooterEntityModel.getTexturedModelData().createModel())
|
||||
|
||||
override fun render(
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int,
|
||||
entity: T,
|
||||
f: Float,
|
||||
g: Float,
|
||||
h: Float,
|
||||
j: Float,
|
||||
k: Float,
|
||||
l: Float
|
||||
) {
|
||||
/*val player = entity as? PlayerEntity? ?: return
|
||||
if (!player.isAirScooting) return
|
||||
matrixStack.push()
|
||||
val age = entity.age.toFloat() + h
|
||||
val vertexConsumer = vertexConsumerProvider.getBuffer(
|
||||
RenderLayer.getBreezeWind(
|
||||
TEXTURE,
|
||||
getXOffset(age) % 1.0f, 0.0f
|
||||
)
|
||||
)
|
||||
val scale: Float = 3.0f
|
||||
matrixStack.translate(0.0, entity.getEyeHeight(entity.pose).toDouble(), 0.0)
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
airBall.setAngles(null, 0.0f, 0.0f, age, 0.0f, 0.0f)
|
||||
airBall.render(matrixStack, vertexConsumer, i, OverlayTexture.DEFAULT_UV)
|
||||
matrixStack.pop()*/
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package gg.norisk.heroes.aang.client.render.entity.feature
|
||||
|
||||
import gg.norisk.heroes.aang.client.render.entity.TornadoEntityRenderer.Companion.updatePartVisibility
|
||||
import gg.norisk.heroes.aang.client.render.entity.model.TornadoEntityModel
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory
|
||||
import net.minecraft.client.render.entity.feature.FeatureRenderer
|
||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext
|
||||
import net.minecraft.client.render.entity.model.EntityModelLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class TornadoWindFeatureRenderer(
|
||||
context: EntityRendererFactory.Context,
|
||||
featureRendererContext: FeatureRendererContext<TornadoEntity?, TornadoEntityModel?>?
|
||||
) : FeatureRenderer<TornadoEntity, TornadoEntityModel?>(featureRendererContext) {
|
||||
private val model = TornadoEntityModel(context.getPart(EntityModelLayers.BREEZE_WIND))
|
||||
|
||||
override fun render(
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int,
|
||||
breezeEntity: TornadoEntity,
|
||||
f: Float,
|
||||
g: Float,
|
||||
h: Float,
|
||||
j: Float,
|
||||
k: Float,
|
||||
l: Float
|
||||
) {
|
||||
val m = breezeEntity.age.toFloat() + h
|
||||
val vertexConsumer =
|
||||
vertexConsumerProvider.getBuffer(RenderLayer.getBreezeWind(TEXTURE, this.getXOffset(m) % 1.0f, 0.0f))
|
||||
model.setAngles(breezeEntity, f, g, j, k, l)
|
||||
updatePartVisibility(this.model, model.windBody).render(
|
||||
matrixStack,
|
||||
vertexConsumer,
|
||||
i,
|
||||
OverlayTexture.DEFAULT_UV
|
||||
)
|
||||
}
|
||||
|
||||
private fun getXOffset(f: Float): Float {
|
||||
return f * 0.02f
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TEXTURE: Identifier = Identifier.ofVanilla("textures/entity/breeze/breeze_wind.png")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package gg.norisk.heroes.aang.client.render.entity.model
|
||||
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import net.minecraft.client.model.*
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.entity.model.SinglePartEntityModel
|
||||
|
||||
class AirScooterEntityModel(modelPart: ModelPart) : SinglePartEntityModel<AirScooterEntity>(
|
||||
RenderLayer::getEntityTranslucent
|
||||
) {
|
||||
private val bone: ModelPart = modelPart.getChild("bone")
|
||||
private val windCharge: ModelPart = bone.getChild("wind_charge")
|
||||
private val wind: ModelPart = bone.getChild("wind")
|
||||
|
||||
override fun setAngles(
|
||||
abstractWindChargeEntity: AirScooterEntity?, f: Float, g: Float, h: Float, i: Float, j: Float
|
||||
) {
|
||||
windCharge.yaw = -h * 16.0f * ((Math.PI / 180.0).toFloat())
|
||||
wind.yaw = h * 16.0f * ((Math.PI / 180.0).toFloat())
|
||||
}
|
||||
|
||||
override fun getPart(): ModelPart {
|
||||
return this.bone
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getTexturedModelData(): TexturedModelData {
|
||||
val modelData = ModelData()
|
||||
val modelPartData = modelData.root
|
||||
val modelPartData2 =
|
||||
modelPartData.addChild("bone", ModelPartBuilder.create(), ModelTransform.pivot(0.0f, 0.0f, 0.0f))
|
||||
modelPartData2.addChild(
|
||||
"wind",
|
||||
ModelPartBuilder.create()
|
||||
.uv(15, 20).cuboid(-4.0f, -1.0f, -4.0f, 8.0f, 2.0f, 8.0f, Dilation(0.0f))
|
||||
.uv(0, 9).cuboid(-3.0f, -2.0f, -3.0f, 6.0f, 4.0f, 6.0f, Dilation(0.0f)),
|
||||
ModelTransform.of(0.0f, 0.0f, 0.0f, 0.0f, -0.7854f, 0.0f)
|
||||
)
|
||||
modelPartData2.addChild(
|
||||
"wind_charge",
|
||||
ModelPartBuilder.create().uv(0, 0).cuboid(-2.0f, -2.0f, -2.0f, 4.0f, 4.0f, 4.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, 0.0f, 0.0f)
|
||||
)
|
||||
return TexturedModelData.of(modelData, 64, 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package gg.norisk.heroes.aang.client.render.entity.model
|
||||
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import net.minecraft.client.model.*
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.entity.model.SinglePartEntityModel
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import java.util.function.Function
|
||||
|
||||
class TornadoEntityModel(private val root: ModelPart) : SinglePartEntityModel<TornadoEntity>(
|
||||
Function(RenderLayer::getEntityTranslucent)
|
||||
) {
|
||||
val head: ModelPart
|
||||
val eyes: ModelPart
|
||||
val windBody: ModelPart = root.getChild("wind_body")
|
||||
private val windTop: ModelPart
|
||||
private val windMid: ModelPart
|
||||
private val windBottom: ModelPart = windBody.getChild("wind_bottom")
|
||||
val rods: ModelPart
|
||||
|
||||
init {
|
||||
this.windMid = windBottom.getChild("wind_mid")
|
||||
this.windTop = windMid.getChild("wind_top")
|
||||
this.head = root.getChild("body").getChild("head")
|
||||
this.eyes = head.getChild("eyes")
|
||||
this.rods = root.getChild("body").getChild("rods")
|
||||
}
|
||||
|
||||
override fun setAngles(breezeEntity: TornadoEntity, f: Float, g: Float, h: Float, i: Float, j: Float) {
|
||||
this.part.traverse().forEach { obj: ModelPart -> obj.resetTransform() }
|
||||
val k = h * 3.1415927f * -0.1f
|
||||
windTop.pivotX = MathHelper.cos(k) * 1.0f * 0.6f
|
||||
windTop.pivotZ = MathHelper.sin(k) * 1.0f * 0.6f
|
||||
windMid.pivotX = MathHelper.sin(k) * 0.5f * 0.8f
|
||||
windMid.pivotZ = MathHelper.cos(k) * 0.8f
|
||||
windBottom.pivotX = MathHelper.cos(k) * -0.25f * 1.0f
|
||||
windBottom.pivotZ = MathHelper.sin(k) * -0.25f * 1.0f
|
||||
head.pivotY = 4.0f + MathHelper.cos(k) / 4.0f
|
||||
rods.yaw = h * 3.1415927f * 0.1f
|
||||
}
|
||||
|
||||
override fun getPart(): ModelPart {
|
||||
return this.root
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val field_47431 = 0.6f
|
||||
private const val field_47432 = 0.8f
|
||||
private const val field_47433 = 1.0f
|
||||
fun getTexturedModelData(i: Int, j: Int): TexturedModelData {
|
||||
val modelData = ModelData()
|
||||
val modelPartData = modelData.root
|
||||
val modelPartData2 =
|
||||
modelPartData.addChild("body", ModelPartBuilder.create(), ModelTransform.pivot(0.0f, 0.0f, 0.0f))
|
||||
val modelPartData3 =
|
||||
modelPartData2.addChild("rods", ModelPartBuilder.create(), ModelTransform.pivot(0.0f, 8.0f, 0.0f))
|
||||
modelPartData3.addChild(
|
||||
"rod_1",
|
||||
ModelPartBuilder.create().uv(0, 17).cuboid(-1.0f, 0.0f, -3.0f, 2.0f, 8.0f, 2.0f, Dilation(0.0f)),
|
||||
ModelTransform.of(2.5981f, -3.0f, 1.5f, -2.7489f, -1.0472f, 3.1416f)
|
||||
)
|
||||
modelPartData3.addChild(
|
||||
"rod_2",
|
||||
ModelPartBuilder.create().uv(0, 17).cuboid(-1.0f, 0.0f, -3.0f, 2.0f, 8.0f, 2.0f, Dilation(0.0f)),
|
||||
ModelTransform.of(-2.5981f, -3.0f, 1.5f, -2.7489f, 1.0472f, 3.1416f)
|
||||
)
|
||||
modelPartData3.addChild(
|
||||
"rod_3",
|
||||
ModelPartBuilder.create().uv(0, 17).cuboid(-1.0f, 0.0f, -3.0f, 2.0f, 8.0f, 2.0f, Dilation(0.0f)),
|
||||
ModelTransform.of(0.0f, -3.0f, -3.0f, 0.3927f, 0.0f, 0.0f)
|
||||
)
|
||||
val modelPartData4 = modelPartData2.addChild(
|
||||
"head",
|
||||
ModelPartBuilder.create().uv(4, 24).cuboid(-5.0f, -5.0f, -4.2f, 10.0f, 3.0f, 4.0f, Dilation(0.0f))
|
||||
.uv(0, 0).cuboid(-4.0f, -8.0f, -4.0f, 8.0f, 8.0f, 8.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, 4.0f, 0.0f)
|
||||
)
|
||||
modelPartData4.addChild(
|
||||
"eyes",
|
||||
ModelPartBuilder.create().uv(4, 24).cuboid(-5.0f, -5.0f, -4.2f, 10.0f, 3.0f, 4.0f, Dilation(0.0f))
|
||||
.uv(0, 0).cuboid(-4.0f, -8.0f, -4.0f, 8.0f, 8.0f, 8.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, 0.0f, 0.0f)
|
||||
)
|
||||
val modelPartData5 =
|
||||
modelPartData.addChild("wind_body", ModelPartBuilder.create(), ModelTransform.pivot(0.0f, 0.0f, 0.0f))
|
||||
val modelPartData6 = modelPartData5.addChild(
|
||||
"wind_bottom",
|
||||
ModelPartBuilder.create().uv(1, 83).cuboid(-2.5f, -7.0f, -2.5f, 5.0f, 7.0f, 5.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, 24.0f, 0.0f)
|
||||
)
|
||||
val modelPartData7 = modelPartData6.addChild(
|
||||
"wind_mid",
|
||||
ModelPartBuilder.create().uv(74, 28).cuboid(-6.0f, -6.0f, -6.0f, 12.0f, 6.0f, 12.0f, Dilation(0.0f))
|
||||
.uv(78, 32).cuboid(-4.0f, -6.0f, -4.0f, 8.0f, 6.0f, 8.0f, Dilation(0.0f)).uv(49, 71)
|
||||
.cuboid(-2.5f, -6.0f, -2.5f, 5.0f, 6.0f, 5.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, -7.0f, 0.0f)
|
||||
)
|
||||
modelPartData7.addChild(
|
||||
"wind_top",
|
||||
ModelPartBuilder.create().uv(0, 0).cuboid(-9.0f, -8.0f, -9.0f, 18.0f, 8.0f, 18.0f, Dilation(0.0f))
|
||||
.uv(6, 6).cuboid(-6.0f, -8.0f, -6.0f, 12.0f, 8.0f, 12.0f, Dilation(0.0f)).uv(105, 57)
|
||||
.cuboid(-2.5f, -8.0f, -2.5f, 5.0f, 8.0f, 5.0f, Dilation(0.0f)),
|
||||
ModelTransform.pivot(0.0f, -6.0f, 0.0f)
|
||||
)
|
||||
return TexturedModelData.of(modelData, i, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package gg.norisk.heroes.aang.client.sound
|
||||
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.isAirBending
|
||||
import gg.norisk.heroes.aang.entity.aang
|
||||
import gg.norisk.heroes.aang.registry.SoundRegistry
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
class AirBendingCircleSoundInstance(private val entity: PlayerEntity) :
|
||||
MovingSoundInstance(SoundRegistry.FLYING, 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()
|
||||
|
||||
val progress = entity.aang.aang_airBallSpinTracker.getSpinProgress()
|
||||
if (!entity.isAirBending) {
|
||||
isFading = true
|
||||
return
|
||||
}
|
||||
|
||||
if (!entity.isRemoved) {
|
||||
val percentage = (progress / 100.0) * 0.2
|
||||
val f: Float = Math.min(0.5f, Math.max(0.1f, percentage.toFloat()))
|
||||
this.volume = f
|
||||
this.pitch = 1f + this.volume
|
||||
} else {
|
||||
isFading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package gg.norisk.heroes.aang.client.sound
|
||||
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility.isSpiritualLevitating
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
class AirBendingLevitationSoundInstance(private val entity: PlayerEntity) :
|
||||
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 && entity.isSpiritualLevitating) {
|
||||
this.volume = 0.75f
|
||||
this.pitch = 0.5f
|
||||
} else {
|
||||
isFading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package gg.norisk.heroes.aang.client.sound
|
||||
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility.isAirScooting
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import gg.norisk.heroes.aang.registry.SoundRegistry
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
class AirScooterSoundInstance(private val entity: Entity) :
|
||||
MovingSoundInstance(SoundRegistry.FLYING, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.3f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
val flag = when {
|
||||
entity is AirScooterEntity -> true
|
||||
entity is PlayerEntity && entity.isAirScooting -> true
|
||||
else -> false
|
||||
}
|
||||
if (!entity.isRemoved && flag) {
|
||||
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.1f, this.entity.velocity.lengthSquared().toFloat()))
|
||||
this.volume = f
|
||||
this.pitch = 1f + this.volume
|
||||
} else {
|
||||
this.setDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package gg.norisk.heroes.aang.client.sound
|
||||
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import gg.norisk.heroes.aang.registry.SoundRegistry
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
class TornadoSoundInstance(private val entity: TornadoEntity) :
|
||||
MovingSoundInstance(SoundRegistry.FLYING, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.3f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if (!entity.isRemoved) {
|
||||
|
||||
val clientPlayer = MinecraftClient.getInstance().player ?: return
|
||||
if (entity.controllingPassenger?.id == clientPlayer.id) {
|
||||
this.x = clientPlayer.x.toFloat().toDouble() + 20
|
||||
this.y = clientPlayer.y.toFloat().toDouble() + 19
|
||||
this.z = clientPlayer.z.toFloat().toDouble()
|
||||
} else {
|
||||
this.x = entity.x.toFloat().toDouble()
|
||||
this.y = entity.y.toFloat().toDouble()
|
||||
this.z = entity.z.toFloat().toDouble()
|
||||
}
|
||||
|
||||
|
||||
val f: Float = this.entity.scale / 5f
|
||||
this.volume = f
|
||||
this.pitch = 1f + this.volume
|
||||
} else {
|
||||
this.setDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package gg.norisk.heroes.aang.client.sound
|
||||
|
||||
import gg.norisk.heroes.aang.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
package gg.norisk.heroes.aang.entity
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.getAirBendingPos
|
||||
import gg.norisk.heroes.aang.ability.AirBallAbility.isAirBending
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility.isAirScooting
|
||||
import gg.norisk.heroes.aang.ability.AirScooterAbility.stopRidingAirBall
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.utils.Easing
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import net.minecraft.entity.*
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.damage.DamageTypes
|
||||
import net.minecraft.entity.mob.MobEntity
|
||||
import net.minecraft.entity.mob.PathAwareEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.tag.BlockTags
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.explosion.AdvancedExplosionBehavior
|
||||
import net.silkmc.silk.core.entity.modifyVelocity
|
||||
import net.silkmc.silk.core.text.broadcastText
|
||||
import java.util.*
|
||||
import java.util.function.Function
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
class AirScooterEntity(entityType: EntityType<out PathAwareEntity>, world: World) :
|
||||
PathAwareEntity(entityType, world) {
|
||||
val startScaleAnimation = OldAnimation(0f, 3f, 1.seconds.toJavaDuration(), Easing.EXPO_OUT)
|
||||
var currentScale: Float = 0f
|
||||
var wasBended = false
|
||||
var isComingBack = false
|
||||
val pickedUpEntities = mutableSetOf<UUID>()
|
||||
|
||||
var wasLaunched: Boolean
|
||||
get() = this.getSyncedData<Boolean>("AirScooter:WasLaunched") ?: false
|
||||
set(value) = this.setSyncedData("AirScooter:WasLaunched", value)
|
||||
|
||||
enum class Type {
|
||||
SCOOTER, PROJECTILE
|
||||
}
|
||||
|
||||
init {
|
||||
this.ignoreCameraFrustum = true
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue = 2.0
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_SCALE)?.baseValue = 0.0
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue = 0.02
|
||||
}
|
||||
|
||||
// Apply player-controlled movement
|
||||
override fun travel(pos: Vec3d) {
|
||||
this.setNoDrag(true)
|
||||
this.setNoGravity(wasLaunched)
|
||||
super.travel(pos)
|
||||
}
|
||||
|
||||
override fun canWalkOnFluid(fluidState: FluidState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun shouldRenderName(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onStartedTrackingBy(player: ServerPlayerEntity) {
|
||||
super.onStartedTrackingBy(player)
|
||||
if (bendingType == Type.SCOOTER) {
|
||||
AirScooterAbility.airScooterSoundPacketS2C.send(id, player)
|
||||
}
|
||||
}
|
||||
|
||||
fun getLerpedScale(f: Float): Float {
|
||||
currentScale = MathHelper.lerp(f, currentScale, this.scale)
|
||||
return currentScale
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
when (bendingType) {
|
||||
Type.SCOOTER -> handleAirScooterType()
|
||||
Type.PROJECTILE -> {
|
||||
handleProjectileType()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleProjectileType() {
|
||||
noClip = false
|
||||
|
||||
if (!wasLaunched && !isBoomerang && !world.isClient) {
|
||||
val owner = getOwner()
|
||||
val targetPos = owner?.getAirBendingPos()
|
||||
if (owner != null && targetPos != null) {
|
||||
val direction = targetPos.subtract(this.pos).normalize()
|
||||
val distance = targetPos.distanceTo(this.pos)
|
||||
|
||||
// Je näher das Projektil am Ziel ist, desto kleiner wird der Multiplikationsfaktor
|
||||
val speedMultiplier = distance
|
||||
|
||||
modifyVelocity(direction.multiply(speedMultiplier))
|
||||
}
|
||||
}
|
||||
|
||||
val player = getOwner()
|
||||
if (isBoomerang && player != null && !world.isClient) {
|
||||
val distanceToPlayer = this.distanceTo(player)
|
||||
pickUpNearbyItems()
|
||||
|
||||
if (isComingBack) {
|
||||
modifyVelocity(player.getAirBendingPos().subtract(this.pos).normalize().multiply(2.0))
|
||||
if (distanceToPlayer < 4) {
|
||||
isBoomerang = false
|
||||
}
|
||||
} else if (distanceToPlayer > 25) {
|
||||
isComingBack = true
|
||||
}
|
||||
}
|
||||
|
||||
val distanceFlag = if (player != null) distanceTo(player) > 100 else false
|
||||
|
||||
if (!world.isClient && wasLaunched) {
|
||||
if (horizontalCollision || verticalCollision || player == null || distanceFlag) {
|
||||
this.discard()
|
||||
this.createExplosion(this.blockPos.toCenterPos())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickUpNearbyItems() {
|
||||
val player = getOwner() ?: return
|
||||
world.getOtherEntities(this, this.boundingBox.expand(2.0)) { it is ItemEntity || it is MobEntity }.forEach {
|
||||
val direction = this.pos.subtract(it.pos)
|
||||
if (it.distanceTo(player) < 8) {
|
||||
it.modifyVelocity(direction.normalize().multiply(0.8))
|
||||
} else {
|
||||
it.modifyVelocity(direction)
|
||||
}
|
||||
if (!pickedUpEntities.contains(it.uuid)) {
|
||||
pickedUpEntities.add(it.uuid)
|
||||
it.sound(SoundEvents.ENTITY_ITEM_PICKUP, 0.2f, Random.nextDouble(1.0, 2.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAirScooterType() {
|
||||
noClip = true
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_SCALE)?.baseValue = startScaleAnimation.get().toDouble()
|
||||
val owner = getOwner()
|
||||
if (owner != null && !world.isClient) {
|
||||
setPosition(owner.pos.add(0.0, 0.2, 0.0))
|
||||
}
|
||||
if (getOwner()?.isAirScooting == false) {
|
||||
this.discard()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOwner(): PlayerEntity? {
|
||||
val id = if (ownerId != -1) ownerId else return null
|
||||
return world.getEntityById(id) as? PlayerEntity?
|
||||
}
|
||||
|
||||
override fun handleFallDamage(f: Float, g: Float, damageSource: DamageSource?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun damage(damageSource: DamageSource, f: Float): Boolean {
|
||||
if (world.isClient) {
|
||||
return false
|
||||
} else if (this.isDead) {
|
||||
return false
|
||||
}
|
||||
if (damageSource.isOf(DamageTypes.GENERIC_KILL)) {
|
||||
return super.damage(damageSource, f)
|
||||
}
|
||||
val attacker = damageSource.attacker as? LivingEntity ?: return false
|
||||
if (attacker.id == ownerId) {
|
||||
if (bendingType == Type.SCOOTER) {
|
||||
return false
|
||||
}
|
||||
if ((attacker as? PlayerEntity?)?.isAirBending == true) return false
|
||||
wasLaunched = true
|
||||
sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.2f, pitch = 2f)
|
||||
setVelocity(attacker, attacker.pitch, attacker.yaw, 0.0f, 2.5f, 1.0f)
|
||||
return false
|
||||
} else {
|
||||
getOwner()?.stopRidingAirBall()
|
||||
this.discard()
|
||||
this.createExplosion(this.pos, 1f)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun launchBoomerang() {
|
||||
isBoomerang = true
|
||||
sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.2f, pitch = 2f)
|
||||
val player = getOwner() ?: return
|
||||
setVelocity(player, player.pitch, player.yaw, 0.0f, 1.5f, 1.0f)
|
||||
}
|
||||
|
||||
var isBoomerang: Boolean
|
||||
get() = this.getSyncedData<Boolean>("AirBallIsBoomerang") ?: false
|
||||
set(value) {
|
||||
if (!value) {
|
||||
isComingBack = false
|
||||
pickedUpEntities.clear()
|
||||
}
|
||||
this.setSyncedData("AirBallIsBoomerang", value)
|
||||
}
|
||||
|
||||
var ownerId: Int
|
||||
get() = this.getSyncedData<Int>("AirBallOwnerId") ?: -1
|
||||
set(value) {
|
||||
this.setSyncedData("AirBallOwnerId", value)
|
||||
}
|
||||
|
||||
var bendingType: Type
|
||||
get() = Type.valueOf(this.getSyncedData<String>("BendingType") ?: Type.SCOOTER.name)
|
||||
set(value) {
|
||||
this.setSyncedData("BendingType", value.name)
|
||||
}
|
||||
|
||||
override fun calculateBoundingBox(): Box {
|
||||
val f = getDimensions(EntityPose.STANDING).withEyeHeight(0f).width / 2.0f
|
||||
val g = getDimensions(EntityPose.STANDING).withEyeHeight(0f).height
|
||||
val h = 0.15f * scale
|
||||
return Box(
|
||||
pos.x - f.toDouble(),
|
||||
pos.y - h,
|
||||
pos.z - f.toDouble(),
|
||||
pos.x + f.toDouble(),
|
||||
pos.y - h + g.toDouble(),
|
||||
pos.z + f.toDouble()
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
private fun createExplosion(vec3d: Vec3d, power: Float = 1.2f * scale) {
|
||||
world
|
||||
.createExplosion(
|
||||
this,
|
||||
null,
|
||||
AdvancedExplosionBehavior(
|
||||
true,
|
||||
false,
|
||||
Optional.of(power),
|
||||
Registries.BLOCK.getEntryList(BlockTags.BLOCKS_WIND_CHARGE_EXPLOSIONS).map(Function.identity())
|
||||
),
|
||||
vec3d.getX(),
|
||||
vec3d.getY(),
|
||||
vec3d.getZ(),
|
||||
power,
|
||||
false,
|
||||
World.ExplosionSourceType.TRIGGER,
|
||||
ParticleTypes.GUST_EMITTER_SMALL,
|
||||
ParticleTypes.GUST_EMITTER_LARGE,
|
||||
SoundEvents.ENTITY_WIND_CHARGE_WIND_BURST
|
||||
)
|
||||
}
|
||||
|
||||
override fun isCollidable(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun collidesWith(entity: Entity?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package gg.norisk.heroes.aang.entity
|
||||
|
||||
import com.mojang.authlib.GameProfile
|
||||
import gg.norisk.emote.network.EmoteNetworking.emoteS2CPacket
|
||||
import gg.norisk.emote.network.EmoteSync
|
||||
import gg.norisk.heroes.aang.ability.SpiritualProjectionAbility.cancelProjection
|
||||
import net.minecraft.client.network.OtherClientPlayerEntity
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import org.apache.commons.lang3.RandomStringUtils
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
|
||||
import org.spongepowered.asm.mixin.injection.invoke.arg.Args
|
||||
import java.util.*
|
||||
|
||||
class DummyPlayer(
|
||||
world: World,
|
||||
blockPos: BlockPos,
|
||||
f: Float,
|
||||
gameProfile: GameProfile
|
||||
) : PlayerEntity(
|
||||
world,
|
||||
blockPos,
|
||||
f,
|
||||
gameProfile
|
||||
) {
|
||||
override fun isSpectator(): Boolean = false
|
||||
override fun isCreative(): Boolean = false
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
}
|
||||
|
||||
override fun handleAttack(entity: Entity): Boolean {
|
||||
cancelProjection(entity)
|
||||
return super.handleAttack(entity)
|
||||
}
|
||||
|
||||
override fun interactAt(playerEntity: PlayerEntity, vec3d: Vec3d, hand: Hand): ActionResult {
|
||||
if (hand == Hand.MAIN_HAND) {
|
||||
cancelProjection(playerEntity)
|
||||
}
|
||||
return super.interactAt(playerEntity, vec3d, hand)
|
||||
}
|
||||
|
||||
override fun onDeath(damageSource: DamageSource) {
|
||||
cancelProjection(damageSource.attacker)
|
||||
super.onDeath(damageSource)
|
||||
}
|
||||
|
||||
fun playEmote(emote: Identifier) {
|
||||
emoteS2CPacket.sendToAll(EmoteSync(this.id, emote, EmoteSync.State.PLAY))
|
||||
}
|
||||
|
||||
fun stopEmote(emote: Identifier) {
|
||||
emoteS2CPacket.sendToAll(EmoteSync(this.id, emote, EmoteSync.State.STOP))
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun UUID.isFakeUUID(): Boolean {
|
||||
return this.toString().startsWith("00000000-0000-0000")
|
||||
}
|
||||
|
||||
fun handleDummyPlayerSpawn(
|
||||
packet: EntitySpawnS2CPacket,
|
||||
callback: CallbackInfoReturnable<Entity>,
|
||||
world: ClientWorld
|
||||
) {
|
||||
if (packet.uuid.isFakeUUID()) {
|
||||
val player = OtherClientPlayerEntity(
|
||||
world,
|
||||
GameProfile(packet.uuid, RandomStringUtils.randomAlphabetic(16))
|
||||
)
|
||||
callback.setReturnValue(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package gg.norisk.heroes.aang.entity
|
||||
|
||||
import gg.norisk.heroes.aang.utils.EntitySpinTracker
|
||||
import gg.norisk.heroes.aang.utils.PlayerRotationTracker
|
||||
import kotlinx.coroutines.Job
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
|
||||
interface IAangPlayer {
|
||||
var rotationTracker: PlayerRotationTracker?
|
||||
val aang_airScooterTasks: MutableList<Job>
|
||||
val aang_spiritualProjectionsTasks: MutableList<Job>
|
||||
val aang_tornadoTasks: MutableList<Job>
|
||||
var aang_tornadoEntity: TornadoEntity?
|
||||
var aang_airBallSpinTracker: EntitySpinTracker
|
||||
}
|
||||
|
||||
val PlayerEntity.aang get() = this as IAangPlayer
|
||||
@@ -0,0 +1,199 @@
|
||||
package gg.norisk.heroes.aang.entity
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.aang.ability.TornadoAbility
|
||||
import gg.norisk.heroes.aang.ability.TornadoAbility.isTornadoMode
|
||||
import gg.norisk.heroes.aang.registry.EntityRegistry
|
||||
import gg.norisk.heroes.aang.utils.PlayerRotationTracker
|
||||
import gg.norisk.heroes.common.utils.SphereUtils
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.FallingBlockEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.mob.PathAwareEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.math.Direction
|
||||
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 kotlin.math.absoluteValue
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
|
||||
class TornadoEntity(entityType: EntityType<out PathAwareEntity>, world: World) :
|
||||
PathAwareEntity(entityType, world) {
|
||||
var currentScale: Float = 0f
|
||||
var rotationTracker: PlayerRotationTracker? = PlayerRotationTracker()
|
||||
|
||||
init {
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue = 2.5
|
||||
}
|
||||
|
||||
fun getLerpedScale(f: Float): Float {
|
||||
currentScale = MathHelper.lerp(f * 0.05f, currentScale, this.scale)
|
||||
return currentScale
|
||||
}
|
||||
|
||||
// Apply player-controlled movement
|
||||
override fun travel(pos: Vec3d) {
|
||||
if (!this.isAlive) return
|
||||
if (this.hasPassengers()) {
|
||||
val passenger = controllingPassenger ?: return super.travel(pos)
|
||||
this.prevYaw = yaw
|
||||
this.prevPitch = pitch
|
||||
|
||||
yaw = passenger.yaw
|
||||
pitch = passenger.pitch * 0.5f
|
||||
setRotation(yaw, pitch)
|
||||
|
||||
this.bodyYaw = this.yaw
|
||||
this.headYaw = this.bodyYaw
|
||||
val x = passenger.sidewaysSpeed * 0.5f
|
||||
val z = 0.6f
|
||||
|
||||
this.movementSpeed = 0.3f
|
||||
super.travel(Vec3d(x.toDouble(), pos.y, z.toDouble()))
|
||||
} else {
|
||||
super.travel(pos)
|
||||
}
|
||||
}
|
||||
|
||||
fun disappear(entity: Entity?) {
|
||||
val player = entity as? PlayerEntity?
|
||||
if (player?.isTornadoMode == true) {
|
||||
TornadoAbility.Ability.addCooldown(player)
|
||||
player.isTornadoMode = false
|
||||
player.sound(SoundEvents.ENTITY_BREEZE_IDLE_AIR, 0.4f, 0.7f)
|
||||
}
|
||||
this.discard()
|
||||
}
|
||||
|
||||
override fun removePassenger(entity: Entity?) {
|
||||
super.removePassenger(entity)
|
||||
disappear(entity)
|
||||
}
|
||||
|
||||
override fun getControllingPassenger(): LivingEntity? {
|
||||
return if (firstPassenger?.id == ownerId) {
|
||||
firstPassenger as? LivingEntity?
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartedTrackingBy(player: ServerPlayerEntity) {
|
||||
super.onStartedTrackingBy(player)
|
||||
TornadoAbility.tornadoSoundPacketS2C.send(id, player)
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
rotationTracker?.update(world.getEntityById(ownerId) as? PlayerEntity?)
|
||||
if (!world.isClient) {
|
||||
serverTick()
|
||||
}
|
||||
}
|
||||
|
||||
override fun tickMovement() {
|
||||
super.tickMovement()
|
||||
if (this.world.isClient) return
|
||||
|
||||
val radius = this.scale.toDouble()
|
||||
val windStrength = 0.1 * this.scale
|
||||
|
||||
for (entity in this.world.getOtherEntities(this, this.boundingBox.expand(radius)) {
|
||||
!it.isSpectator && it !is PlayerEntity || (it is PlayerEntity && !it.isCreative)
|
||||
}) {
|
||||
if (entity.id == ownerId) continue
|
||||
if (entity.type == EntityRegistry.TORNADO) continue
|
||||
// Berechnung des Fortschritts nach oben (Höhe)
|
||||
val maxY = this.eyeY
|
||||
|
||||
// Tornado-Zentrum auf der Höhe des Entitys
|
||||
val effectiveCentre = pos.add(0.0, entity.y - pos.y, 0.0)
|
||||
|
||||
// Entfernung vom Tornado-Zentrum
|
||||
val distFromCentre = entity.pos.distanceTo(effectiveCentre)
|
||||
|
||||
// Windstärke basierend auf Entfernung vom Zentrum
|
||||
val strength = windStrength / distFromCentre
|
||||
|
||||
// Berechnung der Einwärtsbewegung
|
||||
val inwardStrength = min((0.01 + world.random.nextDouble() * 0.5) / radius, strength)
|
||||
|
||||
// Richtung des Tornado-Sogs zur Mitte
|
||||
val inwardXDir = pos.x - entity.x
|
||||
val inwardZDir = pos.z - entity.z
|
||||
val inwardPullX = sign(inwardXDir) * inwardXDir.absoluteValue.coerceAtLeast(radius.toDouble())
|
||||
val inwardPullZ = sign(inwardZDir) * inwardZDir.absoluteValue.coerceAtLeast(radius.toDouble())
|
||||
|
||||
// Spiralförmige Bewegung nach oben und Rotation um das Zentrum
|
||||
val spiralMovement = effectiveCentre.subtract(entity.pos)
|
||||
.normalize()
|
||||
.crossProduct(Vec3d(0.0, 1.0, 0.0)) // Rotation um die Y-Achse
|
||||
.multiply(strength)
|
||||
.add(
|
||||
Vec3d(
|
||||
inwardPullX * inwardStrength,
|
||||
strength * world.random.nextDouble(),
|
||||
inwardPullZ * inwardStrength
|
||||
)
|
||||
)
|
||||
|
||||
// Setze die neue Bewegung des Entities
|
||||
entity.modifyVelocity(spiralMovement.x, spiralMovement.y, spiralMovement.z)
|
||||
|
||||
// Wenn das Entity die maximale Höhe erreicht hat, fliegt es davon
|
||||
if (entity.y >= maxY) {
|
||||
val flyAwayDirection = Vec3d(
|
||||
world.random.nextDouble() - 0.5,
|
||||
world.random.nextDouble() * 0.5,
|
||||
world.random.nextDouble() - 0.5
|
||||
).normalize().multiply(1.3) // Geschwindigkeit des Wegfliegens
|
||||
entity.modifyVelocity(flyAwayDirection)
|
||||
}
|
||||
}
|
||||
|
||||
SphereUtils.generateSphere(this.blockPos, 3 + radius.toInt(), false).filter { pos ->
|
||||
val blockState = world.getBlockState(pos)
|
||||
if (blockState.isAir) return@filter false
|
||||
if (Direction.values().any { world.getBlockState(pos.offset(it)).isAir }) {
|
||||
return@filter true
|
||||
}
|
||||
return@filter false
|
||||
}.shuffled().take(1).forEach {
|
||||
val spawnFromBlock = FallingBlockEntity.spawnFromBlock(world, it, world.getBlockState(it))
|
||||
spawnFromBlock.modifyVelocity(0.0, .2, .0)
|
||||
spawnFromBlock.dropItem = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun canWalkOnFluid(fluidState: FluidState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
private fun serverTick() {
|
||||
val owner = (world as ServerWorld).getEntityById(ownerId) as? PlayerEntity? ?: return
|
||||
val rotationTracker = this.rotationTracker
|
||||
if (rotationTracker != null) {
|
||||
this.getAttributeInstance(EntityAttributes.GENERIC_SCALE)?.baseValue =
|
||||
rotationTracker.getPercentageBetween(1f, 10f).toDouble()
|
||||
}
|
||||
}
|
||||
|
||||
var ownerId: Int
|
||||
get() = this.getSyncedData<Int>("TornadoOwnerId") ?: -1
|
||||
set(value) = this.setSyncedData("TornadoOwnerId", value)
|
||||
|
||||
var isGrowingMode: Boolean
|
||||
get() = this.getSyncedData<Boolean>("TornadoIsGrowingMode") ?: false
|
||||
set(value) = this.setSyncedData("TornadoIsGrowingMode", value)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object EmoteRegistry {
|
||||
val AIR_SCOOTER = "air_scooter_2".toEmote()
|
||||
val AIR_SCOOTER_SITTING = "air_scooter_sitting".toEmote()
|
||||
val SPIRITUAL_PROJECTION_START = "spiritual_projection_start".toEmote()
|
||||
val SPIRITUAL_PROJECTION_LOOP = "spiritual_projection_loop".toEmote()
|
||||
val LEVITATION = "levitation".toEmote()
|
||||
val AIR_BENDING = "air_bending".toEmote()
|
||||
|
||||
fun init() {}
|
||||
|
||||
fun String.toEmote(): Identifier {
|
||||
return "emotes/$this.animation.json".toId()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.entity.AirScooterEntity
|
||||
import gg.norisk.heroes.aang.entity.TornadoEntity
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
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 AIR_SCOOTER = Registry.register(
|
||||
Registries.ENTITY_TYPE,
|
||||
"air_scooter".toId(),
|
||||
EntityType.Builder.create(::AirScooterEntity, SpawnGroup.MISC)
|
||||
.requires(HeroesManager.heroesFlag)
|
||||
.dimensions(0.3125f, 0.3125f)
|
||||
.build(null)
|
||||
)
|
||||
val TORNADO = Registry.register(
|
||||
Registries.ENTITY_TYPE,
|
||||
"tornado".toId(),
|
||||
EntityType.Builder.create(::TornadoEntity, SpawnGroup.MISC)
|
||||
.requires(HeroesManager.heroesFlag)
|
||||
.dimensions(0.6f, 1f)
|
||||
.build(null)
|
||||
)
|
||||
|
||||
fun init() {
|
||||
registerEntityAttributes()
|
||||
}
|
||||
|
||||
private fun registerEntityAttributes() {
|
||||
FabricDefaultAttributeRegistry.register(AIR_SCOOTER, createGenericEntityAttributes())
|
||||
FabricDefaultAttributeRegistry.register(TORNADO, 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).requires(HeroesManager.heroesFlag)
|
||||
.build(null)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import gg.norisk.heroes.aang.client.render.entity.AirScooterEntityRenderer
|
||||
import gg.norisk.heroes.aang.client.render.entity.TornadoEntityRenderer
|
||||
import gg.norisk.heroes.aang.client.render.entity.model.AirScooterEntityModel
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry
|
||||
import net.minecraft.client.render.entity.model.EntityModelLayer
|
||||
|
||||
|
||||
object EntityRendererRegistry {
|
||||
val AIR_SCOOTER_LAYER: EntityModelLayer = EntityModelLayer("air_scooter".toId(), "main")
|
||||
|
||||
fun init() {
|
||||
EntityRendererRegistry.register(EntityRegistry.AIR_SCOOTER, ::AirScooterEntityRenderer)
|
||||
EntityRendererRegistry.register(EntityRegistry.TORNADO, ::TornadoEntityRenderer)
|
||||
EntityModelLayerRegistry.registerModelLayer(
|
||||
AIR_SCOOTER_LAYER,
|
||||
AirScooterEntityModel.Companion::getTexturedModelData
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
|
||||
object ParticleRegistry {
|
||||
val AIR_SCOOTER_DUST = register("air_scooter_dust")
|
||||
val BENDING_AIR = register("bending_air")
|
||||
|
||||
fun init() {
|
||||
}
|
||||
|
||||
private fun register(
|
||||
name: String
|
||||
): ParticleEffect {
|
||||
return Registry.register(Registries.PARTICLE_TYPE, name.toId(), FabricParticleTypes.simple())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.client.particle.AirScooterDustParticle
|
||||
import gg.norisk.heroes.aang.client.particle.BendingAirParticle
|
||||
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import net.minecraft.particle.ParticleType
|
||||
|
||||
object ParticleRendererRegistry {
|
||||
fun init() {
|
||||
ParticleFactoryRegistry.getInstance().register(ParticleRegistry.AIR_SCOOTER_DUST as ParticleType<ParticleEffect>, AirScooterDustParticle::Factory)
|
||||
ParticleFactoryRegistry.getInstance().register(ParticleRegistry.BENDING_AIR as ParticleType<ParticleEffect>, BendingAirParticle::Factory)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package gg.norisk.heroes.aang.registry
|
||||
|
||||
import gg.norisk.heroes.aang.AangManager.toId
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.sound.SoundEvent
|
||||
|
||||
object SoundRegistry {
|
||||
var FLYING = "flying".register()
|
||||
|
||||
fun init() {
|
||||
}
|
||||
|
||||
private fun String.register() = Registry.register(Registries.SOUND_EVENT, this.toId(), SoundEvent.of(this.toId()))
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package gg.norisk.heroes.aang.utils
|
||||
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class CircleDetector3D {
|
||||
|
||||
// Liste zur Speicherung der Mausbewegungen im 3D-Raum
|
||||
private val mouseMovements = mutableSetOf<Vec3d>()
|
||||
|
||||
// Methode, um Mausbewegungen aufzuzeichnen
|
||||
fun addMouseMovement(x: Double, y: Double, z: Double): Boolean {
|
||||
return mouseMovements.add(Vec3d(x, y, z))
|
||||
}
|
||||
|
||||
// Methode zur Berechnung des Kreisähnlichkeitsprozentsatzes im 3D-Raum
|
||||
fun calculateCircleAccuracy(): Double {
|
||||
if (mouseMovements.size < 3) return 0.0 // Nicht genug Punkte, um einen Kreis zu erkennen
|
||||
|
||||
// Berechnung des Mittelpunkts (Schwerpunkt der Bewegung)
|
||||
val center = calculateCenter(mouseMovements)
|
||||
|
||||
// Berechnung des durchschnittlichen Radius
|
||||
val averageRadius = calculateAverageRadius(mouseMovements, center)
|
||||
|
||||
// Berechnung des Fehlerwerts für jeden Punkt
|
||||
var totalError = 0.0
|
||||
for (point in mouseMovements) {
|
||||
val distanceToCenter = calculateDistance(center, point)
|
||||
totalError += abs(distanceToCenter - averageRadius)
|
||||
}
|
||||
|
||||
// Normierung des Fehlers und Umwandlung in Prozent (100% bedeutet perfekter Kreis)
|
||||
val maxError = averageRadius * mouseMovements.size
|
||||
val accuracy = 100.0f - (totalError / maxError * 100.0f)
|
||||
|
||||
return accuracy.coerceIn(0.0, 100.0) // Beschränkung auf 0-100%
|
||||
}
|
||||
|
||||
// Hilfsmethode zur Berechnung des Mittelpunkts im 3D-Raum
|
||||
private fun calculateCenter(points: Collection<Vec3d>): Vec3d {
|
||||
var sumX = 0.0
|
||||
var sumY = 0.0
|
||||
var sumZ = 0.0
|
||||
|
||||
for (point in points) {
|
||||
sumX += point.x
|
||||
sumY += point.y
|
||||
sumZ += point.z
|
||||
}
|
||||
|
||||
val centerX = sumX / points.size
|
||||
val centerY = sumY / points.size
|
||||
val centerZ = sumZ / points.size
|
||||
|
||||
return Vec3d(centerX, centerY, centerZ)
|
||||
}
|
||||
|
||||
// Hilfsmethode zur Berechnung des durchschnittlichen Radius
|
||||
private fun calculateAverageRadius(points: Collection<Vec3d>, center: Vec3d): Double {
|
||||
var totalRadius = 0.0
|
||||
for (point in points) {
|
||||
totalRadius += calculateDistance(center, point)
|
||||
}
|
||||
return totalRadius / points.size
|
||||
}
|
||||
|
||||
// Hilfsmethode zur Berechnung der Distanz zwischen zwei Punkten im 3D-Raum
|
||||
private fun calculateDistance(p1: Vec3d, p2: Vec3d): Double {
|
||||
val dx = p1.x - p2.x
|
||||
val dy = p1.y - p2.y
|
||||
val dz = p1.z - p2.z
|
||||
return sqrt(dx * dx + dy * dy + dz * dz)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package gg.norisk.heroes.aang.utils
|
||||
|
||||
import net.minecraft.entity.Entity
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class EntitySpinTracker {
|
||||
private val yawHistory: Deque<Float> = ArrayDeque()
|
||||
private val maxHistorySize = 60 // Anzahl der Ticks, die wir überwachen (z. B. 1 Sekunde bei 20 Ticks pro Sekunde)
|
||||
private val spinThreshold = 360.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 getSpinProgress(): Float {
|
||||
if (yawHistory.size < 2) {
|
||||
return 0.0f // 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
|
||||
}
|
||||
|
||||
// Berechne den Fortschritt als Prozentsatz
|
||||
return (totalChange / spinThreshold).coerceAtMost(1.0f) * 100.0f
|
||||
}
|
||||
|
||||
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,61 @@
|
||||
package gg.norisk.heroes.aang.utils
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
|
||||
class PlayerRotationTracker {
|
||||
var lastYaw: Float? = null // Zu Beginn noch null, um den ersten Tick zu vermeiden
|
||||
var lastPitch: Float? = null
|
||||
var movementScale = 0f
|
||||
var maxMovementScale = 100f // Maximale Skala, auf die hochgezählt werden kann
|
||||
var movementDecayRate = 0.1f // Geschwindigkeit des Decays, wenn der Spieler sich wenig bewegt
|
||||
var movementIncreaseRate = 0.005f // Geschwindigkeit, mit der die Skala ansteigt bei Bewegung
|
||||
var movementThreshold = 5f // Minimale Änderung in Yaw oder Pitch, um als "Bewegung" zu gelten
|
||||
var onlyDecay = false
|
||||
|
||||
// Diese Methode sollte pro Frame oder Tick aufgerufen werden
|
||||
fun update(player: PlayerEntity?) {
|
||||
if (player == null) return
|
||||
// Aktuelle Yaw- und Pitch-Werte des Spielers
|
||||
val currentYaw = player.yaw
|
||||
val currentPitch = player.pitch
|
||||
|
||||
// Wenn dies das erste Mal ist, dass die Methode aufgerufen wird, setze die initialen Werte
|
||||
if (lastYaw == null || lastPitch == null) {
|
||||
lastYaw = currentYaw
|
||||
lastPitch = currentPitch
|
||||
return // Beim ersten Durchlauf kein Update der Skala
|
||||
}
|
||||
|
||||
// Berechne die Änderungen in Yaw und Pitch
|
||||
val deltaYaw = Math.abs(currentYaw - lastYaw!!)
|
||||
val deltaPitch = Math.abs(currentPitch - lastPitch!!)
|
||||
|
||||
// Überprüfe, ob die Änderung größer als der Schwellwert ist
|
||||
if ((deltaYaw > movementThreshold || deltaPitch > movementThreshold) && !onlyDecay) {
|
||||
// Erhöhe die Skala basierend auf der Bewegungsmenge
|
||||
movementScale += (deltaYaw + deltaPitch) * movementIncreaseRate
|
||||
if (movementScale > maxMovementScale) {
|
||||
movementScale = maxMovementScale
|
||||
}
|
||||
} else {
|
||||
// Verringere die Skala langsam, wenn keine signifikante Bewegung stattfindet
|
||||
movementScale -= movementDecayRate
|
||||
if (movementScale < 0) {
|
||||
movementScale = 0f
|
||||
}
|
||||
}
|
||||
|
||||
// Speichere die aktuellen Werte für den nächsten Tick
|
||||
lastYaw = currentYaw
|
||||
lastPitch = currentPitch
|
||||
}
|
||||
|
||||
// Neue Methode: Prozentsatz zwischen zwei Werten basierend auf der movementScale berechnen
|
||||
fun getPercentageBetween(minValue: Float, maxValue: Float): Float {
|
||||
// Normalisiere die movementScale zwischen 0 und 1
|
||||
val normalizedScale = movementScale / maxMovementScale
|
||||
|
||||
// Berechne den interpolierten Wert zwischen minValue und maxValue
|
||||
return minValue + (maxValue - minValue) * normalizedScale
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
accessWidener v2 named
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "gg.norisk.heroes.aang.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"mixins": [
|
||||
"EntityMixin",
|
||||
"LivingEntityMixin",
|
||||
"PlayerEntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"accessor.CameraAccessor",
|
||||
"client.AbstractClientPlayerEntityMixin",
|
||||
"client.CameraMixin",
|
||||
"client.ClientPlayNetworkHandlerMixin",
|
||||
"client.GameOptionsMixin",
|
||||
"client.GameRendererMixin",
|
||||
"client.HeldItemRendererMixin",
|
||||
"client.LivingEntityRendererMixin",
|
||||
"client.PlayerEntityRendererMixin"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 990 B |
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "emote-lib:bending_air_0",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_alpha",
|
||||
"texture": "aang:graffiti"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_steady": {
|
||||
"spawn_rate": 16,
|
||||
"max_particles": 100
|
||||
},
|
||||
"minecraft:emitter_lifetime_looping": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_sphere": {
|
||||
"radius": 2,
|
||||
"surface_only": true,
|
||||
"direction": "inwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 0.4
|
||||
},
|
||||
"minecraft:particle_initial_speed": 16.5,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_acceleration": [0, 1, 0],
|
||||
"linear_drag_coefficient": 4
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": [0.5, 0.5],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 16,
|
||||
"texture_height": 128,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [16, 16],
|
||||
"step_UV": [0, 16],
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 8,
|
||||
"stretch_to_lifetime": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_motion_collision": {
|
||||
"collision_drag": 0.4,
|
||||
"collision_radius": 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
|
||||
{
|
||||
"format_version": "1.10.0",
|
||||
"particle_effect": {
|
||||
"description": {
|
||||
"identifier": "emote-lib:bending_air_1",
|
||||
"basic_render_parameters": {
|
||||
"material": "particles_alpha",
|
||||
"texture": "aang:graffiti"
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"minecraft:emitter_rate_instant": {
|
||||
"num_particles": 3
|
||||
},
|
||||
"minecraft:emitter_lifetime_once": {
|
||||
"active_time": 1
|
||||
},
|
||||
"minecraft:emitter_shape_sphere": {
|
||||
"radius": 0.5,
|
||||
"surface_only": true,
|
||||
"direction": "inwards"
|
||||
},
|
||||
"minecraft:particle_lifetime_expression": {
|
||||
"max_lifetime": 1
|
||||
},
|
||||
"minecraft:particle_initial_speed": 1,
|
||||
"minecraft:particle_motion_dynamic": {
|
||||
"linear_drag_coefficient": 4
|
||||
},
|
||||
"minecraft:particle_appearance_billboard": {
|
||||
"size": [0.5, 0.5],
|
||||
"facing_camera_mode": "rotate_xyz",
|
||||
"uv": {
|
||||
"texture_width": 16,
|
||||
"texture_height": 128,
|
||||
"flipbook": {
|
||||
"base_UV": [0, 0],
|
||||
"size_UV": [16, 16],
|
||||
"step_UV": [0, 16],
|
||||
"frames_per_second": 12,
|
||||
"max_frame": 8,
|
||||
"stretch_to_lifetime": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"minecraft:particle_motion_collision": {
|
||||
"collision_drag": 0.4,
|
||||
"collision_radius": 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"air_bending": {
|
||||
"loop": "hold_on_last_frame",
|
||||
"animation_length": 0.72,
|
||||
"bones": {
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [-69.48874, 39.09545, 11.28589],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [68.5343, -42.00279, -163.26947],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"air_scooter": {
|
||||
"animation_length": 0.84,
|
||||
"bones": {
|
||||
"bipedRig": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.8": {
|
||||
"vector": [0, 360, 0],
|
||||
"easing": "easeInExpo"
|
||||
},
|
||||
"0.84": {
|
||||
"vector": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [-78.99034, 24.59477, 4.62934]
|
||||
},
|
||||
"0.28": {
|
||||
"vector": [73.84641, 51.3798, 167.24859]
|
||||
},
|
||||
"0.56": {
|
||||
"vector": [31.34641, 51.3798, 167.24859]
|
||||
},
|
||||
"0.8": {
|
||||
"vector": [-81.56011, -42.11662, 4.58533]
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.56": {
|
||||
"vector": [0, 3, 0]
|
||||
},
|
||||
"0.8": {
|
||||
"vector": [0, -1, -3]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [86.44785, -33.16347, -168.10969]
|
||||
},
|
||||
"0.28": {
|
||||
"vector": [-82.49399, -66.60691, 3.05019]
|
||||
},
|
||||
"0.56": {
|
||||
"vector": [-42.49399, -66.60691, 3.05019]
|
||||
},
|
||||
"0.8": {
|
||||
"vector": [-72.50228, 13.17432, 52.2119]
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.56": {
|
||||
"vector": [0, 0, -2]
|
||||
},
|
||||
"0.8": {
|
||||
"vector": [-1, 0, -2]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"particle_effects": {
|
||||
"0.0": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.08": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.12": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.16": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.2": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.28": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.32": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.44": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.48": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.56": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.6": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.68": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
},
|
||||
"0.72": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocator"
|
||||
},
|
||||
"0.8": {
|
||||
"effect": "bending_air_1",
|
||||
"locator": "airlocatorrightarm"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"format_version": "1.12.0",
|
||||
"minecraft:geometry": [
|
||||
{
|
||||
"description": {
|
||||
"identifier": "geometry.unknown",
|
||||
"texture_width": 16,
|
||||
"texture_height": 16,
|
||||
"visible_bounds_width": 2,
|
||||
"visible_bounds_height": 3.5,
|
||||
"visible_bounds_offset": [0, 1.25, 0]
|
||||
},
|
||||
"bones": [
|
||||
{
|
||||
"name": "bipedRig",
|
||||
"pivot": [0, 0, 0]
|
||||
},
|
||||
{
|
||||
"name": "bipedHead",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 24, -4], "size": [8, 8, 8], "uv": [0, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorHead",
|
||||
"parent": "bipedHead",
|
||||
"pivot": [0, 24, 0]
|
||||
},
|
||||
{
|
||||
"name": "bipedBody",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [0, 24, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 12, -2], "size": [8, 12, 4], "uv": [0, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorBody",
|
||||
"parent": "bipedBody",
|
||||
"pivot": [0, 24, 0]
|
||||
},
|
||||
{
|
||||
"name": "bipedRightArm",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [-5, 22, 0],
|
||||
"cubes": [
|
||||
{"origin": [-8, 12, -2], "size": [4, 12, 4], "uv": [16, 32]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorRightArm",
|
||||
"parent": "bipedRightArm",
|
||||
"pivot": [-4, 22, 0]
|
||||
},
|
||||
{
|
||||
"name": "airlocatorrightarm",
|
||||
"parent": "armorRightArm",
|
||||
"pivot": [-6, 12, 0],
|
||||
"locators": {
|
||||
"airlocatorrightarm": [-6, 12, 0]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bipedLeftArm",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [5, 22, 0],
|
||||
"cubes": [
|
||||
{"origin": [4, 12, -2], "size": [4, 12, 4], "uv": [32, 0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorLeftArm",
|
||||
"parent": "bipedLeftArm",
|
||||
"pivot": [4, 22, 0]
|
||||
},
|
||||
{
|
||||
"name": "airlocator",
|
||||
"parent": "armorLeftArm",
|
||||
"pivot": [6, 12, 0],
|
||||
"locators": {
|
||||
"airlocator": [6, 12, 0]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bipedLeftLeg",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [2, 12, 0],
|
||||
"cubes": [
|
||||
{"origin": [0, 0, -2], "size": [4, 12, 4], "uv": [0, 32]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorLeftLeg",
|
||||
"parent": "bipedLeftLeg",
|
||||
"pivot": [2, 12, 0]
|
||||
},
|
||||
{
|
||||
"name": "armorLeftBoot",
|
||||
"parent": "bipedLeftLeg",
|
||||
"pivot": [2, 12, 0]
|
||||
},
|
||||
{
|
||||
"name": "bipedRightLeg",
|
||||
"parent": "bipedRig",
|
||||
"pivot": [-2, 12, 0],
|
||||
"cubes": [
|
||||
{"origin": [-4, 0, -2], "size": [4, 12, 4], "uv": [24, 16]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "armorRightLeg",
|
||||
"parent": "bipedRightLeg",
|
||||
"pivot": [-2, 12, 0]
|
||||
},
|
||||
{
|
||||
"name": "armorRightBoot",
|
||||
"parent": "bipedRightLeg",
|
||||
"pivot": [-2, 12, 0]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"air_scooter_sitting": {
|
||||
"lockVanillaBones": {
|
||||
"bipedRig": false,
|
||||
"bipedHead": false,
|
||||
"bipedBody": false,
|
||||
"bipedLeftArm": true,
|
||||
"bipedRightArm": true,
|
||||
"bipedLeftLeg": false,
|
||||
"bipedRightLeg": false
|
||||
},
|
||||
"loop": "hold_on_last_frame",
|
||||
"bones": {
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"vector": [-90, -40, 0]
|
||||
},
|
||||
"position": {
|
||||
"vector": [-1, 0, 1]
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"vector": [-90, 40, 0]
|
||||
},
|
||||
"position": {
|
||||
"vector": [1, 0, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"levitation": {
|
||||
"loop": "hold_on_last_frame",
|
||||
"lockVanillaBones": {
|
||||
"bipedRig": false,
|
||||
"bipedHead": false,
|
||||
"bipedBody": false,
|
||||
"bipedLeftArm": false,
|
||||
"bipedRightArm": false,
|
||||
"bipedLeftLeg": true,
|
||||
"bipedRightLeg": true
|
||||
},
|
||||
"animation_length": 0.6,
|
||||
"bones": {
|
||||
"bipedRig": {
|
||||
"position": {
|
||||
"vector": [0, "Math.cos(query.anim_time * 100) * 0.5 -0.5", 0]
|
||||
}
|
||||
},
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [0, 0, 65],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [-1, 0, 0],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [0, 0, -65],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [1, 0, 0],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftLeg": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": ["Math.cos(query.anim_time * 150 +25) * -15", 0, 42.5],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [6, 0, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedRightLeg": {
|
||||
"rotation": {
|
||||
"vector": ["Math.cos(query.anim_time * 150 +25) * 15", 0, 0]
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.6": {
|
||||
"vector": [-2, -1, 0],
|
||||
"easing": "easeOutQuint"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"emote": {
|
||||
"loop": true,
|
||||
"lockVanillaBones": {
|
||||
"bipedRig": false,
|
||||
"bipedHead": false,
|
||||
"bipedBody": false,
|
||||
"bipedLeftArm": false,
|
||||
"bipedRightArm": false,
|
||||
"bipedLeftLeg": true,
|
||||
"bipedRightLeg": true
|
||||
},
|
||||
"bones": {
|
||||
"bipedRig": {
|
||||
"position": {
|
||||
"vector": [0, "Math.cos(query.anim_time * 100) * 1 -0.5", 0]
|
||||
}
|
||||
},
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"vector": [0, 0, 80],
|
||||
"easing": "easeOutExpo"
|
||||
},
|
||||
"position": {
|
||||
"vector": [-2, 0, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"vector": [0, 0, -80],
|
||||
"easing": "easeOutExpo"
|
||||
},
|
||||
"position": {
|
||||
"vector": [2, 0, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"bipedLeftLeg": {
|
||||
"rotation": {
|
||||
"vector": [-71.88679, 33.64409, 10.27206],
|
||||
"easing": "easeOutExpo"
|
||||
},
|
||||
"position": {
|
||||
"vector": [5, -2, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"bipedRightLeg": {
|
||||
"rotation": {
|
||||
"vector": [-89.38679, -33.64409, -10.27206],
|
||||
"easing": "easeOutExpo"
|
||||
},
|
||||
"position": {
|
||||
"vector": [-6, -2, 1],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"emote": {
|
||||
"animation_length": 2.12,
|
||||
"lockVanillaBones": {
|
||||
"bipedRig": false,
|
||||
"bipedHead": false,
|
||||
"bipedBody": false,
|
||||
"bipedLeftArm": false,
|
||||
"bipedRightArm": false,
|
||||
"bipedLeftLeg": true,
|
||||
"bipedRightLeg": true
|
||||
},
|
||||
"bones": {
|
||||
"bipedRig": {
|
||||
"position": {
|
||||
"vector": [0, "Math.cos(query.anim_time * 100) * 1 -0.5", 0]
|
||||
}
|
||||
},
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [0, 0, 80],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [-2, 0, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [0, 0, -80],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [2, 0, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftLeg": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [-71.88679, 33.64409, 10.27206],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [5, -2, 0],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedRightLeg": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [-89.38679, -33.64409, -10.27206],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.72": {
|
||||
"vector": [-6, -2, 1],
|
||||
"easing": "easeOutExpo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"textures": [
|
||||
"aang:big_smoke_0",
|
||||
"aang:big_smoke_1",
|
||||
"aang:big_smoke_2",
|
||||
"aang:big_smoke_3",
|
||||
"aang:big_smoke_4",
|
||||
"aang:big_smoke_5",
|
||||
"aang:big_smoke_6",
|
||||
"aang:big_smoke_7",
|
||||
"aang:big_smoke_8",
|
||||
"aang:big_smoke_9",
|
||||
"aang:big_smoke_10",
|
||||
"aang:big_smoke_11"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"textures": [
|
||||
"aang:big_smoke_0",
|
||||
"aang:big_smoke_1",
|
||||
"aang:big_smoke_2",
|
||||
"aang:big_smoke_3",
|
||||
"aang:big_smoke_4",
|
||||
"aang:big_smoke_5",
|
||||
"aang:big_smoke_6",
|
||||
"aang:big_smoke_7",
|
||||
"aang:big_smoke_8",
|
||||
"aang:big_smoke_9",
|
||||
"aang:big_smoke_10",
|
||||
"aang:big_smoke_11"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": [
|
||||
"aang:graffiti"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"flying": {
|
||||
"sounds": [
|
||||
"aang:flying"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 343 B |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"texture": {
|
||||
"blur": true
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 209 B |
|
After Width: | Height: | Size: 179 B |
|
After Width: | Height: | Size: 136 B |
|
After Width: | Height: | Size: 224 B |
|
After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 227 B |
|
After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 217 B |
|
After Width: | Height: | Size: 216 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 203 B |
|
After Width: | Height: | Size: 530 B |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 100 KiB |
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "Aang",
|
||||
"id": "aang",
|
||||
"version": "${version}",
|
||||
"description": "Aang",
|
||||
"authors": [
|
||||
"NoRiskk"
|
||||
],
|
||||
"icon": "assets/aang/icon.png",
|
||||
"license": "ARR",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.aang.AangManager"
|
||||
}
|
||||
],
|
||||
"client": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.aang.AangManager"
|
||||
}
|
||||
],
|
||||
"server": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.aang.AangManager"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"aang.mixins.json"
|
||||
],
|
||||
"accessWidener": "aang.accesswidener",
|
||||
"depends": {
|
||||
},
|
||||
"custom": {
|
||||
"modmenu": {
|
||||
"badges": [
|
||||
"library"
|
||||
],
|
||||
"parent": {
|
||||
"id": "hero-api"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||