fixed time dilations

king progress
beams
This commit is contained in:
craisinlord committed 2026-08-30 21:34:23 -05:00
1 parent af0b052b5c
commit 069d90d6e2
135 files changed
+3383 -483

No files matched your search

@@ -91,6 +91,7 @@ public final class AntarchySettings {
private static double bigBerthaNoneModeDamageBonusPercent = 15.0D;
private static double bigBerthaNightmareDamageBonusPercent = 30.0D;
private static double potentNyxiteInvertedDurationSeconds = 60.0D;
private static boolean dimensionalTearsSpawnNaturally = false;
private static int dimensionalTearLifetimeTicks = 24000;
private static int dimensionalTearInvertedDurationTicks = 2400;
private static int dimensionalTearEmergenceMinIntervalTicks = 2400;
@@ -231,6 +232,20 @@ public final class AntarchySettings {
private static int royalGuardianShieldDurability = 1008;
private static double royalBoltDamage = 6.0D;
private static int royalEggHatchChance = 3;
private static double princeHealth = 120.0D;
private static double princeAttackDamage = 14.0D;
private static double princeMovementSpeed = 0.28D;
private static double princeFlyingSpeed = 0.9D;
private static double princeArmor = 10.0D;
private static double princeKnockbackResistance = 0.6D;
private static double princeFollowRange = 48.0D;
private static double princessHealth = 110.0D;
private static double princessAttackDamage = 10.0D;
private static double princessMovementSpeed = 0.28D;
private static double princessFlyingSpeed = 0.95D;
private static double princessArmor = 8.0D;
private static double princessKnockbackResistance = 0.5D;
private static double princessFollowRange = 48.0D;
private static double kingHealth = 6000.0D;
private static double queenHealth = 6000.0D;
private static double kingAttackDamage = 50.0D;
@@ -530,6 +545,7 @@ public final class AntarchySettings {
private static double kingBeamTracking = 0.1D;
private static double kingBeamTerrainRadius = 5.0D;
private static int kingBeamTerrainCap = 192;
private static int royalDecreeCooldownTicks = 1200;
private static double jerryInfantHealth = 20.0D;
private static double jerryInfantAttackDamage = 2.0D;
private static double jerryMatureHealth = 40.0D;
@@ -791,6 +807,14 @@ public final class AntarchySettings {
return potentNyxiteInvertedDurationSeconds;
}
public static boolean dimensionalTearsSpawnNaturally() {
return dimensionalTearsSpawnNaturally;
}
public static void setDimensionalTearsSpawnNaturally(boolean value) {
dimensionalTearsSpawnNaturally = value;
}
public static int dimensionalTearLifetimeTicks() {
return dimensionalTearLifetimeTicks;
}
@@ -1948,6 +1972,36 @@ public final class AntarchySettings {
public static int royalEggHatchChance() { return royalEggHatchChance; }
public static void setRoyalEggHatchChance(int v) { royalEggHatchChance = v; }
public static double princeHealth() { return princeHealth; }
public static void setPrinceHealth(double v) { princeHealth = v; }
public static double princeAttackDamage() { return princeAttackDamage; }
public static void setPrinceAttackDamage(double v) { princeAttackDamage = v; }
public static double princeMovementSpeed() { return princeMovementSpeed; }
public static void setPrinceMovementSpeed(double v) { princeMovementSpeed = v; }
public static double princeFlyingSpeed() { return princeFlyingSpeed; }
public static void setPrinceFlyingSpeed(double v) { princeFlyingSpeed = v; }
public static double princeArmor() { return princeArmor; }
public static void setPrinceArmor(double v) { princeArmor = v; }
public static double princeKnockbackResistance() { return princeKnockbackResistance; }
public static void setPrinceKnockbackResistance(double v) { princeKnockbackResistance = v; }
public static double princeFollowRange() { return princeFollowRange; }
public static void setPrinceFollowRange(double v) { princeFollowRange = v; }
public static double princessHealth() { return princessHealth; }
public static void setPrincessHealth(double v) { princessHealth = v; }
public static double princessAttackDamage() { return princessAttackDamage; }
public static void setPrincessAttackDamage(double v) { princessAttackDamage = v; }
public static double princessMovementSpeed() { return princessMovementSpeed; }
public static void setPrincessMovementSpeed(double v) { princessMovementSpeed = v; }
public static double princessFlyingSpeed() { return princessFlyingSpeed; }
public static void setPrincessFlyingSpeed(double v) { princessFlyingSpeed = v; }
public static double princessArmor() { return princessArmor; }
public static void setPrincessArmor(double v) { princessArmor = v; }
public static double princessKnockbackResistance() { return princessKnockbackResistance; }
public static void setPrincessKnockbackResistance(double v) { princessKnockbackResistance = v; }
public static double princessFollowRange() { return princessFollowRange; }
public static void setPrincessFollowRange(double v) { princessFollowRange = v; }
public static double kingHealth() { return kingHealth; }
public static void setKingHealth(double v) { kingHealth = v; }
@@ -2800,6 +2854,8 @@ public final class AntarchySettings {
public static void setKingBeamTerrainRadius(double v) { kingBeamTerrainRadius = v; }
public static int kingBeamTerrainCap() { return kingBeamTerrainCap; }
public static void setKingBeamTerrainCap(int v) { kingBeamTerrainCap = v; }
public static int royalDecreeCooldownTicks() { return royalDecreeCooldownTicks; }
public static void setRoyalDecreeCooldownTicks(int v) { royalDecreeCooldownTicks = v; }
public static double jerryInfantHealth() { return jerryInfantHealth; }
public static void setJerryInfantHealth(double v) { jerryInfantHealth = v; }
public static double jerryInfantAttackDamage() { return jerryInfantAttackDamage; }
@@ -99,6 +99,8 @@ public final class AntarchyObjects {
@SuppressWarnings("unchecked")
public static Supplier<EntityType<TimeDilationFieldEntity>> TIME_DILATION_FIELD = (Supplier<EntityType<TimeDilationFieldEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> ROYAL_BLACK_HOLE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<Block> VORTEX_LENS = (Supplier<Block>) UNBOUND;
@SuppressWarnings("unchecked")
public static Supplier<BlockEntityType<VortexLensBlockEntity>> VORTEX_LENS_BLOCK_ENTITY = (Supplier<BlockEntityType<VortexLensBlockEntity>>) UNBOUND;
@@ -677,6 +679,10 @@ public final class AntarchyObjects {
TIME_DILATION_FIELD = supplier;
}
public static void setRoyalBlackHole(Supplier<EntityType<com.craisinlord.antarchy.content.entity.royal.RoyalBlackHoleEntity>> supplier) {
ROYAL_BLACK_HOLE = supplier;
}
public static void setVortexLens(Supplier<Block> supplier) {
VORTEX_LENS = supplier;
}
@@ -194,6 +194,20 @@ public final class AntarchySoundEvents {
public static Supplier<SoundEvent> QUEEN_BEAM_START = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> QUEEN_BEAM_END = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_STOMP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_ROAR = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_WING_FLAP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> KING_FLY_LOOP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCE_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCE_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCE_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCE_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCE_STEP = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_IDLE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_BITE = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_SHOOT = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_FLY = (Supplier<SoundEvent>) UNBOUND;
public static Supplier<SoundEvent> PRINCESS_STEP = (Supplier<SoundEvent>) UNBOUND;
private AntarchySoundEvents() {
}
@@ -383,7 +397,21 @@ public final class AntarchySoundEvents {
Supplier<SoundEvent> queenBeamShoot,
Supplier<SoundEvent> queenBeamStart,
Supplier<SoundEvent> queenBeamLoop,
Supplier<SoundEvent> queenBeamEnd
Supplier<SoundEvent> queenBeamEnd,
Supplier<SoundEvent> kingStomp,
Supplier<SoundEvent> kingRoar,
Supplier<SoundEvent> kingWingFlap,
Supplier<SoundEvent> kingFlyLoop,
Supplier<SoundEvent> princeIdle,
Supplier<SoundEvent> princeBite,
Supplier<SoundEvent> princeShoot,
Supplier<SoundEvent> princeFly,
Supplier<SoundEvent> princeStep,
Supplier<SoundEvent> princessIdle,
Supplier<SoundEvent> princessBite,
Supplier<SoundEvent> princessShoot,
Supplier<SoundEvent> princessFly,
Supplier<SoundEvent> princessStep
) {
SQUIDZOOKA_FIRE = squidzookaFire;
SHRINK_RAY_CHARGE = shrinkRayCharge;
@@ -570,5 +598,19 @@ public final class AntarchySoundEvents {
QUEEN_BEAM_START = queenBeamStart;
QUEEN_BEAM_LOOP = queenBeamLoop;
QUEEN_BEAM_END = queenBeamEnd;
KING_STOMP = kingStomp;
KING_ROAR = kingRoar;
KING_WING_FLAP = kingWingFlap;
KING_FLY_LOOP = kingFlyLoop;
PRINCE_IDLE = princeIdle;
PRINCE_BITE = princeBite;
PRINCE_SHOOT = princeShoot;
PRINCE_FLY = princeFly;
PRINCE_STEP = princeStep;
PRINCESS_IDLE = princessIdle;
PRINCESS_BITE = princessBite;
PRINCESS_SHOOT = princessShoot;
PRINCESS_FLY = princessFly;
PRINCESS_STEP = princessStep;
}
}
@@ -83,6 +83,7 @@ public final class AntarchyTags {
public static final TagKey<EntityType<?>> LUCID_ANCHOR_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("lucid_anchor_immune"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_COMMON_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_common_spawns"));
public static final TagKey<EntityType<?>> DIMENSIONAL_TEAR_RARE_SPAWNS = TagKey.create(Registries.ENTITY_TYPE, id("dimensional_tear_rare_spawns"));
public static final TagKey<EntityType<?>> TIME_DILATION_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("time_dilation_immune"));
private Entities() {
}
@@ -114,6 +115,7 @@ public final class AntarchyTags {
public static final TagKey<Item> BUTTERFLY_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("butterfly_breeding_foods"));
public static final TagKey<Item> REVERIE_DUPLICATION_ITEMS = TagKey.create(Registries.ITEM, id("reverie_duplication_items"));
public static final TagKey<Item> HERCULES_BEETLE_FOOD = TagKey.create(Registries.ITEM, id("hercules_beetle_food"));
public static final TagKey<Item> ROYAL_MOUNT_FOOD = TagKey.create(Registries.ITEM, id("royal_mount_food"));
public static final TagKey<Item> ROLLY_POLLY_FOOD = TagKey.create(Registries.ITEM, id("rolly_polly_food"));
public static final TagKey<Item> OURANWOOD_DEER_FOOD = TagKey.create(Registries.ITEM, id("ouranwood_deer_food"));
public static final TagKey<Item> GLIMMER_AUGMENT_APPLE_COW = TagKey.create(Registries.ITEM, id("glimmer_augment_apple_cow"));
@@ -15,6 +15,9 @@ import net.minecraft.world.level.block.state.BlockState;
public class AntigravelBlock extends Block {
public static final MapCodec<AntigravelBlock> CODEC = simpleCodec(AntigravelBlock::new);
private static final int SETTLE_DELAY = 6;
private static final int SETTLE_ATTEMPT_DENOMINATOR = 3;
public AntigravelBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@@ -28,13 +31,13 @@ public class AntigravelBlock extends Block {
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
super.onPlace(state, level, pos, oldState, movedByPiston);
if (!level.isClientSide) {
level.scheduleTick(pos, this, 2);
level.scheduleTick(pos, this, SETTLE_DELAY);
}
}
@Override
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
level.scheduleTick(pos, this, 2);
level.scheduleTick(pos, this, SETTLE_DELAY);
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
}
@@ -46,8 +49,13 @@ public class AntigravelBlock extends Block {
BlockPos abovePos = pos.above();
BlockState above = level.getBlockState(abovePos);
boolean passable = above.isAir() || above.getCollisionShape(level, abovePos).isEmpty();
if (passable) {
UpwardFallingBlockEntity.fallUp(level, pos, state, false);
if (!passable) {
return;
}
if (random.nextInt(SETTLE_ATTEMPT_DENOMINATOR) != 0) {
level.scheduleTick(pos, this, SETTLE_DELAY + random.nextInt(SETTLE_DELAY));
return;
}
UpwardFallingBlockEntity.fallUp(level, pos, state, false);
}
}
@@ -19,16 +19,20 @@ import net.minecraft.world.level.block.state.BlockState;
public class AntNestBlockEntity extends BlockEntity {
private static final String STORED_ANTS = "StoredAnts";
private static final String NBT_INITIALIZED = "Initialized";
private static final String NBT_REBROOD_COOLDOWN = "RebroodCooldown";
private static final int RELEASE_COOLDOWN_MIN = 80;
private static final int RELEASE_COOLDOWN_RANGE = 100;
private static final int ENTER_NEST_COOLDOWN = 200;
private static final int INITIAL_ANT_COUNT_MIN = 2;
private static final int INITIAL_ANT_COUNT_RANGE = 4;
private static final int REBROOD_COOLDOWN_TICKS = 20 * 60 * 5;
private static final double PLAYER_ACTIVATION_RANGE = 16.0D;
private static final double SPAWN_OFFSET_XZ = 0.5D;
private static final double SPAWN_OFFSET_Y = 0.05D;
private final List<StoredAnt> storedAnts = new ArrayList<>();
private int releaseCooldown = RELEASE_COOLDOWN_MIN;
private int rebroodCooldown = REBROOD_COOLDOWN_TICKS;
private boolean initialized = false;
public AntNestBlockEntity(BlockPos pos, BlockState blockState) {
@@ -36,8 +40,19 @@ public class AntNestBlockEntity extends BlockEntity {
}
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, AntNestBlockEntity nest) {
boolean playerNearby = level.hasNearbyAlivePlayer(
pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, PLAYER_ACTIVATION_RANGE);
if (!playerNearby) {
return;
}
if (!nest.initialized) {
nest.populateInitialAnts(level);
} else if (nest.storedAnts.isEmpty() && nest.nestBlock().shouldPopulateInitialAnts()) {
if (--nest.rebroodCooldown <= 0) {
nest.rebroodCooldown = REBROOD_COOLDOWN_TICKS;
nest.fillWithAnts(level);
}
}
boolean ignoreNight = level.dimensionType().hasFixedTime();
@@ -67,11 +82,16 @@ public class AntNestBlockEntity extends BlockEntity {
return;
}
this.fillWithAnts(level);
}
private void fillWithAnts(ServerLevel level) {
int amount = INITIAL_ANT_COUNT_MIN + level.random.nextInt(INITIAL_ANT_COUNT_RANGE);
for (int i = 0; i < amount && this.storedAnts.size() < this.maxOccupants(); i++) {
EntityType<?> antType = this.nestBlock().initialAntType(level.random);
this.storedAnts.add(this.createStoredAnt(level, antType));
}
this.rebroodCooldown = REBROOD_COOLDOWN_TICKS;
this.setChanged();
}
@@ -152,6 +172,7 @@ public class AntNestBlockEntity extends BlockEntity {
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.putBoolean(NBT_INITIALIZED, this.initialized);
tag.putInt(NBT_REBROOD_COOLDOWN, this.rebroodCooldown);
ListTag listTag = new ListTag();
for (StoredAnt storedAnt : this.storedAnts) {
listTag.add(storedAnt.save());
@@ -163,6 +184,7 @@ public class AntNestBlockEntity extends BlockEntity {
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
this.initialized = tag.getBoolean(NBT_INITIALIZED);
this.rebroodCooldown = tag.contains(NBT_REBROOD_COOLDOWN) ? tag.getInt(NBT_REBROOD_COOLDOWN) : REBROOD_COOLDOWN_TICKS;
this.storedAnts.clear();
ListTag listTag = tag.getList(STORED_ANTS, Tag.TAG_COMPOUND);
for (int i = 0; i < listTag.size(); i++) {
@@ -1,75 +1,147 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.time.TimeDilationEntityAccess;
import com.craisinlord.antarchy.content.time.TimeDilationFieldEntity;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSampler;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshotSampler;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.Mth;
public final class ClientTimeDilationTicker {
private static boolean hadFields;
private static volatile List<TimeDilationFieldEntity> activeFields = List.of();
private static final Map<Entity, Double> ANIM_OFFSETS = new WeakHashMap<>();
private static volatile List<TimeDilationFieldSnapshot> activeFields = List.of();
private static ClientLevel activeLevel;
private static final Map<ItemEntity, ItemSpinState> ITEM_SPIN_STATES = new WeakHashMap<>();
private static final Map<LivingEntity, AttackAnimationState> ATTACK_ANIMATION_STATES = new WeakHashMap<>();
private static final Map<Entity, AnimationClockState> ANIMATION_CLOCKS = new WeakHashMap<>();
private ClientTimeDilationTicker() {
}
public static void tick(ClientLevel level) {
List<TimeDilationFieldEntity> fields = new ArrayList<>();
for (Entity entity : level.entitiesForRendering()) {
if (entity instanceof TimeDilationFieldEntity field && field.isAlive()) {
fields.add(field);
}
}
activeFields = fields;
if (fields.isEmpty()) {
if (hadFields) {
resetRates(level);
hadFields = false;
}
return;
}
hadFields = true;
for (Entity entity : level.entitiesForRendering()) {
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
continue;
}
double rate = TimeDilationFieldSampler.sample(fields, entity.position());
access.antarchy$setTimeDilationRate(rate);
if (rate < TimeDilationMath.NORMAL_RATE) {
ANIM_OFFSETS.merge(entity, rate - TimeDilationMath.NORMAL_RATE, Double::sum);
}
if (activeLevel != level) {
activeLevel = level;
activeFields = List.of();
ITEM_SPIN_STATES.clear();
ATTACK_ANIMATION_STATES.clear();
ANIMATION_CLOCKS.clear();
com.craisinlord.antarchy.content.time.TimeDilationApi.clearSyncedClientRates();
}
// Entity rates come from the server. Field snapshots are used only for local effects.
}
public static float dilateAnimationTime(Entity entity, float vanillaTime) {
Double offset = ANIM_OFFSETS.get(entity);
return offset == null ? vanillaTime : (float) (vanillaTime + offset);
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ANIMATION_CLOCKS.remove(entity);
return vanillaTime;
}
AnimationClockState state = ANIMATION_CLOCKS.computeIfAbsent(entity,
ignored -> new AnimationClockState(vanillaTime));
float delta = vanillaTime - state.lastVanillaTime;
if (delta >= 0.0F) {
state.temporalTime += delta * (float) rate;
}
state.lastVanillaTime = vanillaTime;
return state.temporalTime;
}
public static float dilateAttackAnimation(LivingEntity entity, float partialTick, float vanillaAnimation) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ATTACK_ANIMATION_STATES.remove(entity);
return vanillaAnimation;
}
AttackAnimationState state = ATTACK_ANIMATION_STATES.computeIfAbsent(
entity, ignored -> new AttackAnimationState(entity.tickCount, vanillaAnimation)
);
if (state.lastTick != entity.tickCount) {
state.previous = state.current;
state.current += (vanillaAnimation - state.current) * (float) rate;
state.lastTick = entity.tickCount;
}
return state.previous + (state.current - state.previous) * partialTick;
}
public static float dilateRotation(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.rotLerp(partialTick, previous, current);
}
return previous + Mth.wrapDegrees(current - previous) * partialTick * (float) rate;
}
public static float dilatePitch(LivingEntity entity, float previous, float current, float partialTick) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return Mth.lerp(partialTick, previous, current);
}
return previous + (current - previous) * partialTick * (float) rate;
}
public static float dilateWalkAmount(LivingEntity entity, float amount) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity);
return rate >= TimeDilationMath.NORMAL_RATE ? amount : amount * (float) rate;
}
public static float dilateItemSpin(ItemEntity item, float vanillaSpin) {
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
if (rate >= TimeDilationMath.NORMAL_RATE) {
ITEM_SPIN_STATES.remove(item);
return vanillaSpin;
}
ItemSpinState state = ITEM_SPIN_STATES.computeIfAbsent(item, ignored -> new ItemSpinState(vanillaSpin));
float rawDelta = vanillaSpin - state.lastVanillaSpin;
state.lastVanillaSpin = vanillaSpin;
state.temporalSpin += rawDelta * (float) rate;
return state.temporalSpin;
}
private static final class ItemSpinState {
private float lastVanillaSpin;
private float temporalSpin;
private ItemSpinState(float initialSpin) {
this.lastVanillaSpin = initialSpin;
this.temporalSpin = initialSpin;
}
}
private static final class AttackAnimationState {
private int lastTick;
private float previous;
private float current;
private AttackAnimationState(int tick, float initial) {
this.lastTick = tick;
this.previous = initial;
this.current = initial;
}
}
private static final class AnimationClockState {
private float lastVanillaTime;
private float temporalTime;
private AnimationClockState(float initialTime) {
this.lastVanillaTime = initialTime;
this.temporalTime = initialTime;
}
}
public static double rateAt(double x, double y, double z) {
List<TimeDilationFieldEntity> fields = activeFields;
List<TimeDilationFieldSnapshot> fields = activeFields;
if (fields.isEmpty()) {
return TimeDilationMath.NORMAL_RATE;
}
return TimeDilationFieldSampler.sample(fields, x, y, z);
return TimeDilationFieldSnapshotSampler.sample(fields, x, y, z);
}
private static void resetRates(ClientLevel level) {
for (Entity entity : level.entitiesForRendering()) {
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
continue;
}
if (access.antarchy$getTimeDilationRate() < TimeDilationMath.NORMAL_RATE) {
access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE);
}
}
public static void applyFields(List<TimeDilationFieldSnapshot> fields) {
activeFields = List.copyOf(fields);
}
}
@@ -2,18 +2,34 @@ package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.RoyalBossModel;
import com.craisinlord.antarchy.content.entity.royal.RoyalBossEntity;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
import software.bernie.geckolib.util.Color;
public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
private static final ResourceLocation QUEEN_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_outer.png");
private static final ResourceLocation QUEEN_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_inner.png");
private static final ResourceLocation QUEEN_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/queen/queen_beam_end_1.png");
private static final ResourceLocation KING_FIRE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_outer.png");
private static final ResourceLocation KING_FIRE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_inner.png");
private static final ResourceLocation KING_FIRE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/fire_beam_end_1.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_outer.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_inner.png");
private static final ResourceLocation KING_LIGHTNING_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/lightning_beam_end_1.png");
private static final ResourceLocation KING_ICE_BEAM_OUTER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_outer.png");
private static final ResourceLocation KING_ICE_BEAM_INNER = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_inner.png");
private static final ResourceLocation KING_ICE_BEAM_END = ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/king/ice_beam_end_1.png");
public RoyalBossRenderer(EntityRendererProvider.Context context) {
super(context, new RoyalBossModel());
this.shadowRadius = 8.0F;
@@ -24,6 +40,16 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
return RenderType.entityCutoutNoCull(texture);
}
@Override
public Color getRenderColor(RoyalBossEntity animatable, float partialTick, int packedLight) {
if (!animatable.isRoyalAccelerated()) {
return Color.ofRGBA(255, 255, 255, 255);
}
float pulse = 0.5F + 0.5F * (float) Math.sin((animatable.tickCount + partialTick) * 0.4F);
int green = 60 + Math.round(60.0F * pulse);
return Color.ofRGBA(255, green, green, 255);
}
@Override
public void preRender(PoseStack poseStack, RoyalBossEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource,
@Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
@@ -32,6 +58,11 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
this.shadowRadius = 8.0F * RoyalBossEntity.MODEL_RENDER_SCALE;
}
private static final float BEAM_OUTER_RADIUS = 0.55F;
private static final float BEAM_INNER_RADIUS = 0.26F;
private static final float BEAM_TILE_BLOCKS = 3.0F;
private static final float BEAM_SCROLL_SPEED = 0.32F;
@Override
public void render(RoyalBossEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) {
@@ -40,36 +71,105 @@ public class RoyalBossRenderer extends GeoEntityRenderer<RoyalBossEntity> {
if (!entity.isFiringRoyalBeam() || end == null) {
return;
}
double x = net.minecraft.util.Mth.lerp(partialTick, entity.xo, entity.getX());
double y = net.minecraft.util.Mth.lerp(partialTick, entity.yo, entity.getY());
double z = net.minecraft.util.Mth.lerp(partialTick, entity.zo, entity.getZ());
double x = Mth.lerp(partialTick, entity.xo, entity.getX());
double y = Mth.lerp(partialTick, entity.yo, entity.getY());
double z = Mth.lerp(partialTick, entity.zo, entity.getZ());
Vec3 start = entity.getRoyalBeamShootFrom(partialTick).subtract(x, y, z);
Vec3 finish = end.subtract(x, y, z);
Vec3 direction = finish.subtract(start).normalize();
Vec3 side = direction.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (side.lengthSqr() < 1.0E-4D) {
side = direction.cross(new Vec3(1.0D, 0.0D, 0.0D));
Vec3 axis = finish.subtract(start);
double length = axis.length();
if (length < 0.05D) {
return;
}
side = side.normalize().scale(0.22D);
VertexConsumer vertices = bufferSource.getBuffer(RenderType.lightning());
poseStack.pushPose();
poseStack.scale(0.5F, 0.5F, 0.5F);
drawBeamQuad(vertices, poseStack, start, finish, side);
drawBeamQuad(vertices, poseStack, start, finish, new Vec3(-side.z, side.y, side.x));
poseStack.popPose();
}
Vec3 dir = axis.scale(1.0D / length);
Vec3 side1 = dir.cross(new Vec3(0.0D, 1.0D, 0.0D));
if (side1.lengthSqr() < 1.0E-4D) {
side1 = dir.cross(new Vec3(1.0D, 0.0D, 0.0D));
}
side1 = side1.normalize();
Vec3 side2 = dir.cross(side1).normalize();
private static void drawBeamQuad(VertexConsumer vertices, PoseStack poseStack, Vec3 start, Vec3 end, Vec3 side) {
ResourceLocation outer = QUEEN_BEAM_OUTER;
ResourceLocation inner = QUEEN_BEAM_INNER;
ResourceLocation endTexture = QUEEN_BEAM_END;
if (entity instanceof KingEntity) {
switch (entity.getRoyalBeamElement()) {
case FIRE -> {
outer = KING_FIRE_BEAM_OUTER;
inner = KING_FIRE_BEAM_INNER;
endTexture = KING_FIRE_BEAM_END;
}
case LIGHTNING -> {
outer = KING_LIGHTNING_BEAM_OUTER;
inner = KING_LIGHTNING_BEAM_INNER;
endTexture = KING_LIGHTNING_BEAM_END;
}
case ICE -> {
outer = KING_ICE_BEAM_OUTER;
inner = KING_ICE_BEAM_INNER;
endTexture = KING_ICE_BEAM_END;
}
case GENERIC -> {
}
}
}
float time = entity.tickCount + partialTick;
float tiles = (float) (length / BEAM_TILE_BLOCKS);
float scroll = -time * BEAM_SCROLL_SPEED;
var pose = poseStack.last().pose();
vertex(vertices, pose, start.subtract(side));
vertex(vertices, pose, start.add(side));
vertex(vertices, pose, end.add(side));
vertex(vertices, pose, end.subtract(side));
VertexConsumer outerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(outer));
drawBeamPlane(outerBuffer, pose, start, finish, side1.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
drawBeamPlane(outerBuffer, pose, start, finish, side2.scale(BEAM_OUTER_RADIUS), scroll, scroll + tiles);
float innerScroll = -time * BEAM_SCROLL_SPEED * 1.6F;
VertexConsumer innerBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(inner));
drawBeamPlane(innerBuffer, pose, start, finish, side1.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
drawBeamPlane(innerBuffer, pose, start, finish, side2.scale(BEAM_INNER_RADIUS), innerScroll, innerScroll + tiles);
float impactSize = 1.1F + 0.35F * Mth.sin(time * 0.6F);
Vec3 camLeft = new Vec3(this.entityRenderDispatcher.camera.getLeftVector());
Vec3 camUp = new Vec3(this.entityRenderDispatcher.camera.getUpVector());
VertexConsumer endBuffer = bufferSource.getBuffer(RenderType.entityTranslucent(endTexture));
drawBillboard(endBuffer, pose, finish, camLeft.scale(impactSize), camUp.scale(impactSize));
}
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos) {
private static void drawBeamPlane(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 start, Vec3 end, Vec3 half,
float startV, float endV) {
emitQuad(vertices, pose,
start.subtract(half), start.add(half), end.add(half), end.subtract(half),
0.0F, startV, 1.0F, endV);
emitQuad(vertices, pose,
start.add(half), start.subtract(half), end.subtract(half), end.add(half),
0.0F, startV, 1.0F, endV);
}
private static void drawBillboard(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 center, Vec3 left, Vec3 up) {
emitQuad(vertices, pose,
center.add(left).add(up), center.subtract(left).add(up),
center.subtract(left).subtract(up), center.add(left).subtract(up),
0.0F, 0.0F, 1.0F, 1.0F);
emitQuad(vertices, pose,
center.subtract(left).add(up), center.add(left).add(up),
center.add(left).subtract(up), center.subtract(left).subtract(up),
0.0F, 0.0F, 1.0F, 1.0F);
}
private static void emitQuad(VertexConsumer vertices, org.joml.Matrix4f pose,
Vec3 a, Vec3 b, Vec3 c, Vec3 d,
float minU, float minV, float maxU, float maxV) {
vertex(vertices, pose, a, minU, minV);
vertex(vertices, pose, b, maxU, minV);
vertex(vertices, pose, c, maxU, maxV);
vertex(vertices, pose, d, minU, maxV);
}
private static void vertex(VertexConsumer vertices, org.joml.Matrix4f pose, Vec3 pos, float u, float v) {
vertices.addVertex(pose, (float) pos.x, (float) pos.y, (float) pos.z)
.setColor(255, 255, 255, 255).setUv(0.0F, 0.0F).setUv2(15728880 & 65535, 15728880 >> 16)
.setColor(255, 255, 255, 255).setUv(u, v)
.setOverlay(net.minecraft.client.renderer.texture.OverlayTexture.NO_OVERLAY)
.setLight(0x00F000F0)
.setNormal(0.0F, 1.0F, 0.0F);
}
}
@@ -0,0 +1,76 @@
package com.craisinlord.antarchy.content.command;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.DoubleArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
public final class DimensionalTearCommand {
private static final double DEFAULT_GAP = 24.0D;
private static final double VERTICAL_OFFSET = 1.0D;
private DimensionalTearCommand() {
}
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(
Commands.literal("dimensionaltear")
.requires(source -> source.hasPermission(2))
.executes(context -> spawnPair(context, DEFAULT_GAP))
.then(Commands.literal("single")
.executes(DimensionalTearCommand::spawnSingle))
.then(Commands.argument("blocksAhead", DoubleArgumentType.doubleArg(2.0D, 512.0D))
.executes(context -> spawnPair(context, DoubleArgumentType.getDouble(context, "blocksAhead"))))
);
}
private static int lifetime() {
return Math.max(1200, AntarchySettings.dimensionalTearLifetimeTicks());
}
private static Vec3 anchorPos(ServerPlayer player) {
return player.position().add(0.0D, VERTICAL_OFFSET, 0.0D);
}
private static int spawnSingle(CommandContext<CommandSourceStack> context) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
ServerPlayer player = context.getSource().getPlayerOrException();
ServerLevel level = context.getSource().getLevel();
DimensionalTearEntity tear = DimensionalTearEntity.create(level, anchorPos(player), player.getYRot(), lifetime());
level.addFreshEntity(tear);
context.getSource().sendSuccess(() -> Component.literal("Spawned an unlinked dimensional tear"), true);
return 1;
}
private static int spawnPair(CommandContext<CommandSourceStack> context, double gap) throws com.mojang.brigadier.exceptions.CommandSyntaxException {
ServerPlayer player = context.getSource().getPlayerOrException();
ServerLevel level = context.getSource().getLevel();
float yawA = player.getYRot();
Vec3 posA = anchorPos(player);
Vec3 forward = Vec3.directionFromRotation(0.0F, yawA);
Vec3 posB = posA.add(forward.scale(gap));
float yawB = Mth.wrapDegrees(yawA + 180.0F);
DimensionalTearEntity tearA = DimensionalTearEntity.create(level, posA, yawA, lifetime());
DimensionalTearEntity tearB = DimensionalTearEntity.create(level, posB, yawB, lifetime());
tearA.linkTo(tearB);
tearB.linkTo(tearA);
level.addFreshEntity(tearA);
level.addFreshEntity(tearB);
int spacing = Mth.floor(gap);
context.getSource().sendSuccess(
() -> Component.literal("Spawned a linked dimensional tear pair, " + spacing + " blocks apart"),
true
);
return 1;
}
}
@@ -1,14 +1,19 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.content.block.AntimetalScaffoldingBlock;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MoverType;
@@ -32,10 +37,17 @@ public class UpwardFallingBlockEntity extends Entity {
private static final double RISE_ACCEL = 0.04;
private static final double DRAG = 0.98;
private static final int MAX_TICKS = 200;
private static final int MAX_LIVE_PER_LEVEL = 200;
private static final double PLAYER_ACTIVATION_RADIUS = 128.0;
private static final int INSTANT_MOVE_MAX_DISTANCE = 48;
private static final double MIN_TRAVEL_FOR_DAMAGE = 1.5;
private static final Object2IntOpenHashMap<ResourceKey<Level>> LIVE_COUNT = new Object2IntOpenHashMap<>();
public int time = 0;
private boolean breaksOnLeaves = true;
private double distanceTraveled = 0.0;
private boolean counted = false;
public UpwardFallingBlockEntity(EntityType<UpwardFallingBlockEntity> type, Level level) {
super(type, level);
@@ -60,16 +72,90 @@ public class UpwardFallingBlockEntity extends Entity {
}
public static void fallUp(Level level, BlockPos pos, BlockState state, boolean breaksOnLeaves) {
if (level.isClientSide) return;
// Prevent stacking multiple entities from the same block position
if (!level.getEntitiesOfClass(UpwardFallingBlockEntity.class, new AABB(pos)).isEmpty()) return;
if (!(level instanceof ServerLevel serverLevel)) {
return;
}
level.removeBlock(pos, false);
UpwardFallingBlockEntity entity = new UpwardFallingBlockEntity(TYPE.get(), level);
ResourceKey<Level> dimension = serverLevel.dimension();
int live = LIVE_COUNT.getInt(dimension);
if (live >= MAX_LIVE_PER_LEVEL) {
live = serverLevel.getEntities(TYPE.get(), Entity::isAlive).size();
if (live <= 0) {
LIVE_COUNT.removeInt(dimension);
} else {
LIVE_COUNT.put(dimension, live);
}
}
boolean noPlayerNearby = serverLevel.getNearestPlayer(
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, PLAYER_ACTIVATION_RADIUS, EntitySelector.NO_SPECTATORS) == null;
if (live >= MAX_LIVE_PER_LEVEL || noPlayerNearby) {
resolveInstantly(serverLevel, pos, state, breaksOnLeaves);
return;
}
serverLevel.removeBlock(pos, false);
UpwardFallingBlockEntity entity = new UpwardFallingBlockEntity(TYPE.get(), serverLevel);
entity.entityData.set(DATA_BLOCK_STATE, state);
entity.breaksOnLeaves = breaksOnLeaves;
entity.setPos(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
level.addFreshEntity(entity);
if (serverLevel.addFreshEntity(entity)) {
entity.counted = true;
LIVE_COUNT.addTo(serverLevel.dimension(), 1);
}
}
private static void resolveInstantly(ServerLevel level, BlockPos pos, BlockState state, boolean breaksOnLeaves) {
level.removeBlock(pos, false);
BlockPos.MutableBlockPos rest = pos.mutable();
for (int i = 0; i < INSTANT_MOVE_MAX_DISTANCE; i++) {
BlockPos above = rest.above();
if (above.getY() >= level.getMaxBuildHeight()) {
break;
}
BlockState aboveState = level.getBlockState(above);
if (aboveState.getBlock() instanceof AntimetalScaffoldingBlock) {
break;
}
if (breaksOnLeaves && aboveState.is(BlockTags.LEAVES)) {
dropAsItem(level, rest, state);
return;
}
if (!aboveState.canBeReplaced()) {
break;
}
rest.move(Direction.UP);
}
if (level.getBlockState(rest).canBeReplaced()) {
level.setBlock(rest, state, Block.UPDATE_ALL);
} else {
dropAsItem(level, rest, state);
}
}
private static void dropAsItem(Level level, BlockPos pos, BlockState state) {
if (state.isAir()) {
return;
}
ItemStack drop = new ItemStack(state.getBlock().asItem());
if (!drop.isEmpty()) {
Block.popResource(level, pos, drop);
}
}
@Override
public void remove(Entity.RemovalReason reason) {
if (this.counted) {
this.counted = false;
ResourceKey<Level> key = this.level().dimension();
int remaining = LIVE_COUNT.addTo(key, -1) - 1;
if (remaining <= 0) {
LIVE_COUNT.removeInt(key);
}
}
super.remove(reason);
}
@Override
@@ -99,7 +185,9 @@ public class UpwardFallingBlockEntity extends Entity {
return;
}
damageEntitiesAlongPath(blockState);
if (distanceTraveled > MIN_TRAVEL_FOR_DAMAGE) {
damageEntitiesAlongPath(blockState);
}
BlockPos headPos = BlockPos.containing(getX(), getBoundingBox().maxY, getZ());
BlockState above = level().getBlockState(headPos);
@@ -120,12 +120,22 @@ public class CloudSharkEntity extends Monster implements GeoEntity {
return super.finalizeSpawn(level, difficulty, spawnReason, spawnData);
}
private static final int MIN_SPAWN_HEIGHT_ABOVE_TERRAIN = 16;
public static boolean canSpawn(EntityType<CloudSharkEntity> entityType, ServerLevelAccessor level,
MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
return level.getDifficulty() != Difficulty.PEACEFUL
&& level.isEmptyBlock(pos)
&& (level.isEmptyBlock(pos.above()) || level.isEmptyBlock(pos.below()))
&& level.getFluidState(pos).isEmpty();
if (level.getDifficulty() == Difficulty.PEACEFUL || !level.getFluidState(pos).isEmpty()) {
return false;
}
if (!level.isEmptyBlock(pos) || !level.isEmptyBlock(pos.above()) || !level.isEmptyBlock(pos.below())) {
return false;
}
if (spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.SPAWN_EGG
|| spawnReason == MobSpawnType.COMMAND || spawnReason == MobSpawnType.BUCKET) {
return true;
}
int terrainHeight = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, pos.getX(), pos.getZ());
return pos.getY() >= terrainHeight + MIN_SPAWN_HEIGHT_ABOVE_TERRAIN;
}
@Override
@@ -183,6 +183,7 @@ public class NightmareEntity extends Monster implements GeoEntity {
private static final RawAnimation PHASE_TWO_WALK_ANIM = RawAnimation.begin().thenLoop("walk_P2");
private static final RawAnimation PHASE_TWO_FLY_ANIM = RawAnimation.begin().thenLoop("fly_P2");
private static final String LOCOMOTION_CONTROLLER = "main_controller";
private static final int LOCOMOTION_TRANSITION_TICKS = 5;
private static final String SPECIAL_CONTROLLER = "special_controller";
private static final String SPECIAL_ATTACK_TRIGGER = "attack";
private static final String SPECIAL_ATTACK_P2_TRIGGER = "attack_P2";
@@ -293,7 +294,7 @@ public class NightmareEntity extends Monster implements GeoEntity {
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, LOCOMOTION_CONTROLLER, 0, this::mainAnimController));
controllers.add(new AnimationController<>(this, LOCOMOTION_CONTROLLER, LOCOMOTION_TRANSITION_TICKS, this::mainAnimController));
controllers.add(new AnimationController<>(this, SPECIAL_CONTROLLER, 0, state -> PlayState.STOP)
.triggerableAnim(SPECIAL_ATTACK_TRIGGER, ATTACK_ANIM)
.triggerableAnim(SPECIAL_ATTACK_P2_TRIGGER, ATTACK_P2_ANIM)
@@ -3,34 +3,98 @@ package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.network.chat.Component;
import net.minecraft.ChatFormatting;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.network.protocol.game.ClientboundClearTitlesPacket;
import net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket;
import net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket;
import net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket;
import net.minecraft.world.BossEvent;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.Projectile;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.ArmorMaterials;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.EntityType;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import com.craisinlord.antarchy.content.entity.royal.decree.AdvanceDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.CloseQuartersDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.ComeNoCloserDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.DoNotRunDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.FightMeCowardDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.HandsOffTheCrownDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.KeepYourDistanceDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.KneelDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.NoRespiteDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.NoRetreatDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.RoyalDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.ShowNoMercyDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.SkyIsMineDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.StandTallDecree;
import com.craisinlord.antarchy.content.entity.royal.decree.StandYourGroundDecree;
import java.util.EnumMap;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class KingEntity extends RoyalBossEntity {
public enum Behavior {
AIRBORNE, SPRINTING, RETREATING, HUGGING, RANGED, KEEPING_FAR
}
private static final int STOMP_COOLDOWN_TICKS = 220;
private static final int STOMP_WINDUP_TICKS = 18;
private static final double STOMP_RADIUS = 15.0D;
private static final int WING_GUST_COOLDOWN_TICKS = 320;
private static final int WING_GUST_WINDUP_TICKS = 14;
private static final double WING_GUST_RADIUS = 22.0D;
private static final int CHAIN_LIGHTNING_INTERVAL = 12;
private static final int CHAIN_LIGHTNING_JUMPS = 4;
private static final double CHAIN_LIGHTNING_JUMP_RANGE = 9.0D;
private static final int ICE_FREEZE_PER_TICK = 7;
private static final int BEHAVIOR_SCORE_CAP = 12;
@Nullable
private Vec3 patrolCenter;
private int patrolCooldownTicks;
private int decreeCooldownTicks;
private int fireAttackCooldownTicks;
private int lightningAttackCooldownTicks;
private RoyalDecree activeDecree = new AdvanceDecree();
private static final List<RoyalDecree> DECREES = List.of(new CloseQuartersDecree(), new KeepYourDistanceDecree(), new NoRetreatDecree(), new AdvanceDecree());
private int activeDecreeTicks;
private int stompCooldownTicks;
private int stompWindupTicks;
private int wingGustCooldownTicks;
private int wingGustWindupTicks;
private boolean decreeRetreatPressure;
private final Map<UUID, Long> judgmentCooldowns = new HashMap<>();
private final Map<UUID, Long> lastPlayerDamageTime = new HashMap<>();
private final Map<UUID, Integer> iceBuildup = new HashMap<>();
private final EnumMap<Behavior, Integer> behaviorScores = new EnumMap<>(Behavior.class);
@Nullable
private RoyalDecree activeDecree;
private static final List<RoyalDecree> DECREES = List.of(
new SkyIsMineDecree(), new DoNotRunDecree(), new KneelDecree(), new CloseQuartersDecree(),
new ComeNoCloserDecree(), new FightMeCowardDecree(), new NoRespiteDecree(), new NoRetreatDecree(),
new KeepYourDistanceDecree(), new StandTallDecree(), new StandYourGroundDecree(),
new HandsOffTheCrownDecree(), new AdvanceDecree(), new ShowNoMercyDecree());
public KingEntity(EntityType<? extends KingEntity> entityType, Level level) {
super(entityType, level);
}
@@ -80,6 +144,13 @@ public class KingEntity extends RoyalBossEntity {
@Override
protected SoundEvent royalBeamStartSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@Override
protected SoundEvent royalBeamStartSound(RoyalHead head) {
return head.slot() == RoyalHead.Slot.LEFT
? AntarchySoundEvents.KING_FIREBALL_SHOOT.get()
: AntarchySoundEvents.KING_BEAM_SHOOT.get();
}
@Override
protected SoundEvent royalBeamLoopSound() { return AntarchySoundEvents.KING_BEAM_SHOOT.get(); }
@@ -106,61 +177,362 @@ public class KingEntity extends RoyalBossEntity {
}
@Override
public void tick() {
super.tick();
if (!this.level().isClientSide && this.getTarget() != null && this.level() instanceof ServerLevel level) {
this.tickElementalAttacks(level, this.getTarget());
protected RoyalBeamElement royalBeamElement(@Nullable RoyalHead head) {
if (head == null) {
return RoyalBeamElement.GENERIC;
}
if (this.level().isClientSide || this.getTarget() != null || this.isDeadOrDying()
|| !(this.level() instanceof ServerLevel)) {
if (!this.level().isClientSide && this.getTarget() != null && this.level() instanceof ServerLevel level) {
this.tickDecree(level, this.getTarget());
}
return switch (head.slot()) {
case LEFT -> RoyalBeamElement.FIRE;
case CENTER -> RoyalBeamElement.LIGHTNING;
case RIGHT -> RoyalBeamElement.ICE;
};
}
@Override
protected void tickRoyalBeamEffects(RoyalHead head, Vec3 start, Vec3 end) {
if (!(this.level() instanceof ServerLevel level)) {
return;
}
if (this.patrolCenter == null) {
var particle = switch (head.slot()) {
case LEFT -> ParticleTypes.FLAME;
case CENTER -> ParticleTypes.ELECTRIC_SPARK;
case RIGHT -> ParticleTypes.SNOWFLAKE;
};
Vec3 direction = end.subtract(start);
double distance = direction.length();
if (distance > 0.001D) {
direction = direction.scale(1.0D / distance);
for (double walked = 0.0D; walked <= distance; walked += 2.5D) {
Vec3 point = start.add(direction.scale(walked));
level.sendParticles(particle, point.x, point.y, point.z, 2, 0.12D, 0.12D, 0.12D, 0.01D);
}
}
LivingEntity target = head.target(this.level());
switch (head.slot()) {
case LEFT -> {
if (target != null && target.isAlive()) {
target.setRemainingFireTicks(Math.max(target.getRemainingFireTicks(), 40));
}
}
case CENTER -> {
if (this.tickCount % CHAIN_LIGHTNING_INTERVAL == 0) {
this.chainLightning(level, end);
}
}
case RIGHT -> {
if (target != null && target.isAlive()) {
this.applyIceFreezePressure(target);
}
}
}
}
private void chainLightning(ServerLevel level, Vec3 origin) {
java.util.Set<UUID> struck = new java.util.HashSet<>();
Vec3 from = origin;
float damage = (float) AntarchySettings.kingBeamDamage() * 0.6F;
for (int jump = 0; jump < CHAIN_LIGHTNING_JUMPS; jump++) {
LivingEntity next = null;
double bestScore = Double.MAX_VALUE;
for (LivingEntity candidate : level.getEntitiesOfClass(LivingEntity.class,
new AABB(from, from).inflate(CHAIN_LIGHTNING_JUMP_RANGE),
e -> e.isAlive() && !(e instanceof RoyalBossEntity) && e.getType() != this.getType())) {
if (struck.contains(candidate.getUUID())) {
continue;
}
double score = candidate.position().distanceToSqr(from) - (isConductive(candidate) ? 16.0D : 0.0D);
if (score < bestScore) {
bestScore = score;
next = candidate;
}
}
if (next == null) {
break;
}
struck.add(next.getUUID());
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(level);
if (bolt != null) {
bolt.moveTo(next.getX(), next.getY(), next.getZ());
bolt.setVisualOnly(true);
level.addFreshEntity(bolt);
}
next.hurt(this.damageSources().lightningBolt(), damage);
from = next.position().add(0.0D, next.getBbHeight() * 0.5D, 0.0D);
if (isConductive(next)) {
jump--;
}
}
}
private static boolean isConductive(LivingEntity entity) {
for (ItemStack stack : entity.getArmorSlots()) {
if (stack.getItem() instanceof ArmorItem armor) {
var material = armor.getMaterial();
if (material == ArmorMaterials.IRON || material == ArmorMaterials.NETHERITE || material == ArmorMaterials.CHAIN) {
return true;
}
}
}
return false;
}
private void applyIceFreezePressure(LivingEntity target) {
int buildup = this.iceBuildup.merge(target.getUUID(), ICE_FREEZE_PER_TICK, Integer::sum);
target.setTicksFrozen(Math.min(target.getTicksRequiredToFreeze() + 60, target.getTicksFrozen() + ICE_FREEZE_PER_TICK * 2));
if (buildup >= 60) {
target.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 40, 3, false, true));
this.iceBuildup.put(target.getUUID(), 20);
}
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide) {
return;
}
if (this.iceBuildup.size() > 16) {
this.iceBuildup.clear();
}
LivingEntity target = this.getTarget();
if (target != null && !this.isDeadOrDying() && this.level() instanceof ServerLevel level) {
this.trackBehavior(target);
this.tickDecree(level, target);
this.tickKingWholeBody(level, target);
return;
}
if (this.isDeadOrDying() || !(this.level() instanceof ServerLevel)) {
return;
}
this.decreeRetreatPressure = false;
if (this.patrolCenter == null || this.position().distanceToSqr(this.patrolCenter) > 64.0D * 64.0D) {
this.patrolCenter = this.position();
}
if (this.patrolCooldownTicks-- <= 0) {
this.patrolCooldownTicks = 100 + this.random.nextInt(100);
this.playSound(AntarchySoundEvents.KING_WING_FLAP.get(), 2.0F, 0.9F + this.random.nextFloat() * 0.15F);
double angle = this.random.nextDouble() * Math.PI * 2.0D;
double radius = 12.0D + this.random.nextDouble() * 18.0D;
this.getMoveControl().setWantedPosition(this.patrolCenter.x + Math.cos(angle) * radius,
this.patrolCenter.y + 6.0D + this.random.nextDouble() * 12.0D,
this.patrolCenter.z + Math.sin(angle) * radius, 1.0D);
double px = this.patrolCenter.x + Math.cos(angle) * radius;
double pz = this.patrolCenter.z + Math.sin(angle) * radius;
double py = this.groundYBelow(px, pz) + FLYING_PREFERRED_HOVER + this.random.nextDouble() * 5.0D;
this.getMoveControl().setWantedPosition(px, py, pz, 1.0D);
}
}
private void tickDecree(ServerLevel level, net.minecraft.world.entity.LivingEntity target) {
if (this.decreeCooldownTicks-- <= 0) {
this.decreeCooldownTicks = 220;
this.activeDecree = DECREES.get(this.random.nextInt(DECREES.size()));
this.playSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 3.0F, 0.9F + this.random.nextFloat() * 0.15F);
private void trackBehavior(LivingEntity target) {
double distance = target.distanceTo(this);
this.bumpBehavior(Behavior.AIRBORNE, !target.onGround() && !target.isPassenger() ? 1 : -1);
this.bumpBehavior(Behavior.SPRINTING, target.isSprinting() ? 1 : -1);
this.bumpBehavior(Behavior.HUGGING, distance <= 10.0D ? 1 : -1);
this.bumpBehavior(Behavior.KEEPING_FAR, distance >= 22.0D ? 1 : -1);
if (this.patrolCenter != null) {
this.bumpBehavior(Behavior.RETREATING, distance > 20.0D && target.getDeltaMovement().horizontalDistanceSqr() > 0.02D
&& target.position().subtract(this.position()).dot(target.getDeltaMovement()) > 0.0D ? 1 : -1);
}
this.activeDecree.apply(level, this, target);
}
private void tickElementalAttacks(ServerLevel level, net.minecraft.world.entity.LivingEntity target) {
if (this.fireAttackCooldownTicks-- <= 0) {
this.fireAttackCooldownTicks = 110;
Vec3 start = this.position().add(0.0D, 5.0D, 0.0D);
RoyalBoltEntity bolt = new RoyalBoltEntity(AntarchyObjects.ROYAL_BOLT.get(), level);
bolt.setOwner(this);
bolt.setPos(start);
Vec3 delta = target.getEyePosition().subtract(start);
bolt.shoot(delta.x, delta.y, delta.z, 1.15F, 0.0F);
level.addFreshEntity(bolt);
this.playSound(AntarchySoundEvents.KING_FIREBALL_SHOOT.get(), 3.0F, 1.0F);
}
if (this.lightningAttackCooldownTicks-- <= 0) {
this.lightningAttackCooldownTicks = 150;
for (net.minecraft.world.entity.LivingEntity living : level.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class,
target.getBoundingBox().inflate(5.0D), entity -> entity.isAlive() && entity != this)) {
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(level);
if (bolt == null) continue;
bolt.moveTo(living.getX(), living.getY(), living.getZ());
level.addFreshEntity(bolt);
private void bumpBehavior(Behavior behavior, int delta) {
int updated = Math.max(0, Math.min(BEHAVIOR_SCORE_CAP, this.behaviorScores.getOrDefault(behavior, 0) + delta));
this.behaviorScores.put(behavior, updated);
}
public int behaviorScore(Behavior behavior) {
return this.behaviorScores.getOrDefault(behavior, 0);
}
public void setDecreeRetreatPressure(boolean value) {
this.decreeRetreatPressure = value;
}
private void tickKingWholeBody(ServerLevel level, LivingEntity target) {
if (this.stompCooldownTicks > 0) this.stompCooldownTicks--;
if (this.wingGustCooldownTicks > 0) this.wingGustCooldownTicks--;
boolean skyIsMine = this.activeDecree instanceof SkyIsMineDecree;
boolean forceFar = this.decreeRetreatPressure
|| this.activeDecree instanceof ComeNoCloserDecree
|| this.activeDecree instanceof KeepYourDistanceDecree;
if (this.stompWindupTicks > 0) {
if (--this.stompWindupTicks == 0) {
this.performStomp(level, target);
}
} else if (this.wingGustWindupTicks > 0) {
if (--this.wingGustWindupTicks == 0) {
this.performWingGust(level, target);
}
} else {
double distance = target.distanceTo(this);
if (this.stompCooldownTicks <= 0 && (distance <= STOMP_RADIUS || skyIsMine)) {
this.stompCooldownTicks = skyIsMine ? STOMP_COOLDOWN_TICKS / 2 : STOMP_COOLDOWN_TICKS;
this.stompWindupTicks = STOMP_WINDUP_TICKS;
this.triggerAnim("body_action", "stomp");
this.playSound(AntarchySoundEvents.KING_ROAR.get(), 3.0F, 0.7F);
if (skyIsMine) {
this.getMoveControl().setWantedPosition(this.getX(), target.getY(), this.getZ(), 1.2D);
}
} else if (this.wingGustCooldownTicks <= 0 && distance <= WING_GUST_RADIUS && (forceFar || this.random.nextInt(3) == 0)) {
this.wingGustCooldownTicks = WING_GUST_COOLDOWN_TICKS;
this.wingGustWindupTicks = WING_GUST_WINDUP_TICKS;
this.triggerAnim("body_action", "wing_gust");
this.playSound(AntarchySoundEvents.KING_WING_FLAP.get(), 3.0F, 0.85F);
}
}
if (forceFar && target.distanceTo(this) < 18.0D) {
Vec3 away = this.position().subtract(target.position()).normalize().scale(24.0D);
double ax = this.getX() + away.x;
double az = this.getZ() + away.z;
double ay = this.groundYBelow(ax, az) + FLYING_PREFERRED_HOVER + 6.0D;
this.getMoveControl().setWantedPosition(ax, ay, az, 1.3D);
}
}
private void performStomp(ServerLevel level, LivingEntity target) {
DamageSource source = this.damageSources().mobAttack(this);
float base = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE) * 1.4F;
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
this.getBoundingBox().inflate(STOMP_RADIUS),
e -> e.isAlive() && e instanceof Player)) {
double distance = Math.max(1.0D, living.distanceTo(this));
float falloff = (float) Math.max(0.2D, 1.0D - distance / (STOMP_RADIUS + 6.0D));
living.hurt(source, base * falloff);
Vec3 push = living.position().subtract(this.position()).normalize().scale(2.4D * falloff);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.85D, push.z));
living.hasImpulse = true;
}
RoyalBlockDestruction.destroySphere(level, this,
new Vec3(this.getX(), this.getY(), this.getZ()), STOMP_RADIUS * 0.5D, 96, 80.0D, 0.15F);
level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, this.getX(), this.getY(), this.getZ(), 6, 4.0D, 0.5D, 4.0D, 0.0D);
this.playSound(AntarchySoundEvents.KING_STOMP.get(), 3.5F, 0.8F);
}
private void performWingGust(ServerLevel level, LivingEntity target) {
Vec3 flat = this.getForward().multiply(1.0D, 0.0D, 1.0D);
Vec3 forward = flat.lengthSqr() < 1.0E-4D
? target.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D).normalize()
: flat.normalize();
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
this.getBoundingBox().inflate(WING_GUST_RADIUS),
e -> e.isAlive() && e instanceof Player)) {
Vec3 toTarget = living.position().subtract(this.position()).multiply(1.0D, 0.0D, 1.0D);
if (toTarget.lengthSqr() < 1.0E-4D || forward.dot(toTarget.normalize()) < 0.1D) {
continue;
}
Vec3 push = toTarget.normalize().scale(3.2D);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.5D, push.z));
living.hasImpulse = true;
living.hurt(this.damageSources().mobAttack(this), 6.0F);
}
level.sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX() + forward.x * 6.0D, this.getY() + 4.0D, this.getZ() + forward.z * 6.0D, 12, 4.0D, 2.0D, 4.0D, 0.0D);
}
private void tickDecree(ServerLevel level, LivingEntity target) {
if (this.activeDecree == null) {
if (this.decreeCooldownTicks-- > 0) {
return;
}
this.activeDecreeTicks = 300;
this.activeDecree = this.pickDecree(target);
this.decreeRetreatPressure = false;
this.playSound(AntarchySoundEvents.KING_DECREE_CAST.get(), 3.0F, 0.9F + this.random.nextFloat() * 0.15F);
this.playSound(AntarchySoundEvents.KING_ROAR.get(), 3.0F, 0.8F + this.random.nextFloat() * 0.12F);
if (target instanceof ServerPlayer player) {
player.connection.send(new ClientboundSetTitlesAnimationPacket(5, 300, 10));
player.connection.send(new ClientboundSetTitleTextPacket(Component.translatable(this.activeDecree.translationKey())
.withStyle(style -> style.withColor(ChatFormatting.GOLD).withBold(true))));
player.connection.send(new ClientboundSetSubtitleTextPacket(Component.translatable(this.activeDecree.instructionKey())
.withStyle(style -> style.withColor(ChatFormatting.WHITE))));
}
}
int countdown = this.activeDecree.countdownTicks(target);
if (countdown > 0 && countdown <= 100 && this.tickCount % 20 == 0) {
level.playSound(null, target.blockPosition(), SoundEvents.BELL_BLOCK, SoundSource.HOSTILE, 0.8F, 1.0F + countdown / 500.0F);
}
this.activeDecree.apply(level, this, target);
if (this.activeDecree != null && --this.activeDecreeTicks <= 0) {
this.endDecree(target);
}
}
private RoyalDecree pickDecree(LivingEntity target) {
int totalWeight = 0;
int[] weights = new int[DECREES.size()];
for (int i = 0; i < DECREES.size(); i++) {
weights[i] = Math.max(1, DECREES.get(i).contextWeight(this, target));
totalWeight += weights[i];
}
int roll = this.random.nextInt(totalWeight);
for (int i = 0; i < DECREES.size(); i++) {
roll -= weights[i];
if (roll < 0) {
return DECREES.get(i);
}
}
return DECREES.get(this.random.nextInt(DECREES.size()));
}
private void endDecree(@Nullable LivingEntity target) {
if (target instanceof ServerPlayer player) {
player.connection.send(new ClientboundClearTitlesPacket(false));
}
if (this.activeDecree != null) {
this.activeDecree.onEnded();
}
this.activeDecree = null;
this.activeDecreeTicks = 0;
this.decreeRetreatPressure = false;
this.decreeCooldownTicks = AntarchySettings.royalDecreeCooldownTicks();
}
public void clearActiveDecree() {
this.clearActiveDecree(null);
}
public void clearActiveDecree(@Nullable LivingEntity target) {
this.endDecree(target);
}
public boolean blocksHealing() {
return this.activeDecree instanceof NoRespiteDecree;
}
public long ticksSincePlayerDamage(LivingEntity target) {
long last = this.lastPlayerDamageTime.getOrDefault(target.getUUID(), 0L);
return this.level().getGameTime() - last;
}
public void invokeJudgment(LivingEntity target) {
if (target == null || !target.isAlive()) return;
long now = this.level().getGameTime();
if (this.judgmentCooldowns.getOrDefault(target.getUUID(), 0L) <= now) {
this.judgmentCooldowns.put(target.getUUID(), now + 20L);
target.hurt(this.damageSources().magic(), 6.0F);
}
}
@Override
public boolean hurt(DamageSource source, float amount) {
if (!this.level().isClientSide && source.getEntity() instanceof LivingEntity attacker) {
this.lastPlayerDamageTime.put(attacker.getUUID(), this.level().getGameTime());
boolean projectile = source.getDirectEntity() instanceof Projectile;
this.bumpBehavior(projectile ? Behavior.RANGED : Behavior.HUGGING, 2);
if (this.activeDecree instanceof CloseQuartersDecree && projectile) {
this.invokeJudgment(attacker);
} else if (this.activeDecree instanceof ComeNoCloserDecree && !projectile) {
this.invokeJudgment(attacker);
} else if (this.activeDecree instanceof HandsOffTheCrownDecree) {
this.invokeJudgment(attacker);
}
}
return super.hurt(source, amount);
}
public static boolean blocksHealingAround(LivingEntity target) {
if (target.level().isClientSide) return false;
return target.level().getEntitiesOfClass(KingEntity.class, target.getBoundingBox().inflate(128.0D),
king -> king.isAlive() && king.blocksHealing()).stream().findFirst().isPresent();
}
}
@@ -1,9 +1,12 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.level.Level;
public class PrinceEntity extends RoyalMountEntity {
@@ -12,9 +15,20 @@ public class PrinceEntity extends RoyalMountEntity {
}
public static AttributeSupplier.Builder createAttributes() {
return RoyalMountEntity.createBaseAttributes()
.add(Attributes.MAX_HEALTH, 120.0D)
.add(Attributes.ATTACK_DAMAGE, 14.0D);
return RoyalMountEntity.createBaseAttributes(
AntarchySettings.princeHealth(), AntarchySettings.princeAttackDamage(),
AntarchySettings.princeMovementSpeed(), AntarchySettings.princeFlyingSpeed(),
AntarchySettings.princeArmor(), AntarchySettings.princeKnockbackResistance(),
AntarchySettings.princeFollowRange());
}
@Override
protected RoyalBeamElement boltElement(int lateralIndex) {
return switch (lateralIndex) {
case -1 -> RoyalBeamElement.FIRE;
case 1 -> RoyalBeamElement.ICE;
default -> RoyalBeamElement.LIGHTNING;
};
}
@Override
@@ -26,4 +40,29 @@ public class PrinceEntity extends RoyalMountEntity {
protected EntityType<? extends RoyalBoltEntity> boltType() {
return AntarchyObjects.ROYAL_BOLT.get();
}
@Override
protected SoundEvent idleSound() {
return AntarchySoundEvents.PRINCE_IDLE.get();
}
@Override
protected SoundEvent biteSound() {
return AntarchySoundEvents.PRINCE_BITE.get();
}
@Override
protected SoundEvent shootSound() {
return AntarchySoundEvents.PRINCE_SHOOT.get();
}
@Override
protected SoundEvent flySound() {
return AntarchySoundEvents.PRINCE_FLY.get();
}
@Override
protected SoundEvent stepSound() {
return AntarchySoundEvents.PRINCE_STEP.get();
}
}
@@ -1,9 +1,11 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.level.Level;
public class PrincessEntity extends RoyalMountEntity {
@@ -12,10 +14,11 @@ public class PrincessEntity extends RoyalMountEntity {
}
public static AttributeSupplier.Builder createAttributes() {
return RoyalMountEntity.createBaseAttributes()
.add(Attributes.MAX_HEALTH, 110.0D)
.add(Attributes.ATTACK_DAMAGE, 10.0D)
.add(Attributes.FLYING_SPEED, 0.95D);
return RoyalMountEntity.createBaseAttributes(
AntarchySettings.princessHealth(), AntarchySettings.princessAttackDamage(),
AntarchySettings.princessMovementSpeed(), AntarchySettings.princessFlyingSpeed(),
AntarchySettings.princessArmor(), AntarchySettings.princessKnockbackResistance(),
AntarchySettings.princessFollowRange());
}
@Override
@@ -27,4 +30,29 @@ public class PrincessEntity extends RoyalMountEntity {
protected EntityType<? extends RoyalBoltEntity> boltType() {
return AntarchyObjects.ROYAL_BOLT.get();
}
@Override
protected SoundEvent idleSound() {
return AntarchySoundEvents.PRINCESS_IDLE.get();
}
@Override
protected SoundEvent biteSound() {
return AntarchySoundEvents.PRINCESS_BITE.get();
}
@Override
protected SoundEvent shootSound() {
return AntarchySoundEvents.PRINCESS_SHOOT.get();
}
@Override
protected SoundEvent flySound() {
return AntarchySoundEvents.PRINCESS_FLY.get();
}
@Override
protected SoundEvent stepSound() {
return AntarchySoundEvents.PRINCESS_STEP.get();
}
}
@@ -3,16 +3,28 @@ package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.ManticoreEntity;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.BossEvent;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
@@ -22,15 +34,41 @@ import com.craisinlord.antarchy.content.entity.portal.DimensionalTearEntity;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.AABB;
public class QueenEntity extends RoyalBossEntity {
private static final String SUMMON_COOLDOWN_KEY = "ManticoreSummonCooldownTicks";
private static final int FAILED_SUMMON_RETRY_TICKS = 20;
private static final int POSITION_ATTEMPTS_PER_MANTICORE = 8;
private static final ResourceLocation ACCEL_SPEED_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "queen_royal_acceleration_speed");
private static final ResourceLocation ACCEL_FLY_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "queen_royal_acceleration_fly");
private static final DustParticleOptions ACCEL_DUST = new DustParticleOptions(new org.joml.Vector3f(1.0F, 0.15F, 0.15F), 2.0F);
private static final int GRAVITY_STOMP_COOLDOWN = 180;
private static final int TIME_FIELD_COOLDOWN = 260;
private static final int MOMENTUM_LOCK_COOLDOWN = 420;
private static final int MOMENTUM_LOCK_DURATION = 90;
private static final double MOMENTUM_LOCK_RADIUS = 26.0D;
private static final int CRUSHING_GRAVITY_COOLDOWN = 360;
private static final int CRUSHING_GRAVITY_DURATION = 120;
private static final double CRUSHING_GRAVITY_RADIUS = 18.0D;
private static final double CRUSHING_GRAVITY_STRENGTH = 0.12D;
private static final int ACCELERATION_COOLDOWN = 500;
private static final int ACCELERATION_DURATION = 140;
private static final int BLACK_HOLE_COOLDOWN = 340;
private int manticoreSummonCooldownTicks;
private int gravityStompCooldownTicks;
private int timeFieldCooldownTicks;
private int momentumLockCooldownTicks;
private int momentumLockTicks;
private int crushingGravityCooldownTicks;
private int crushingGravityTicks;
private int accelerationCooldownTicks;
private int accelerationTicks;
private int blackHoleCooldownTicks;
private int idleWanderCooldownTicks;
private final Map<UUID, Vec3> frozenVelocities = new HashMap<>();
public QueenEntity(EntityType<? extends QueenEntity> entityType, Level level) {
super(entityType, level);
@@ -101,14 +139,42 @@ public class QueenEntity extends RoyalBossEntity {
return RoyalBeamTerrainMode.DESTROY;
}
public static boolean isFieldImmune(Entity entity) {
return entity instanceof RoyalBossEntity
|| entity.getType().is(AntarchyTags.Entities.TIME_DILATION_IMMUNE);
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide || this.isDeadOrDying()) {
return;
}
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive() || !this.canAttack(target)) {
this.tickIdleWander();
}
this.tickManticoreSummon();
this.tickGravityAttacks();
this.tickQueenAbilities();
this.tickRoyalAcceleration();
this.tickMomentumLock();
this.tickCrushingGravity();
}
private void tickIdleWander() {
if (this.idleWanderCooldownTicks-- > 0) {
return;
}
this.idleWanderCooldownTicks = 80 + this.random.nextInt(80);
double angle = this.random.nextDouble() * Mth.TWO_PI;
double radius = 6.0D + this.random.nextDouble() * 14.0D;
double wx = this.getX() + Math.cos(angle) * radius;
double wz = this.getZ() + Math.sin(angle) * radius;
this.getMoveControl().setWantedPosition(wx, this.groundYBelow(wx, wz), wz, 0.7D);
}
private int cooldown(int base) {
return this.accelerationTicks > 0 ? Math.max(20, base / 2) : base;
}
private void tickManticoreSummon() {
@@ -116,11 +182,10 @@ public class QueenEntity extends RoyalBossEntity {
this.manticoreSummonCooldownTicks--;
return;
}
if (!(this.level() instanceof ServerLevel serverLevel)) {
return;
}
net.minecraft.world.entity.LivingEntity target = this.getTarget();
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive() || !this.canAttack(target)) {
return;
}
@@ -136,19 +201,17 @@ public class QueenEntity extends RoyalBossEntity {
int spawned = this.spawnManticoreTears(serverLevel, target, summonCount);
this.manticoreSummonCooldownTicks = spawned > 0
? Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks())
? this.cooldown(Math.max(20, AntarchySettings.queenManticoreSummonCooldownTicks()))
: FAILED_SUMMON_RETRY_TICKS;
if (spawned > 0) {
this.triggerAnim("body_action", "minion_spawn");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.8F + this.random.nextFloat() * 0.12F);
serverLevel.sendParticles(
net.minecraft.core.particles.ParticleTypes.PORTAL,
this.getX(), this.getY() + 2.0D, this.getZ(),
serverLevel.sendParticles(ParticleTypes.PORTAL, this.getX(), this.getY() + 2.0D, this.getZ(),
36, 3.0D, 2.0D, 3.0D, 0.15D);
}
}
private int spawnManticoreTears(ServerLevel level, net.minecraft.world.entity.LivingEntity target, int count) {
private int spawnManticoreTears(ServerLevel level, LivingEntity target, int count) {
int pairs = Math.max(1, Math.min(3, (count + 1) / 2));
int created = 0;
for (int i = 0; i < pairs; i++) {
@@ -169,38 +232,193 @@ public class QueenEntity extends RoyalBossEntity {
return created;
}
private void tickGravityAttacks() {
if (this.gravityStompCooldownTicks > 0) {
this.gravityStompCooldownTicks--;
}
if (this.timeFieldCooldownTicks > 0) {
this.timeFieldCooldownTicks--;
}
private void tickQueenAbilities() {
if (this.gravityStompCooldownTicks > 0) this.gravityStompCooldownTicks--;
if (this.timeFieldCooldownTicks > 0) this.timeFieldCooldownTicks--;
if (this.momentumLockCooldownTicks > 0) this.momentumLockCooldownTicks--;
if (this.crushingGravityCooldownTicks > 0) this.crushingGravityCooldownTicks--;
if (this.accelerationCooldownTicks > 0) this.accelerationCooldownTicks--;
if (this.blackHoleCooldownTicks > 0) this.blackHoleCooldownTicks--;
if (!(this.level() instanceof ServerLevel level)) {
return;
}
net.minecraft.world.entity.LivingEntity target = this.getTarget();
LivingEntity target = this.getTarget();
if (target == null || !target.isAlive()) {
return;
}
if (this.gravityStompCooldownTicks <= 0) {
this.gravityStompCooldownTicks = 180;
this.triggerAnim("body_action", "stomp");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.7F);
DamageSource source = this.damageSources().mobAttack(this);
for (net.minecraft.world.entity.LivingEntity living : level.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class,
this.getBoundingBox().inflate(18.0D), entity -> entity instanceof Player && entity.isAlive())) {
double distance = Math.max(1.0D, living.distanceTo(this));
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
living.hasImpulse = true;
Phase phase = this.phase();
boolean majorBusy = this.momentumLockTicks > 0 || this.crushingGravityTicks > 0;
boolean allowOverlap = phase != Phase.ONE;
if (this.gravityStompCooldownTicks <= 0 && (allowOverlap || !majorBusy)) {
this.gravityStompCooldownTicks = this.cooldown(GRAVITY_STOMP_COOLDOWN);
this.performGravityStomp(level, target);
}
if (this.timeFieldCooldownTicks <= 0 && (allowOverlap || !majorBusy)) {
this.timeFieldCooldownTicks = this.cooldown(TIME_FIELD_COOLDOWN);
double fieldRate = phase == Phase.THREE ? 0.1D : 0.4D;
TimeDilationApi.createField(level, target.position(), 12.0D, fieldRate, 140 + phase.ordinal() * 40);
this.triggerAnim("body_action", "wing_gust");
}
if (this.blackHoleCooldownTicks <= 0 && (allowOverlap || !majorBusy)) {
this.blackHoleCooldownTicks = this.cooldown(BLACK_HOLE_COOLDOWN);
this.castBlackHole(level, target);
}
if (this.momentumLockTicks <= 0 && this.momentumLockCooldownTicks <= 0 && (allowOverlap || !majorBusy)) {
this.startMomentumLock(level);
}
if (this.crushingGravityTicks <= 0 && this.crushingGravityCooldownTicks <= 0 && phase != Phase.ONE) {
this.crushingGravityCooldownTicks = CRUSHING_GRAVITY_COOLDOWN;
this.crushingGravityTicks = CRUSHING_GRAVITY_DURATION;
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.6F);
}
if (this.accelerationTicks <= 0 && this.accelerationCooldownTicks <= 0
&& (phase == Phase.THREE || (phase == Phase.TWO && this.random.nextInt(3) == 0))) {
this.startRoyalAcceleration();
}
}
private void performGravityStomp(ServerLevel level, LivingEntity target) {
this.triggerAnim("body_action", "stomp");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.7F);
DamageSource source = this.damageSources().mobAttack(this);
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class,
this.getBoundingBox().inflate(18.0D), entity -> entity instanceof Player && entity.isAlive())) {
double distance = Math.max(1.0D, living.distanceTo(this));
living.hurt(source, (float) (12.0D * Math.max(0.25D, 1.0D - distance / 24.0D)));
Vec3 push = living.position().subtract(this.position()).normalize().scale(1.3D);
living.setDeltaMovement(living.getDeltaMovement().add(push.x, 0.65D, push.z));
living.hasImpulse = true;
}
level.sendParticles(ParticleTypes.SONIC_BOOM, this.getX(), this.getY() + 1.0D, this.getZ(), 4, 2.0D, 0.5D, 2.0D, 0.0D);
}
private void castBlackHole(ServerLevel level, LivingEntity target) {
Vec3 anchor = target.position().add(
(this.random.nextDouble() - 0.5D) * 4.0D,
2.0D + this.random.nextDouble() * 2.0D,
(this.random.nextDouble() - 0.5D) * 4.0D);
RoyalBlackHoleEntity hole = RoyalBlackHoleEntity.create(level, anchor, this.getUUID());
level.addFreshEntity(hole);
this.triggerAnim("body_action", "wing_gust");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 1.15F);
level.sendParticles(ParticleTypes.REVERSE_PORTAL, anchor.x, anchor.y, anchor.z, 60, 1.0D, 1.0D, 1.0D, 0.4D);
}
private void startMomentumLock(ServerLevel level) {
this.momentumLockCooldownTicks = MOMENTUM_LOCK_COOLDOWN;
this.momentumLockTicks = MOMENTUM_LOCK_DURATION;
this.frozenVelocities.clear();
this.triggerAnim("body_action", "wing_gust");
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.0F, 0.5F);
for (Entity entity : level.getEntitiesOfClass(Entity.class,
this.getBoundingBox().inflate(MOMENTUM_LOCK_RADIUS))) {
if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) {
continue;
}
this.frozenVelocities.put(entity.getUUID(), entity.getDeltaMovement());
}
}
private void tickMomentumLock() {
if (this.momentumLockTicks <= 0) {
return;
}
this.momentumLockTicks--;
if (!(this.level() instanceof ServerLevel level)) {
return;
}
for (Map.Entry<UUID, Vec3> entry : this.frozenVelocities.entrySet()) {
Entity entity = level.getEntity(entry.getKey());
if (entity == null || !entity.isAlive()) {
continue;
}
if (this.momentumLockTicks <= 0) {
entity.setDeltaMovement(entry.getValue());
entity.hasImpulse = true;
} else {
entity.setDeltaMovement(Vec3.ZERO);
entity.fallDistance = 0.0F;
entity.hasImpulse = true;
if (this.tickCount % 6 == 0 && level instanceof ServerLevel serverLevel) {
serverLevel.sendParticles(ParticleTypes.END_ROD, entity.getX(), entity.getY() + entity.getBbHeight() * 0.5D, entity.getZ(),
2, 0.2D, 0.2D, 0.2D, 0.0D);
}
}
}
if (this.timeFieldCooldownTicks <= 0) {
this.timeFieldCooldownTicks = 260;
TimeDilationApi.createField(level, target.position(), 12.0D, 0.45D, 140);
this.triggerAnim("body_action", "wing_gust");
if (this.momentumLockTicks <= 0) {
this.frozenVelocities.clear();
}
}
private void tickCrushingGravity() {
if (this.crushingGravityTicks <= 0) {
return;
}
this.crushingGravityTicks--;
if (!(this.level() instanceof ServerLevel level)) {
return;
}
for (Entity entity : level.getEntitiesOfClass(Entity.class,
this.getBoundingBox().inflate(CRUSHING_GRAVITY_RADIUS))) {
if (entity == this || isFieldImmune(entity) || entity instanceof Player player && player.isCreative()) {
continue;
}
double downSign = AntarchyGravityApi.isGravityInverted(entity) ? 1.0D : -1.0D;
entity.setDeltaMovement(entity.getDeltaMovement().add(0.0D, downSign * CRUSHING_GRAVITY_STRENGTH, 0.0D));
entity.hasImpulse = true;
}
if (this.tickCount % 8 == 0) {
level.sendParticles(ACCEL_DUST, this.getX(), this.getY() + 1.0D, this.getZ(), 20,
CRUSHING_GRAVITY_RADIUS * 0.5D, 1.0D, CRUSHING_GRAVITY_RADIUS * 0.5D, 0.0D);
}
}
private void startRoyalAcceleration() {
this.accelerationCooldownTicks = ACCELERATION_COOLDOWN;
this.accelerationTicks = ACCELERATION_DURATION;
this.setRoyalAccelerated(true);
this.playSound(AntarchySoundEvents.QUEEN_ROAR.get(), 3.5F, 1.4F);
applyAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
applyAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
}
private static void applyAccelModifier(AttributeInstance attribute, ResourceLocation id) {
if (attribute == null || attribute.getModifier(id) != null) {
return;
}
attribute.addTransientModifier(new AttributeModifier(id, 0.9D, AttributeModifier.Operation.ADD_MULTIPLIED_BASE));
}
private static void removeAccelModifier(AttributeInstance attribute, ResourceLocation id) {
if (attribute != null) {
attribute.removeModifier(id);
}
}
private void tickRoyalAcceleration() {
if (this.accelerationTicks <= 0) {
if (this.isRoyalAccelerated()) {
this.setRoyalAccelerated(false);
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
}
return;
}
this.accelerationTicks--;
if (this.level() instanceof ServerLevel level && this.tickCount % 2 == 0) {
level.sendParticles(ACCEL_DUST,
this.getX() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(),
this.getY() + this.random.nextDouble() * this.getBbHeight(),
this.getZ() + (this.random.nextDouble() - 0.5D) * this.getBbWidth(),
6, 0.4D, 0.6D, 0.4D, 0.02D);
}
if (this.accelerationTicks <= 0) {
this.setRoyalAccelerated(false);
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
}
}
@@ -209,10 +427,12 @@ public class QueenEntity extends RoyalBossEntity {
if (!this.level().isClientSide && this.level() instanceof ServerLevel level) {
DimensionalTearEntity.discardQueenOwnedTears(level, this.getUUID());
}
removeAccelModifier(this.getAttribute(Attributes.MOVEMENT_SPEED), ACCEL_SPEED_ID);
removeAccelModifier(this.getAttribute(Attributes.FLYING_SPEED), ACCEL_FLY_ID);
super.die(damageSource);
}
private boolean spawnManticore(ServerLevel serverLevel, net.minecraft.world.entity.LivingEntity target) {
private boolean spawnManticore(ServerLevel serverLevel, LivingEntity target) {
double minimumDistance = Math.max(4.0D, this.getBbWidth() * 0.55D);
double maximumDistance = Math.max(minimumDistance + 1.0D, AntarchySettings.queenManticoreSummonRange());
for (int attempt = 0; attempt < POSITION_ATTEMPTS_PER_MANTICORE; attempt++) {
@@ -248,6 +468,10 @@ public class QueenEntity extends RoyalBossEntity {
tag.putInt(SUMMON_COOLDOWN_KEY, this.manticoreSummonCooldownTicks);
tag.putInt("GravityStompCooldownTicks", this.gravityStompCooldownTicks);
tag.putInt("TimeFieldCooldownTicks", this.timeFieldCooldownTicks);
tag.putInt("MomentumLockCooldownTicks", this.momentumLockCooldownTicks);
tag.putInt("CrushingGravityCooldownTicks", this.crushingGravityCooldownTicks);
tag.putInt("AccelerationCooldownTicks", this.accelerationCooldownTicks);
tag.putInt("BlackHoleCooldownTicks", this.blackHoleCooldownTicks);
}
@Override
@@ -256,5 +480,9 @@ public class QueenEntity extends RoyalBossEntity {
this.manticoreSummonCooldownTicks = Math.max(0, tag.getInt(SUMMON_COOLDOWN_KEY));
this.gravityStompCooldownTicks = Math.max(0, tag.getInt("GravityStompCooldownTicks"));
this.timeFieldCooldownTicks = Math.max(0, tag.getInt("TimeFieldCooldownTicks"));
this.momentumLockCooldownTicks = Math.max(0, tag.getInt("MomentumLockCooldownTicks"));
this.crushingGravityCooldownTicks = Math.max(0, tag.getInt("CrushingGravityCooldownTicks"));
this.accelerationCooldownTicks = Math.max(0, tag.getInt("AccelerationCooldownTicks"));
this.blackHoleCooldownTicks = Math.max(0, tag.getInt("BlackHoleCooldownTicks"));
}
}
@@ -0,0 +1,204 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.content.AntarchyObjects;
import java.util.UUID;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public class RoyalBlackHoleEntity extends Entity {
private static final EntityDataAccessor<Float> RADIUS =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Boolean> COLLAPSING =
SynchedEntityData.defineId(RoyalBlackHoleEntity.class, EntityDataSerializers.BOOLEAN);
private static final String OWNER_KEY = "OwnerUuid";
private static final String AGE_KEY = "Age";
private static final String ACTIVE_KEY = "ActiveTicks";
private static final double DEFAULT_RADIUS = 14.0D;
private static final int DEFAULT_ACTIVE_TICKS = 120;
private static final int COLLAPSE_TICKS = 12;
@Nullable
private UUID ownerId;
private int age;
private int activeTicks = DEFAULT_ACTIVE_TICKS;
public RoyalBlackHoleEntity(EntityType<? extends RoyalBlackHoleEntity> entityType, Level level) {
super(entityType, level);
this.noPhysics = true;
this.noCulling = true;
}
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, double radius, int activeTicks, @Nullable UUID ownerId) {
RoyalBlackHoleEntity hole = new RoyalBlackHoleEntity(AntarchyObjects.ROYAL_BLACK_HOLE.get(), level);
hole.setPos(center.x, center.y, center.z);
hole.setRadius(radius);
hole.activeTicks = Math.max(20, activeTicks);
hole.ownerId = ownerId;
return hole;
}
public static RoyalBlackHoleEntity create(ServerLevel level, Vec3 center, @Nullable UUID ownerId) {
return create(level, center, DEFAULT_RADIUS, DEFAULT_ACTIVE_TICKS, ownerId);
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
builder.define(RADIUS, (float) DEFAULT_RADIUS);
builder.define(COLLAPSING, false);
}
public void setRadius(double radius) {
this.entityData.set(RADIUS, (float) Math.max(1.0D, radius));
}
public double radius() {
return this.entityData.get(RADIUS);
}
public boolean isCollapsing() {
return this.entityData.get(COLLAPSING);
}
@Override
public void tick() {
super.tick();
if (this.level().isClientSide) {
this.spawnClientParticles();
return;
}
this.age++;
if (!this.isCollapsing()) {
this.applyPull();
if (this.age >= this.activeTicks) {
this.entityData.set(COLLAPSING, true);
this.age = 0;
}
return;
}
if (this.age >= COLLAPSE_TICKS) {
this.collapse();
this.discard();
}
}
private void applyPull() {
Vec3 center = this.position();
double radius = this.radius();
for (Entity entity : this.level().getEntitiesOfClass(Entity.class, new AABB(center, center).inflate(radius))) {
if (entity == this || this.isImmune(entity)) {
continue;
}
Vec3 toCenter = center.subtract(entity.position());
double distance = toCenter.length();
if (distance < 0.5D || distance > radius) {
continue;
}
double strength = 0.16D * (1.0D - distance / radius);
Vec3 pull = toCenter.scale(strength / distance);
entity.setDeltaMovement(entity.getDeltaMovement().scale(0.86D).add(pull));
entity.hasImpulse = true;
entity.fallDistance = 0.0F;
}
}
private void collapse() {
if (!(this.level() instanceof ServerLevel level)) {
return;
}
Vec3 center = this.position();
double radius = this.radius();
DamageSource source = this.ownerEntity() != null
? this.damageSources().mobAttack(this.ownerEntity())
: this.damageSources().magic();
for (LivingEntity living : level.getEntitiesOfClass(LivingEntity.class, new AABB(center, center).inflate(radius))) {
if (this.isImmune(living)) {
continue;
}
double distance = Math.max(1.0D, living.position().distanceTo(center));
float falloff = (float) Math.max(0.25D, 1.0D - distance / radius);
living.hurt(source, 26.0F * falloff);
Vec3 launch = living.position().subtract(center).normalize().scale(2.6D * falloff);
living.setDeltaMovement(launch.x, 1.1D * falloff + 0.4D, launch.z);
living.hasImpulse = true;
}
if (level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
RoyalBlockDestruction.destroySphere(level, this, center, radius * 0.4D, 160, 60.0D, 0.1F);
}
level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x, center.y, center.z, 8, 3.0D, 3.0D, 3.0D, 0.0D);
level.sendParticles(ParticleTypes.REVERSE_PORTAL, center.x, center.y, center.z, 120, 4.0D, 4.0D, 4.0D, 0.6D);
}
private boolean isImmune(Entity entity) {
if (entity instanceof RoyalBossEntity) {
return true;
}
if (this.ownerId != null && this.ownerId.equals(entity.getUUID())) {
return true;
}
return entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE);
}
@Nullable
private LivingEntity ownerEntity() {
if (this.ownerId == null || !(this.level() instanceof ServerLevel level)) {
return null;
}
return level.getEntity(this.ownerId) instanceof LivingEntity living ? living : null;
}
private void spawnClientParticles() {
Vec3 center = this.position();
for (int i = 0; i < 6; i++) {
double angle = this.random.nextDouble() * Math.PI * 2.0D;
double r = this.radius() * (0.3D + this.random.nextDouble() * 0.7D);
double px = center.x + Math.cos(angle) * r;
double pz = center.z + Math.sin(angle) * r;
double py = center.y + (this.random.nextDouble() - 0.5D) * 3.0D;
this.level().addParticle(ParticleTypes.PORTAL, px, py, pz,
(center.x - px) * 0.18D, (center.y - py) * 0.18D, (center.z - pz) * 0.18D);
}
}
@Override
public boolean isPickable() {
return false;
}
@Override
public boolean isIgnoringBlockTriggers() {
return true;
}
@Override
protected void readAdditionalSaveData(CompoundTag tag) {
this.setRadius(tag.getDouble("Radius"));
this.age = tag.getInt(AGE_KEY);
this.activeTicks = tag.contains(ACTIVE_KEY) ? tag.getInt(ACTIVE_KEY) : DEFAULT_ACTIVE_TICKS;
this.ownerId = tag.hasUUID(OWNER_KEY) ? tag.getUUID(OWNER_KEY) : null;
}
@Override
protected void addAdditionalSaveData(CompoundTag tag) {
tag.putDouble("Radius", this.radius());
tag.putInt(AGE_KEY, this.age);
tag.putInt(ACTIVE_KEY, this.activeTicks);
if (this.ownerId != null) {
tag.putUUID(OWNER_KEY, this.ownerId);
}
}
}
@@ -1,10 +1,19 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
import net.minecraft.world.level.Level;
@@ -12,10 +21,42 @@ import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
public class RoyalBoltEntity extends AbstractHurtingProjectile {
private static final EntityDataAccessor<Integer> ELEMENT =
SynchedEntityData.defineId(RoyalBoltEntity.class, EntityDataSerializers.INT);
public RoyalBoltEntity(EntityType<? extends RoyalBoltEntity> entityType, Level level) {
super(entityType, level);
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(ELEMENT, RoyalBeamElement.GENERIC.ordinal());
}
public void setElement(RoyalBeamElement element) {
this.entityData.set(ELEMENT, element.ordinal());
}
public RoyalBeamElement getElement() {
RoyalBeamElement[] values = RoyalBeamElement.values();
return values[Math.floorMod(this.entityData.get(ELEMENT), values.length)];
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putInt("Element", this.entityData.get(ELEMENT));
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
if (tag.contains("Element")) {
this.entityData.set(ELEMENT, tag.getInt("Element"));
}
}
@Override
protected double getDefaultGravity() {
return 0.02D;
@@ -33,7 +74,12 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
@Override
protected ParticleOptions getTrailParticle() {
return ParticleTypes.END_ROD;
return switch (this.getElement()) {
case FIRE -> ParticleTypes.FLAME;
case LIGHTNING -> ParticleTypes.ELECTRIC_SPARK;
case ICE -> ParticleTypes.SNOWFLAKE;
case GENERIC -> ParticleTypes.WITCH;
};
}
@Override
@@ -51,6 +97,28 @@ public class RoyalBoltEntity extends AbstractHurtingProjectile {
target.hurt(this.damageSources().mobProjectile(this, owner instanceof LivingEntity le ? le : null), damage);
if (target instanceof LivingEntity living) {
living.knockback(0.35D, this.getX() - target.getX(), this.getZ() - target.getZ());
switch (this.getElement()) {
case FIRE -> living.setRemainingFireTicks(Math.max(living.getRemainingFireTicks(), 80));
case ICE -> {
living.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 100, 1));
living.setTicksFrozen(Math.min(living.getTicksRequiredToFreeze() + 40, living.getTicksFrozen() + 140));
}
case LIGHTNING -> {
if (this.level() instanceof ServerLevel serverLevel) {
LightningBolt bolt = EntityType.LIGHTNING_BOLT.create(serverLevel);
if (bolt != null) {
bolt.moveTo(target.getX(), target.getY(), target.getZ());
bolt.setVisualOnly(true);
if (owner instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
bolt.setCause(serverPlayer);
}
serverLevel.addFreshEntity(bolt);
}
}
}
case GENERIC -> {
}
}
}
}
@@ -7,6 +7,7 @@ import com.craisinlord.antarchy.content.entity.multipart.MultipartPartDefinition
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamController;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamSettings;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamTerrainMode;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import java.util.List;
import net.minecraft.network.chat.Component;
import net.minecraft.network.syncher.EntityDataAccessor;
@@ -24,6 +25,8 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
@@ -87,10 +90,18 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
private static final double CRUSH_MAX_RESISTANCE = 60.0D;
private static final float CRUSH_DROP_CHANCE = 0.1F;
protected static final double FLYING_MIN_HOVER = 2.5D;
protected static final double FLYING_PREFERRED_HOVER = 6.0D;
protected static final double FLYING_MAX_HOVER_ABOVE_GROUND = 24.0D;
private static final EntityDataAccessor<Boolean> FIRING =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> HAS_BEAM_END =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Boolean> ACCELERATED =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.BOOLEAN);
private static final EntityDataAccessor<Integer> BEAM_ELEMENT =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Float> BEAM_END_X =
SynchedEntityData.defineId(RoyalBossEntity.class, EntityDataSerializers.FLOAT);
private static final EntityDataAccessor<Float> BEAM_END_Y =
@@ -115,6 +126,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
@Nullable
private RoyalHead activeBeamHead;
private int beamLoopSoundTicks;
private int beamHeadCursor;
@Nullable
private Entity[] multipartParts;
@@ -154,6 +166,17 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
protected abstract RoyalBeamTerrainMode royalBeamTerrainMode();
protected RoyalBeamElement royalBeamElement(@Nullable RoyalHead head) {
return RoyalBeamElement.GENERIC;
}
protected SoundEvent royalBeamStartSound(RoyalHead head) {
return this.royalBeamStartSound();
}
protected void tickRoyalBeamEffects(RoyalHead head, Vec3 start, Vec3 end) {
}
protected RoyalBeamTerrainMode royalBeamTerrainMode(@Nullable RoyalHead head) {
return this.royalBeamTerrainMode();
}
@@ -219,6 +242,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
super.defineSynchedData(builder);
builder.define(FIRING, false);
builder.define(HAS_BEAM_END, false);
builder.define(ACCELERATED, false);
builder.define(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal());
builder.define(BEAM_END_X, 0.0F);
builder.define(BEAM_END_Y, 0.0F);
builder.define(BEAM_END_Z, 0.0F);
@@ -273,6 +298,9 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
if (this.level().isClientSide) {
return;
}
if (this.isFlyingBoss() && !this.isDeadOrDying()) {
this.tickFlyingAltitude();
}
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
@@ -347,6 +375,10 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
this.royalBeamSettings(),
this.royalBeamTerrainMode(this.activeBeamHead),
this::setRoyalBeamEndPosition);
Vec3 beamEnd = this.beamController.beamEndPosition();
if (beamEnd != null) {
this.tickRoyalBeamEffects(this.activeBeamHead, this.headAnchor(this.activeBeamHead), beamEnd);
}
if (!this.beamController.isFiring()) {
this.stopRoyalBeam();
}
@@ -358,7 +390,8 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return;
}
Phase phase = this.phase();
for (RoyalHead head : this.heads) {
for (int offset = 0; offset < this.heads.length; offset++) {
RoyalHead head = this.heads[(this.beamHeadCursor + offset) % this.heads.length];
LivingEntity target = head.target(this.level());
if (!head.readyToAttack() || target == null || !this.canAttack(target)
|| this.headWithinBiteReach(head, target)) {
@@ -369,12 +402,14 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
head.startShoot(duration, cooldown);
head.setBeamActive(true);
this.activeBeamHead = head;
this.beamHeadCursor = (head.slot().partIndex()) % this.heads.length;
this.beamCooldownTicks = cooldown;
this.beamController.start(this.headAnchor(head), target.getEyePosition(), duration);
this.entityData.set(BEAM_ELEMENT, this.royalBeamElement(head).ordinal());
this.setFiringRoyalBeam(true);
this.triggerAnim(head.slot().controllerName(), "shoot");
this.beamLoopSoundTicks = 20;
this.playSound(this.royalBeamStartSound(), 3.0F, 1.0F);
this.playSound(this.royalBeamStartSound(head), 3.0F, 1.0F);
this.playSound(this.royalBeamShootSound(), 3.0F, 1.0F);
return;
}
@@ -386,6 +421,7 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
}
this.activeBeamHead = null;
this.beamController.stop();
this.entityData.set(BEAM_ELEMENT, RoyalBeamElement.GENERIC.ordinal());
this.playSound(this.royalBeamEndSound(), 2.5F, 1.0F);
this.setFiringRoyalBeam(false);
this.setRoyalBeamEndPosition(null);
@@ -437,11 +473,47 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
double reach = this.getBbWidth() * 0.5D + this.biteReach() * 0.5D;
double dx = target.getX() - this.getX();
double dz = target.getZ() - this.getZ();
if ((dx * dx + dz * dz) > reach * reach) {
double wantedY = this.isFlyingBoss() ? target.getY() + 3.0D : target.getY();
this.getMoveControl().setWantedPosition(target.getX(), wantedY, target.getZ(), this.isFlyingBoss() ? 1.1D : 1.0D);
} else {
boolean closeHorizontally = (dx * dx + dz * dz) <= reach * reach;
if (!this.isFlyingBoss()) {
if (closeHorizontally) {
this.getNavigation().stop();
} else {
this.getMoveControl().setWantedPosition(target.getX(), target.getY(), target.getZ(), 1.0D);
}
return;
}
double groundBelowTarget = this.groundYBelow(target.getX(), target.getZ());
double wantedY = Mth.clamp(target.getY() + 3.0D,
groundBelowTarget + FLYING_MIN_HOVER,
groundBelowTarget + FLYING_MAX_HOVER_ABOVE_GROUND);
boolean closeVertically = Math.abs(wantedY - this.getY()) <= 2.5D;
if (closeHorizontally && closeVertically) {
this.getNavigation().stop();
} else {
this.getMoveControl().setWantedPosition(target.getX(), wantedY, target.getZ(), 1.1D);
}
}
protected double groundYBelow(double x, double z) {
return this.level().getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
Mth.floor(x), Mth.floor(z));
}
private void tickFlyingAltitude() {
if (this.onGround()) {
this.setDeltaMovement(this.getDeltaMovement().add(0.0D, 0.2D, 0.0D));
}
double groundY = this.groundYBelow(this.getX(), this.getZ());
double ceiling = groundY + FLYING_MAX_HOVER_ABOVE_GROUND;
if (this.getY() > ceiling) {
double desiredY = groundY + FLYING_PREFERRED_HOVER;
this.getMoveControl().setWantedPosition(this.getX(), desiredY, this.getZ(), 1.0D);
Vec3 delta = this.getDeltaMovement();
if (delta.y > -0.1D) {
this.setDeltaMovement(delta.x, Math.max(-0.5D, delta.y - 0.1D), delta.z);
}
}
}
@@ -539,6 +611,29 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
super.die(damageSource);
}
@Override
@Nullable
public ItemEntity spawnAtLocation(ItemStack stack, float offsetY) {
if (stack.isEmpty() || this.level().isClientSide) {
return null;
}
double spread = this.getBbWidth() * 0.6D;
double height = 20.0D + this.random.nextDouble() * 10.0D;
ItemEntity itemEntity = new ItemEntity(
this.level(),
this.getX() + (this.random.nextDouble() - 0.5D) * spread,
this.getY() + height,
this.getZ() + (this.random.nextDouble() - 0.5D) * spread,
stack);
itemEntity.setDeltaMovement(
(this.random.nextDouble() - 0.5D) * 0.12D,
-0.2D - this.random.nextDouble() * 0.15D,
(this.random.nextDouble() - 0.5D) * 0.12D);
itemEntity.setDefaultPickUpDelay();
this.level().addFreshEntity(itemEntity);
return itemEntity;
}
@Override
protected void tickDeath() {
this.deathTime++;
@@ -569,6 +664,22 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return super.hurt(source, cappedAmount);
}
@Override
public boolean canBeAffected(net.minecraft.world.effect.MobEffectInstance effectInstance) {
if (effectInstance.is(com.craisinlord.antarchy.content.AntarchyObjects.INVERTED_EFFECT.get())) {
return false;
}
return super.canBeAffected(effectInstance);
}
public boolean isRoyalAccelerated() {
return this.entityData.get(ACCELERATED);
}
public void setRoyalAccelerated(boolean accelerated) {
this.entityData.set(ACCELERATED, accelerated);
}
public boolean isFiringRoyalBeam() {
return this.entityData.get(FIRING);
}
@@ -608,18 +719,25 @@ public abstract class RoyalBossEntity extends Monster implements GeoEntity, Mult
return new Vec3(x, y, z);
}
public RoyalBeamElement getRoyalBeamElement() {
int ordinal = this.entityData.get(BEAM_ELEMENT);
return ordinal >= 0 && ordinal < RoyalBeamElement.values().length
? RoyalBeamElement.values()[ordinal]
: RoyalBeamElement.GENERIC;
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "locomotion", 4, this::locomotionPredicate));
controllers.add(new AnimationController<>(this, "head_left", 0, RoyalBossEntity::headIdle)
.triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_1"))
.triggerableAnim("shoot", RawAnimation.begin().thenLoop("shoot_1")));
.triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_1")));
controllers.add(new AnimationController<>(this, "head_center", 0, RoyalBossEntity::headIdle)
.triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_2"))
.triggerableAnim("shoot", RawAnimation.begin().thenLoop("shoot_2")));
.triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_2")));
controllers.add(new AnimationController<>(this, "head_right", 0, RoyalBossEntity::headIdle)
.triggerableAnim("bite", RawAnimation.begin().thenPlay("bite_3"))
.triggerableAnim("shoot", RawAnimation.begin().thenLoop("shoot_3")));
.triggerableAnim("shoot", RawAnimation.begin().thenPlay("shoot_3")));
controllers.add(new AnimationController<>(this, "body_action", 0, this::bodyActionPredicate)
.triggerableAnim("stomp", RawAnimation.begin().thenPlay("stomp"))
.triggerableAnim("wing_gust", RawAnimation.begin().thenPlay("wing_gust"))
@@ -1,13 +1,18 @@
package com.craisinlord.antarchy.content.entity.royal;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchySoundEvents;
import com.craisinlord.antarchy.content.AntarchyTags;
import com.craisinlord.antarchy.content.entity.royal.beam.RoyalBeamElement;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
@@ -40,6 +45,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
@@ -91,6 +97,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
private boolean actionHit;
private int spitCooldown;
private int quirkTimer;
private int flapSoundTimer;
private boolean riderAscendPressed;
private boolean riderDescendPressed;
@@ -109,15 +116,17 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.xpReward = 20;
}
public static AttributeSupplier.Builder createBaseAttributes() {
public static AttributeSupplier.Builder createBaseAttributes(
double health, double attackDamage, double movementSpeed, double flyingSpeed,
double armor, double knockbackResistance, double followRange) {
return Mob.createMobAttributes()
.add(Attributes.MAX_HEALTH, 120.0D)
.add(Attributes.ATTACK_DAMAGE, 12.0D)
.add(Attributes.MOVEMENT_SPEED, 0.28D)
.add(Attributes.FLYING_SPEED, 0.9D)
.add(Attributes.FOLLOW_RANGE, 48.0D)
.add(Attributes.KNOCKBACK_RESISTANCE, 0.6D)
.add(Attributes.ARMOR, 10.0D)
.add(Attributes.MAX_HEALTH, health)
.add(Attributes.ATTACK_DAMAGE, attackDamage)
.add(Attributes.MOVEMENT_SPEED, movementSpeed)
.add(Attributes.FLYING_SPEED, flyingSpeed)
.add(Attributes.FOLLOW_RANGE, followRange)
.add(Attributes.KNOCKBACK_RESISTANCE, knockbackResistance)
.add(Attributes.ARMOR, armor)
.add(Attributes.STEP_HEIGHT, 1.5D);
}
@@ -125,6 +134,26 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
protected abstract EntityType<? extends RoyalBoltEntity> boltType();
protected abstract SoundEvent idleSound();
protected abstract SoundEvent biteSound();
protected abstract SoundEvent shootSound();
protected abstract SoundEvent flySound();
protected abstract SoundEvent stepSound();
@Override
protected SoundEvent getAmbientSound() {
return this.idleSound();
}
@Override
protected void playStepSound(BlockPos pos, BlockState state) {
this.playSound(this.stepSound(), 0.16F, 1.0F + (this.random.nextFloat() - 0.5F) * 0.2F);
}
@Override
protected PathNavigation createNavigation(Level level) {
GroundPathNavigation navigation = new GroundPathNavigation(this, level);
@@ -135,7 +164,12 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
@Override
protected void registerGoals() {
this.goalSelector.addGoal(0, new FloatGoal(this));
this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this));
this.goalSelector.addGoal(1, new SitWhenOrderedToGoal(this) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse();
}
});
this.goalSelector.addGoal(2, new RoyalMountSpitGoal(this));
this.goalSelector.addGoal(3, new net.minecraft.world.entity.ai.goal.MeleeAttackGoal(this, 1.2D, true) {
@Override
@@ -155,14 +189,35 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 0.8D) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isVehicle() && !RoyalMountEntity.this.isOrderedToSit() && super.canUse();
return !RoyalMountEntity.this.isBaby() && !RoyalMountEntity.this.isVehicle()
&& !RoyalMountEntity.this.isOrderedToSit() && super.canUse();
}
});
this.goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 12.0F));
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this));
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this));
this.targetSelector.addGoal(3, new HurtByTargetGoal(this).setAlertOthers());
this.goalSelector.addGoal(8, new RandomLookAroundGoal(this) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse();
}
});
this.targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse();
}
});
this.targetSelector.addGoal(2, new OwnerHurtTargetGoal(this) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse();
}
});
this.targetSelector.addGoal(3, new HurtByTargetGoal(this) {
@Override
public boolean canUse() {
return !RoyalMountEntity.this.isBaby() && super.canUse();
}
}.setAlertOthers());
}
@Override
@@ -197,6 +252,9 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.moveControl = flying ? this.flyingMoveControl : this.groundMoveControl;
this.navigation = flying ? this.flyingNavigation : this.groundNavigation;
this.setNoGravity(flying);
if (flying && !this.level().isClientSide) {
this.playSound(this.flySound(), 1.0F, 1.0F + (this.random.nextFloat() - 0.5F) * 0.2F);
}
}
}
@@ -226,15 +284,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
@Override
public boolean isFood(ItemStack stack) {
return isGoldenTreat(stack);
}
public static boolean isGoldenTreat(ItemStack stack) {
if (stack.isEmpty()) {
return false;
}
String path = net.minecraft.core.registries.BuiltInRegistries.ITEM.getKey(stack.getItem()).getPath();
return path.contains("gold") || path.contains("golden");
return stack.is(AntarchyTags.Items.ROYAL_MOUNT_FOOD);
}
@Override
@@ -327,6 +377,11 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.spitCooldown--;
}
if ((this.isFlying() || !this.onGround()) && --this.flapSoundTimer <= 0) {
this.flapSoundTimer = 24 + this.random.nextInt(12);
this.playSound(this.flySound(), 0.6F, 1.0F + (this.random.nextFloat() - 0.5F) * 0.3F);
}
if (this.actionTicks > 0) {
this.actionTicks--;
if (!this.actionHit && this.actionTicks <= ACTION_TICKS - BITE_HIT_TICK
@@ -385,6 +440,7 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
this.actionHit = false;
this.setAnimState(this.isFlying() ? ANIM_FLY_BITE : ANIM_BITE);
this.triggerAnim("main", this.isFlying() ? "fly_bite" : "bite");
this.playSound(this.biteSound(), 1.2F, 0.9F + this.random.nextFloat() * 0.2F);
}
private void applyBiteDamage() {
@@ -445,12 +501,17 @@ public abstract class RoyalMountEntity extends TamableAnimal implements GeoEntit
Vec3 dir = aim.add(right.scale(i * 0.14D)).normalize();
RoyalBoltEntity bolt = new RoyalBoltEntity(this.boltType(), this.level());
bolt.setOwner(this);
bolt.setElement(this.boltElement(i));
Vec3 spawn = this.getEyePosition().add(aim.scale(1.6D)).add(right.scale(i * 0.9D));
bolt.setPos(spawn.x, spawn.y, spawn.z);
bolt.shoot(dir.x, dir.y, dir.z, 1.4F, 1.0F);
this.level().addFreshEntity(bolt);
}
this.playSound(SoundEvents.BREEZE_SHOOT, 1.2F, 0.9F);
this.playSound(this.shootSound(), 1.2F, 0.9F + this.random.nextFloat() * 0.2F);
}
protected RoyalBeamElement boltElement(int lateralIndex) {
return RoyalBeamElement.GENERIC;
}
public boolean toggleMountedFlight(ServerPlayer player) {
@@ -5,6 +5,8 @@ import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
@@ -29,6 +31,8 @@ public final class RoyalBeamController {
private Vec3 beamEndPosition;
private int beamTicks;
private int terrainMutationsThisTick;
private static final int ICE_MUTATIONS_PER_TICK = 3;
private static final float ICE_IMPACT_RADIUS = 2.0F;
public RoyalBeamController(Mob owner) {
this.owner = owner;
@@ -101,7 +105,8 @@ public final class RoyalBeamController {
syncedEndPosition.accept(this.beamEndPosition);
if (!this.owner.level().isClientSide && this.beamEndPosition != null) {
marchDamageAndTerrain(shootFrom, this.beamEndPosition, settings, terrainMode);
boolean impactedBlock = hit.getType() == HitResult.Type.BLOCK;
marchDamageAndTerrain(shootFrom, this.beamEndPosition, settings, terrainMode, impactedBlock);
}
if (this.beamTicks <= 0) {
@@ -114,22 +119,32 @@ public final class RoyalBeamController {
Vec3 shootFrom,
Vec3 beamEnd,
RoyalBeamSettings settings,
RoyalBeamTerrainMode terrainMode
RoyalBeamTerrainMode terrainMode,
boolean impactedBlock
) {
Vec3 direction = beamEnd.subtract(shootFrom).normalize();
double distance = shootFrom.distanceTo(beamEnd);
DamageSource damageSource = this.owner.damageSources().mobAttack(this.owner);
Set<Integer> damagedThisTick = new HashSet<>();
boolean pathMutates = terrainMode == RoyalBeamTerrainMode.DESTROY;
for (double walked = settings.pathStep(); walked < Math.min(distance, settings.range()); walked += settings.pathStep()) {
Vec3 sample = shootFrom.add(direction.scale(walked));
hurtEntitiesAround(sample, settings.pathDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick);
if (shouldMutateTerrain(settings, walked)) {
if (pathMutates && shouldMutateTerrain(settings, walked)) {
mutateTerrainAround(sample, settings.pathTerrainRadius(), settings, terrainMode);
}
}
hurtEntitiesAround(beamEnd, settings.impactDamageRadius(), settings.damage(), settings.knockback(), damageSource, settings.requireLineOfSightForDamage(), damagedThisTick);
if (shouldMutateTerrain(settings, shootFrom.distanceTo(beamEnd))) {
if (!shouldMutateTerrain(settings, distance)) {
return;
}
if (terrainMode == RoyalBeamTerrainMode.BUILD_ICE) {
if (impactedBlock) {
mutateTerrainAround(beamEnd, ICE_IMPACT_RADIUS, settings, terrainMode);
}
} else {
mutateTerrainAround(beamEnd, settings.impactTerrainRadius(), settings, terrainMode);
}
}
@@ -233,8 +248,11 @@ public final class RoyalBeamController {
int maxZ = Mth.floor(center.z + radius);
float radiusSqr = radius * radius;
int cap = terrainMode == RoyalBeamTerrainMode.BUILD_ICE
? Math.min(ICE_MUTATIONS_PER_TICK, settings.maxTerrainMutationsPerTick())
: settings.maxTerrainMutationsPerTick();
for (BlockPos cursor : BlockPos.betweenClosed(minX, minY, minZ, maxX, maxY, maxZ)) {
if (this.terrainMutationsThisTick >= settings.maxTerrainMutationsPerTick()) {
if (this.terrainMutationsThisTick >= cap) {
return;
}
BlockPos pos = cursor.immutable();
@@ -274,13 +292,13 @@ public final class RoyalBeamController {
}
private void buildIceBlock(Level level, BlockPos pos, BlockState existing, RoyalBeamSettings settings) {
boolean softTarget = existing.isAir()
boolean replaceable = existing.isAir()
|| existing.canBeReplaced()
|| !existing.getFluidState().isEmpty()
|| existing.getBlock().getExplosionResistance() <= settings.maxTerrainResistance()
&& existing.getDestroySpeed(level, pos) >= 0.0F
&& level.getBlockEntity(pos) == null;
if (!softTarget) {
|| !existing.getFluidState().isEmpty();
if (!replaceable || level.getBlockEntity(pos) != null || existing.is(BlockTags.ICE)) {
return;
}
if (!touchesFreezableSurface(level, pos)) {
return;
}
@@ -289,6 +307,20 @@ public final class RoyalBeamController {
this.terrainMutationsThisTick++;
}
private static boolean touchesFreezableSurface(Level level, BlockPos pos) {
for (Direction direction : Direction.values()) {
BlockPos neighbor = pos.relative(direction);
BlockState state = level.getBlockState(neighbor);
if (state.is(BlockTags.ICE) || state.is(Blocks.SNOW_BLOCK)) {
continue;
}
if (state.isFaceSturdy(level, neighbor, direction.getOpposite())) {
return true;
}
}
return false;
}
private Block randomIceBlock() {
return switch (this.owner.getRandom().nextInt(5)) {
case 0 -> Blocks.BLUE_ICE;
@@ -0,0 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.beam;
public enum RoyalBeamElement {
GENERIC,
FIRE,
LIGHTNING,
ICE
}
@@ -2,8 +2,17 @@ package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class AdvanceDecree implements RoyalDecree {
public String name() { return "ADVANCE"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { Vec3 p = king.position().subtract(target.position()).normalize().scale(0.35D); target.setDeltaMovement(target.getDeltaMovement().add(p.x, 0.05D, p.z)); target.hasImpulse = true; }
private static final double REQUIRED_RANGE = 14.0D;
public String translationKey() { return "decree.antarchy.advance"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
king.setDecreeRetreatPressure(true);
if (target.distanceTo(king) > REQUIRED_RANGE) {
king.invokeJudgment(target);
}
}
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.KEEPING_FAR);
}
}
@@ -3,6 +3,10 @@ import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class CloseQuartersDecree implements RoyalDecree {
public String name() { return "CLOSE QUARTERS"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) <= 12.0D) target.hurt(king.damageSources().mobAttack(king), 4.0F); }
public String translationKey() { return "decree.antarchy.close_quarters"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.RANGED);
}
}
@@ -0,0 +1,12 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class ComeNoCloserDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.come_no_closer"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.HUGGING);
}
}
@@ -0,0 +1,12 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class DoNotRunDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.do_not_run"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.isSprinting()) king.invokeJudgment(target); }
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.SPRINTING);
}
}
@@ -0,0 +1,16 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class FightMeCowardDecree implements RoyalDecree {
public String name() { return "FIGHT ME, COWARD!"; }
public String translationKey() { return "decree.antarchy.fight_me_coward"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.distanceTo(king) > 28.0D) {
Vec3 pull = king.position().subtract(target.position()).normalize().scale(0.35D);
target.setDeltaMovement(target.getDeltaMovement().add(pull.x, 0.05D, pull.z));
target.hasImpulse = true;
}
}
}
@@ -0,0 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class HandsOffTheCrownDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.hands_off_the_crown"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
}
@@ -2,8 +2,16 @@ package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class KeepYourDistanceDecree implements RoyalDecree {
public String name() { return "KEEP YOUR DISTANCE"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) < 18.0D) { Vec3 p = target.position().subtract(king.position()).normalize().scale(0.8D); target.setDeltaMovement(target.getDeltaMovement().add(p.x, 0.2D, p.z)); target.hasImpulse = true; } }
private static final double INNER_RADIUS = 16.0D;
public String translationKey() { return "decree.antarchy.keep_your_distance"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.distanceTo(king) <= INNER_RADIUS) {
king.invokeJudgment(target);
}
}
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.HUGGING);
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class KneelDecree implements RoyalDecree {
private final Map<UUID, Integer> deadlines = new HashMap<>();
public String translationKey() { return "decree.antarchy.kneel"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
UUID id = target.getUUID();
if (target.isCrouching()) { deadlines.remove(id); king.clearActiveDecree(target); return; }
int deadline = deadlines.computeIfAbsent(id, ignored -> target.tickCount + 100);
if (target.tickCount >= deadline) { king.invokeJudgment(target); deadlines.put(id, target.tickCount + 100); }
}
@Override
public int countdownTicks(LivingEntity target) {
return this.deadlines.getOrDefault(target.getUUID(), target.tickCount + 100) - target.tickCount;
}
}
@@ -0,0 +1,8 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class NoRespiteDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.no_respite"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { }
}
@@ -3,6 +3,15 @@ import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class NoRetreatDecree implements RoyalDecree {
public String name() { return "NO RETREAT"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (target.distanceTo(king) > 28.0D) target.hurt(king.damageSources().mobAttack(king), 3.0F); }
private static final double ALLOWED_RANGE = 26.0D;
public String translationKey() { return "decree.antarchy.no_retreat"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.distanceTo(king) > ALLOWED_RANGE) {
king.invokeJudgment(target);
}
}
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.RETREATING);
}
}
@@ -5,6 +5,21 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public interface RoyalDecree {
String name();
String translationKey();
void apply(ServerLevel level, KingEntity king, LivingEntity target);
default int countdownTicks(LivingEntity target) {
return -1;
}
default String instructionKey() {
return this.translationKey() + ".subtitle";
}
default int contextWeight(KingEntity king, LivingEntity target) {
return 4;
}
default void onEnded() {
}
}
@@ -0,0 +1,13 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class ShowNoMercyDecree implements RoyalDecree {
private static final long IDLE_DAMAGE_LIMIT_TICKS = 80L;
public String translationKey() { return "decree.antarchy.show_no_mercy"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (king.ticksSincePlayerDamage(target) > IDLE_DAMAGE_LIMIT_TICKS) {
king.invokeJudgment(target);
}
}
}
@@ -0,0 +1,12 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class SkyIsMineDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.the_sky_is_mine"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) { if (!target.onGround() && !target.isPassenger()) king.invokeJudgment(target); }
@Override
public int contextWeight(KingEntity king, LivingEntity target) {
return 4 + king.behaviorScore(KingEntity.Behavior.AIRBORNE);
}
}
@@ -0,0 +1,12 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
public final class StandTallDecree implements RoyalDecree {
public String translationKey() { return "decree.antarchy.stand_tall"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
if (target.isCrouching()) {
king.invokeJudgment(target);
}
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.content.entity.royal.decree;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.phys.Vec3;
public final class StandYourGroundDecree implements RoyalDecree {
private static final double ALLOWED_DRIFT = 6.0D;
private final Map<UUID, Vec3> anchors = new HashMap<>();
public String translationKey() { return "decree.antarchy.stand_your_ground"; }
public void apply(ServerLevel level, KingEntity king, LivingEntity target) {
Vec3 anchor = this.anchors.computeIfAbsent(target.getUUID(), ignored -> target.position());
if (target.position().distanceToSqr(anchor) > ALLOWED_DRIFT * ALLOWED_DRIFT) {
king.invokeJudgment(target);
}
}
@Override
public void onEnded() {
this.anchors.clear();
}
}
@@ -9,6 +9,7 @@ import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
@@ -228,33 +229,19 @@ public class WindVortexEntity extends Entity {
VortexMode mode = this.getMode();
for (Entity entity : this.level().getEntities(this, area, this::canAffectEntity)) {
Vec3 relative = entity.position().subtract(this.position());
double axisDistance = relative.dot(basis.axis);
if (axisDistance < -1.0D || axisDistance > height + 2.0D) {
continue;
}
double along = Mth.clamp(axisDistance, 0.0D, height);
double currentProgress = height <= 0.0D ? 1.0D : along / height;
double captureProgress = mode == VortexMode.LENS_PULL ? 1.0D - currentProgress : currentProgress;
double shapeProgress = mode == VortexMode.LENS_PULL ? 1.0D - currentProgress : currentProgress;
double radius = this.radiusAt(shapeProgress);
Vec3 axisPoint = basis.axis.scale(axisDistance);
Vec3 radialVector = relative.subtract(axisPoint);
double captureRadius = radius * 1.35D + (entity instanceof Player ? 1.5D : 1.75D);
boolean requireLos = !this.travelling && mode == VortexMode.GATHER_RETURN;
if (radialVector.lengthSqr() > captureRadius * captureRadius
|| (requireLos && !this.hasClearPathTo(entity))) {
continue;
}
this.carriedProgress.putIfAbsent(entity.getUUID(), captureProgress);
this.captureEntity(entity, height, basis, mode);
}
if (!(this.level() instanceof ServerLevel serverLevel)) {
return;
}
for (ServerPlayer player : serverLevel.players()) {
if (area.intersects(player.getBoundingBox())) {
this.captureEntity(player, height, basis, mode);
}
}
Iterator<Map.Entry<UUID, Double>> iterator = this.carriedProgress.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, Double> entry = iterator.next();
@@ -314,8 +301,39 @@ public class WindVortexEntity extends Entity {
return radial.lengthSqr() < 1.0E-6D ? basis.sideA.scale(this.radiusAt(progress)) : radial;
}
private void captureEntity(Entity entity, double height, Basis basis, VortexMode mode) {
if (!this.canAffectEntity(entity)) {
return;
}
Vec3 relative = entity.position().subtract(this.position());
double axisDistance = relative.dot(basis.axis);
if (axisDistance < -1.0D || axisDistance > height + 2.0D) {
return;
}
double along = Mth.clamp(axisDistance, 0.0D, height);
double currentProgress = height <= 0.0D ? 1.0D : along / height;
double captureProgress = mode == VortexMode.LENS_PULL ? 1.0D - currentProgress : currentProgress;
double shapeProgress = mode == VortexMode.LENS_PULL ? 1.0D - currentProgress : currentProgress;
double radius = this.radiusAt(shapeProgress);
Vec3 axisPoint = basis.axis.scale(axisDistance);
Vec3 radialVector = relative.subtract(axisPoint);
double captureRadius = radius * 1.35D + (entity instanceof Player ? 1.5D : 1.75D);
boolean requireLos = !this.travelling && mode == VortexMode.GATHER_RETURN;
if (radialVector.lengthSqr() > captureRadius * captureRadius
|| (requireLos && !this.hasClearPathTo(entity))) {
return;
}
this.carriedProgress.putIfAbsent(entity.getUUID(), captureProgress);
}
private boolean canAffectEntity(Entity entity) {
if (!entity.isAlive() || entity.isSpectator() || entity.noPhysics || entity.getType().is(AntarchyTags.Entities.WIND_VORTEX_IMMUNE)) {
if (entity instanceof WindVortexEntity
|| !entity.isAlive()
|| entity.isSpectator()
|| entity.noPhysics
|| entity.getType().is(AntarchyTags.Entities.WIND_VORTEX_IMMUNE)) {
return false;
}
return true;
@@ -370,7 +388,12 @@ public class WindVortexEntity extends Entity {
entity.setOnGround(false);
}
if (entity instanceof Player) {
entity.move(net.minecraft.world.entity.MoverType.SELF, wanted.scale(0.65D));
Vec3 before = entity.position();
Vec3 playerCarry = wanted.scale(0.65D);
entity.move(net.minecraft.world.entity.MoverType.SELF, playerCarry);
if (entity.position().distanceToSqr(before) < 1.0E-6D && playerCarry.lengthSqr() > 1.0E-6D) {
entity.setPos(before.add(playerCarry));
}
entity.setOnGround(false);
}
entity.setDeltaMovement(updatedMovement);
@@ -0,0 +1,46 @@
package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.time.TimeDilationFieldSnapshot;
import io.netty.buffer.ByteBuf;
import java.util.List;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record TimeDilationFieldsPayload(List<TimeDilationFieldSnapshot> fields) implements CustomPacketPayload {
public static final Type<TimeDilationFieldsPayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "time_dilation_fields"));
private static final StreamCodec<ByteBuf, TimeDilationFieldSnapshot> FIELD_CODEC = StreamCodec.of(
(buf, field) -> {
buf.writeDouble(field.x());
buf.writeDouble(field.y());
buf.writeDouble(field.z());
buf.writeDouble(field.radius());
buf.writeDouble(field.rate());
buf.writeInt(field.age());
buf.writeInt(field.durationTicks());
},
buf -> new TimeDilationFieldSnapshot(
buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readDouble(),
buf.readDouble(), buf.readInt(), buf.readInt()
)
);
public static final StreamCodec<ByteBuf, TimeDilationFieldsPayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.collection(java.util.ArrayList::new, FIELD_CODEC, 128),
TimeDilationFieldsPayload::fields,
TimeDilationFieldsPayload::new
);
public TimeDilationFieldsPayload {
fields = List.copyOf(fields);
}
@Override
public Type<TimeDilationFieldsPayload> type() {
return TYPE;
}
}
@@ -2,17 +2,25 @@ package com.craisinlord.antarchy.content.network;
import com.craisinlord.antarchy.Antarchy;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
public record TimeDilationRatePayload(int entityId, double rate) implements CustomPacketPayload {
public record TimeDilationRatePayload(UUID entityUuid, double rate) implements CustomPacketPayload {
public static final Type<TimeDilationRatePayload> TYPE =
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "time_dilation_rate"));
private static final StreamCodec<ByteBuf, UUID> UUID_CODEC = StreamCodec.of(
(buf, uuid) -> {
buf.writeLong(uuid.getMostSignificantBits());
buf.writeLong(uuid.getLeastSignificantBits());
},
buf -> new UUID(buf.readLong(), buf.readLong())
);
public static final StreamCodec<ByteBuf, TimeDilationRatePayload> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
TimeDilationRatePayload::entityId,
UUID_CODEC,
TimeDilationRatePayload::entityUuid,
ByteBufCodecs.DOUBLE,
TimeDilationRatePayload::rate,
TimeDilationRatePayload::new
@@ -1,21 +1,32 @@
package com.craisinlord.antarchy.content.time;
import java.util.function.BiConsumer;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.List;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
public final class TimeDilationApi {
private static BiConsumer<ServerPlayer, Double> syncDispatcher = (player, rate) -> {
private static BiConsumer<Entity, Double> syncDispatcher = (entity, rate) -> {
};
private static BiConsumer<ServerPlayer, List<TimeDilationFieldSnapshot>> fieldSyncDispatcher = (player, fields) -> {
};
private static final Map<UUID, Double> SYNCED_CLIENT_RATES = new ConcurrentHashMap<>();
private TimeDilationApi() {
}
public static void setSyncDispatcher(BiConsumer<ServerPlayer, Double> dispatcher) {
syncDispatcher = dispatcher != null ? dispatcher : (player, rate) -> {
public static void setSyncDispatcher(BiConsumer<Entity, Double> dispatcher) {
syncDispatcher = dispatcher != null ? dispatcher : (entity, rate) -> {
};
}
public static void setFieldSyncDispatcher(BiConsumer<ServerPlayer, List<TimeDilationFieldSnapshot>> dispatcher) {
fieldSyncDispatcher = dispatcher != null ? dispatcher : (player, fields) -> {
};
}
@@ -27,6 +38,12 @@ public final class TimeDilationApi {
public static double getRate(Entity entity) {
if (entity instanceof TimeDilationEntityAccess access) {
if (entity.level().isClientSide) {
Double syncedRate = SYNCED_CLIENT_RATES.get(entity.getUUID());
if (syncedRate != null) {
return syncedRate;
}
}
return access.antarchy$getTimeDilationRate();
}
return TimeDilationMath.NORMAL_RATE;
@@ -37,19 +54,30 @@ public final class TimeDilationApi {
}
public static void applySyncedRate(Entity entity, double rate) {
applySyncedRate(entity.getUUID(), rate);
if (entity instanceof TimeDilationEntityAccess access) {
access.antarchy$setTimeDilationRate(rate);
}
}
public static void syncPlayerRate(ServerPlayer player, double rate) {
syncDispatcher.accept(player, TimeDilationMath.clampRate(rate));
public static void applySyncedRate(UUID entityUuid, double rate) {
double clampedRate = TimeDilationMath.clampRate(rate);
SYNCED_CLIENT_RATES.put(entityUuid, clampedRate);
}
public static void clearSyncedClientRates() {
SYNCED_CLIENT_RATES.clear();
}
public static void syncEntityRate(Entity entity, double rate) {
syncDispatcher.accept(entity, TimeDilationMath.clampRate(rate));
}
public static void syncFields(ServerPlayer player, List<TimeDilationFieldSnapshot> fields) {
fieldSyncDispatcher.accept(player, fields);
}
public static boolean consumeTick(Entity entity, String timerKey) {
if (entity instanceof Player) {
return true;
}
double rate = getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE || !(entity instanceof TimeDilationEntityAccess access)) {
return true;
@@ -10,4 +10,8 @@ public interface TimeDilationEntityAccess {
boolean antarchy$isInTimeDilationMove();
void antarchy$setInTimeDilationMove(boolean inMove);
boolean antarchy$isApplyingExternalImpulse();
void antarchy$setApplyingExternalImpulse(boolean applying);
}
@@ -0,0 +1,19 @@
package com.craisinlord.antarchy.content.time;
public record TimeDilationFieldSnapshot(
double x,
double y,
double z,
double radius,
double rate,
int age,
int durationTicks
) {
public double radiusSquared() {
return this.radius * this.radius;
}
public double effectiveRate() {
return TimeDilationMath.effectiveFieldRate(this.rate, this.age, this.durationTicks);
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.time;
public final class TimeDilationFieldSnapshotSampler {
private TimeDilationFieldSnapshotSampler() {
}
public static double sample(Iterable<TimeDilationFieldSnapshot> fields, double x, double y, double z) {
double combinedRate = TimeDilationMath.NORMAL_RATE;
for (TimeDilationFieldSnapshot field : fields) {
double dx = field.x() - x;
double dy = field.y() - y;
double dz = field.z() - z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
if (distanceSqr >= field.radiusSquared()) {
continue;
}
double falloff = TimeDilationMath.gaussianFalloff(Math.sqrt(distanceSqr), field.radius());
combinedRate *= TimeDilationMath.localFieldRate(field.effectiveRate(), falloff);
}
return TimeDilationMath.clampRate(combinedRate);
}
}
@@ -20,6 +20,7 @@ public final class TimeDilationManager {
public static void tickServer(MinecraftServer server) {
for (ServerLevel level : server.getAllLevels()) {
List<TimeDilationFieldEntity> fields = collectFields(level);
syncFieldSnapshots(level, fields);
if (fields.isEmpty()) {
if (DILATED_LEVELS.remove(level.dimension())) {
resetRates(level);
@@ -32,6 +33,18 @@ public final class TimeDilationManager {
}
}
private static void syncFieldSnapshots(ServerLevel level, List<TimeDilationFieldEntity> fields) {
List<TimeDilationFieldSnapshot> snapshots = fields.stream()
.map(field -> new TimeDilationFieldSnapshot(
field.getX(), field.getY(), field.getZ(), field.fieldRadius(), field.fieldRate(),
field.fieldAge(), field.fieldDurationTicks()))
.limit(128)
.toList();
for (ServerPlayer player : level.players()) {
TimeDilationApi.syncFields(player, snapshots);
}
}
private static List<TimeDilationFieldEntity> collectFields(ServerLevel level) {
List<TimeDilationFieldEntity> fields = new ArrayList<>();
for (Entity entity : level.getAllEntities()) {
@@ -47,11 +60,18 @@ public final class TimeDilationManager {
if (!(entity instanceof TimeDilationEntityAccess access) || entity instanceof TimeDilationFieldEntity) {
continue;
}
if (entity.getType().is(com.craisinlord.antarchy.content.AntarchyTags.Entities.TIME_DILATION_IMMUNE)) {
if (access.antarchy$getTimeDilationRate() < TimeDilationMath.NORMAL_RATE) {
access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE);
TimeDilationApi.syncEntityRate(entity, TimeDilationMath.NORMAL_RATE);
}
continue;
}
double previousRate = access.antarchy$getTimeDilationRate();
double rate = TimeDilationFieldSampler.sample(fields, entity.position());
access.antarchy$setTimeDilationRate(rate);
if (entity instanceof ServerPlayer player && Math.abs(previousRate - rate) > 0.001D) {
TimeDilationApi.syncPlayerRate(player, rate);
if (Math.abs(previousRate - rate) > 0.001D) {
TimeDilationApi.syncEntityRate(entity, rate);
}
}
}
@@ -65,9 +85,7 @@ public final class TimeDilationManager {
continue;
}
access.antarchy$setTimeDilationRate(TimeDilationMath.NORMAL_RATE);
if (entity instanceof ServerPlayer player) {
TimeDilationApi.syncPlayerRate(player, TimeDilationMath.NORMAL_RATE);
}
TimeDilationApi.syncEntityRate(entity, TimeDilationMath.NORMAL_RATE);
}
}
}
@@ -196,7 +196,7 @@ public class ElythiaBiomeSource extends BiomeSource {
}
if (biome.is(MOLEWORM_CAVES) && y > this.molewormCavesMaxQuartY) {
return resolveSurfaceFallback(x, z, sampler, this.molewormSurfaceFallbackCache, MOLEWORM_CAVES, GLIMMERING_POOLS);
return resolveSurfaceFallback(x, z, sampler, this.molewormSurfaceFallbackCache, MOLEWORM_CAVES, GLIMMERING_POOLS, CLOUD_SEA);
}
Climate.TargetPoint target = sampler.sample(x, this.seaLevelQuartY, z);
@@ -222,7 +222,7 @@ public class ElythiaBiomeSource extends BiomeSource {
biome = this.glimmeringPoolsHolder != null ? this.glimmeringPoolsHolder : biome;
}
if ((biome.is(PEACH_FOREST) || biome.is(GLIMMERING_POOLS)) && y <= this.undergroundGuardMaxQuartY) {
return resolveSurfaceFallback(x, z, sampler, this.peachForestSurfaceFallbackCache, PEACH_FOREST, GLIMMERING_POOLS);
return resolveSurfaceFallback(x, z, sampler, this.peachForestSurfaceFallbackCache, PEACH_FOREST, GLIMMERING_POOLS, CLOUD_SEA);
}
return biome;
@@ -320,6 +320,9 @@ public class ElythiaBiomeSource extends BiomeSource {
}
}
result = nullToConfiguredFallback(result, x, this.surfaceBiomeSampleQuartY, z, sampler);
if (result != null && result.is(CLOUD_SEA) && this.defaultLandHolder != null) {
result = this.defaultLandHolder;
}
cache.key = key;
cache.value = result;
@@ -384,7 +387,6 @@ public class ElythiaBiomeSource extends BiomeSource {
if (this.ouranwoodForestHolder != null) return this.ouranwoodForestHolder;
if (this.glimmeringPoolsHolder != null) return this.glimmeringPoolsHolder;
if (this.oceanHolder != null) return this.oceanHolder;
if (this.cloudSeaHolder != null) return this.cloudSeaHolder;
return this.firstConfiguredHolder;
}
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.content.worldgen.thoraxis;
import com.craisinlord.antarchy.config.AntarchySettings;
import com.craisinlord.antarchy.content.AntarchyObjects;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
@@ -22,6 +23,10 @@ public final class DimensionalTearFeature extends Feature<NoneFeatureConfigurati
@Override
public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
if (!AntarchySettings.dimensionalTearsSpawnNaturally()) {
return false;
}
WorldGenLevel level = context.level();
if (!(level instanceof ServerLevelAccessor)) {
return false;
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.entity.royal.KingEntity;
import net.minecraft.world.entity.LivingEntity;
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(LivingEntity.class)
public abstract class RoyalNoRespiteMixin {
@Inject(method = "heal", at = @At("HEAD"), cancellable = true)
private void antarchy$blockRoyalNoRespiteHealing(float amount, CallbackInfo ci) {
if (KingEntity.blocksHealingAround((LivingEntity) (Object) this)) {
ci.cancel();
}
}
}
@@ -0,0 +1,36 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.entity.projectile.AbstractArrow;
import org.objectweb.asm.Opcodes;
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.Redirect;
@Mixin(AbstractArrow.class)
public abstract class AbstractArrowActionTimeDilationMixin {
@Shadow private int life;
@Shadow protected int inGroundTime;
@Shadow public int shakeTime;
@Redirect(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;shakeTime:I", opcode = Opcodes.PUTFIELD))
private void antarchy$slowShakeTime(AbstractArrow arrow, int value) {
if (TimeDilationApi.consumeTick(arrow, "arrow_shake")) this.shakeTime = value;
}
@Redirect(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;inGroundTime:I", opcode = Opcodes.PUTFIELD, ordinal = 0))
private void antarchy$slowInGroundTime(AbstractArrow arrow, int value) {
if (TimeDilationApi.consumeTick(arrow, "arrow_in_ground")) this.inGroundTime = value;
}
@Redirect(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;inGroundTime:I", opcode = Opcodes.PUTFIELD, ordinal = 1))
private void antarchy$resetInGroundTime(AbstractArrow arrow, int value) {
this.inGroundTime = value;
}
@Redirect(method = "tickDespawn", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;life:I", opcode = Opcodes.PUTFIELD))
private void antarchy$slowLifetime(AbstractArrow arrow, int value) {
if (TimeDilationApi.consumeTick(arrow, "arrow_lifetime")) this.life = value;
}
}
@@ -0,0 +1,30 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(AbstractArrow.class)
public abstract class AbstractArrowMovementTimeDilationMixin {
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/phys/Vec3;add(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3;"))
private Vec3 antarchy$slowCollisionPath(Vec3 position, Vec3 movement) {
double rate = TimeDilationApi.getRate((AbstractArrow) (Object) this);
return position.add(rate >= TimeDilationMath.NORMAL_RATE ? movement : movement.scale(rate));
}
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;setPos(DDD)V"))
private void antarchy$slowPosition(AbstractArrow arrow, double x, double y, double z) {
double rate = Math.max(TimeDilationMath.MIN_RATE, TimeDilationApi.getRate(arrow));
if (rate >= TimeDilationMath.NORMAL_RATE) {
arrow.setPos(x, y, z);
return;
}
arrow.setPos(arrow.getX() + (x - arrow.getX()) * rate,
arrow.getY() + (y - arrow.getY()) * rate,
arrow.getZ() + (z - arrow.getZ()) * rate);
}
}
@@ -0,0 +1,16 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.entity.monster.Creeper;
import org.spongepowered.asm.mixin.Mixin;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(Creeper.class)
public abstract class CreeperTimeDilationMixin {
@ModifyReturnValue(method = "getSwellDir", at = @At("RETURN"))
private int antarchy$slowSwellProgress(int direction) {
Creeper creeper = (Creeper) (Object) this;
return TimeDilationApi.consumeTick(creeper, "creeper_swell") ? direction : 0;
}
}
@@ -0,0 +1,19 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
@Mixin(EndCrystal.class)
public abstract class EndCrystalTimeDilationMixin {
@WrapOperation(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/boss/enderdragon/EndCrystal;time:I", opcode = Opcodes.PUTFIELD))
private void antarchy$slowCrystalTime(EndCrystal crystal, int value, Operation<Void> original) {
if (TimeDilationApi.consumeTick(crystal, "end_crystal_time")) {
original.call(crystal, value);
}
}
}
@@ -15,7 +15,8 @@ public abstract class EntityDeltaMovementTimeDilationMixin {
private Vec3 antarchy$scaleTimeDilatedVelocityChange(Vec3 nextMovement) {
Entity entity = (Entity) (Object) this;
if (!(entity instanceof TimeDilationEntityAccess access)
|| access.antarchy$isInTimeDilationMove()) {
|| access.antarchy$isInTimeDilationMove()
|| access.antarchy$isApplyingExternalImpulse()) {
return nextMovement;
}
@@ -0,0 +1,32 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(Entity.class)
public abstract class EntityRotationTimeDilationMixin {
@ModifyVariable(method = "setYRot", at = @At("HEAD"), argsOnly = true)
private float antarchy$slowYaw(float nextYaw) {
Entity entity = (Entity) (Object) this;
double rate = TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return nextYaw;
}
return entity.getYRot() + Mth.wrapDegrees(nextYaw - entity.getYRot()) * (float) rate;
}
@ModifyVariable(method = "setXRot", at = @At("HEAD"), argsOnly = true)
private float antarchy$slowPitch(float nextPitch) {
Entity entity = (Entity) (Object) this;
double rate = TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return nextPitch;
}
return entity.getXRot() + (nextPitch - entity.getXRot()) * (float) rate;
}
}
@@ -16,6 +16,8 @@ public abstract class EntityTimeDilationStateMixin implements TimeDilationEntity
private final Map<String, Double> antarchy$timeDilationTimerProgress = new HashMap<>();
@Unique
private boolean antarchy$inTimeDilationMove;
@Unique
private boolean antarchy$applyingExternalImpulse;
@Override
public double antarchy$getTimeDilationRate() {
@@ -57,4 +59,14 @@ public abstract class EntityTimeDilationStateMixin implements TimeDilationEntity
public void antarchy$setInTimeDilationMove(boolean inMove) {
this.antarchy$inTimeDilationMove = inMove;
}
@Override
public boolean antarchy$isApplyingExternalImpulse() {
return this.antarchy$applyingExternalImpulse;
}
@Override
public void antarchy$setApplyingExternalImpulse(boolean applying) {
this.antarchy$applyingExternalImpulse = applying;
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(AbstractHurtingProjectile.class)
public abstract class HurtingProjectileMovementTimeDilationMixin {
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractHurtingProjectile;setPos(DDD)V"))
private void antarchy$slowPosition(AbstractHurtingProjectile projectile, double x, double y, double z) {
double rate = Math.max(TimeDilationMath.MIN_RATE, TimeDilationApi.getRate(projectile));
if (rate >= TimeDilationMath.NORMAL_RATE) {
projectile.setPos(x, y, z);
return;
}
projectile.setPos(projectile.getX() + (x - projectile.getX()) * rate,
projectile.getY() + (y - projectile.getY()) * rate,
projectile.getZ() + (z - projectile.getZ()) * rate);
}
}
@@ -0,0 +1,43 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.client.ClientTimeDilationTicker;
import net.minecraft.client.renderer.entity.ItemEntityRenderer;
import net.minecraft.world.entity.item.ItemEntity;
import org.spongepowered.asm.mixin.Mixin;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ItemEntityRenderer.class)
public abstract class ItemEntityRendererTimeDilationMixin {
@Unique
private static final ThreadLocal<ItemEntity> antarchy$renderingItem = new ThreadLocal<>();
@Inject(method = "render", at = @At("HEAD"))
private void antarchy$beginItemRender(ItemEntity item, float entityYaw, float partialTick,
com.mojang.blaze3d.vertex.PoseStack poseStack,
net.minecraft.client.renderer.MultiBufferSource buffer,
int packedLight, CallbackInfo ci) {
antarchy$renderingItem.set(item);
}
@Inject(method = "render", at = @At("RETURN"))
private void antarchy$endItemRender(ItemEntity item, float entityYaw, float partialTick,
com.mojang.blaze3d.vertex.PoseStack poseStack,
net.minecraft.client.renderer.MultiBufferSource buffer,
int packedLight, CallbackInfo ci) {
antarchy$renderingItem.remove();
}
@ModifyArg(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;sin(F)F"), index = 0)
private float antarchy$slowItemBob(float phase) {
ItemEntity item = antarchy$renderingItem.get();
if (item == null) {
return phase;
}
double rate = com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(item);
return rate >= 1.0D ? phase : phase * (float) rate;
}
}
@@ -0,0 +1,15 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.client.ClientTimeDilationTicker;
import net.minecraft.world.entity.item.ItemEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
@Mixin(ItemEntity.class)
public abstract class ItemEntityRotationTimeDilationMixin {
@ModifyReturnValue(method = "getSpin", at = @At("RETURN"))
private float antarchy$slowSpin(float spin) {
return ClientTimeDilationTicker.dilateItemSpin((ItemEntity) (Object) this, spin);
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public abstract class LivingEntityAttackCooldownTimeDilationMixin {
@Shadow
private int attackStrengthTicker;
@Inject(method = "tick", at = @At("RETURN"))
private void antarchy$slowAttackCooldown(CallbackInfo ci) {
LivingEntity entity = (LivingEntity) (Object) this;
if (!TimeDilationApi.consumeTick(entity, "living_attack_cooldown")) {
this.attackStrengthTicker = Math.max(0, this.attackStrengthTicker - 1);
}
}
}
@@ -0,0 +1,25 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationEntityAccess;
import net.minecraft.world.entity.LivingEntity;
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(LivingEntity.class)
public abstract class LivingEntityKnockbackTimeDilationMixin {
@Inject(method = "knockback", at = @At("HEAD"))
private void antarchy$beginExternalImpulse(double strength, double x, double z, CallbackInfo ci) {
if (this instanceof TimeDilationEntityAccess access) {
access.antarchy$setApplyingExternalImpulse(true);
}
}
@Inject(method = "knockback", at = @At("RETURN"))
private void antarchy$endExternalImpulse(double strength, double x, double z, CallbackInfo ci) {
if (this instanceof TimeDilationEntityAccess access) {
access.antarchy$setApplyingExternalImpulse(false);
}
}
}
@@ -4,11 +4,72 @@ import com.craisinlord.antarchy.content.client.ClientTimeDilationTicker;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.util.Mth;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(LivingEntityRenderer.class)
public abstract class LivingEntityRendererAnimationTimeDilationMixin {
private static final ThreadLocal<LivingEntity> antarchy$renderingEntity = new ThreadLocal<>();
@org.spongepowered.asm.mixin.injection.Inject(method = "render", at = @At("HEAD"))
private void antarchy$beginRender(LivingEntity entity, float entityYaw, float partialTick,
com.mojang.blaze3d.vertex.PoseStack poseStack,
net.minecraft.client.renderer.MultiBufferSource buffer,
int packedLight, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) {
antarchy$renderingEntity.set(entity);
}
@org.spongepowered.asm.mixin.injection.Inject(method = "render", at = @At("RETURN"))
private void antarchy$endRender(LivingEntity entity, float entityYaw, float partialTick,
com.mojang.blaze3d.vertex.PoseStack poseStack,
net.minecraft.client.renderer.MultiBufferSource buffer,
int packedLight, org.spongepowered.asm.mixin.injection.callback.CallbackInfo ci) {
antarchy$renderingEntity.remove();
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;rotLerp(FFF)F", ordinal = 0))
private float antarchy$slowBodyYaw(float partialTick, float previous, float current) {
LivingEntity entity = antarchy$renderingEntity.get();
return entity == null ? Mth.rotLerp(partialTick, previous, current)
: ClientTimeDilationTicker.dilateRotation(entity, previous, current, partialTick);
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;rotLerp(FFF)F", ordinal = 1))
private float antarchy$slowHeadYaw(float partialTick, float previous, float current) {
LivingEntity entity = antarchy$renderingEntity.get();
return entity == null ? Mth.rotLerp(partialTick, previous, current)
: ClientTimeDilationTicker.dilateRotation(entity, previous, current, partialTick);
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;lerp(FFF)F", ordinal = 0))
private float antarchy$slowHeadPitch(float partialTick, float previous, float current) {
LivingEntity entity = antarchy$renderingEntity.get();
return entity == null ? Mth.lerp(partialTick, previous, current)
: ClientTimeDilationTicker.dilatePitch(entity, previous, current, partialTick);
}
@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/WalkAnimationState;speed(F)F"))
private float antarchy$slowWalkAmount(net.minecraft.world.entity.WalkAnimationState state, float partialTick) {
LivingEntity entity = antarchy$renderingEntity.get();
float amount = state.speed(partialTick);
return entity == null ? amount : ClientTimeDilationTicker.dilateWalkAmount(entity, amount);
}
@org.spongepowered.asm.mixin.injection.Inject(method = "getOverlayCoords", at = @At("HEAD"), cancellable = true)
private static void antarchy$slowHurtOverlay(LivingEntity entity, float partialTick, CallbackInfoReturnable<Integer> cir) {
if (com.craisinlord.antarchy.content.time.TimeDilationApi.getRate(entity) < 1.0D
&& (entity.hurtTime > 0 || entity.deathTime > 0)) {
cir.setReturnValue(net.minecraft.client.renderer.texture.OverlayTexture.pack(1.0F, entity.deathTime > 0));
}
}
@ModifyReturnValue(method = "getAttackAnim", at = @At("RETURN"))
private float antarchy$slowAttackAnimation(float animation, LivingEntity entity, float partialTick) {
return ClientTimeDilationTicker.dilateAttackAnimation(entity, partialTick, animation);
}
@ModifyReturnValue(method = "getBob", at = @At("RETURN"))
private float antarchy$slowIdleAnimation(float bob, LivingEntity entity, float partialTick) {
return ClientTimeDilationTicker.dilateAnimationTime(entity, bob);
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.mixins.time;
import net.minecraft.client.particle.Particle;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(Particle.class)
public interface ParticlePositionAccessor {
@Accessor("x")
double antarchy$getX();
@Accessor("y")
double antarchy$getY();
@Accessor("z")
double antarchy$getZ();
}
@@ -0,0 +1,30 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.client.ClientTimeDilationTicker;
import net.minecraft.client.particle.PortalParticle;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;
@Mixin(PortalParticle.class)
public abstract class PortalParticleTimeDilationMixin {
@Unique
private double antarchy$timeProgress;
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void antarchy$slowPortalParticle(CallbackInfo ci) {
ParticlePositionAccessor position = (ParticlePositionAccessor) (Object) this;
double rate = ClientTimeDilationTicker.rateAt(position.antarchy$getX(), position.antarchy$getY(), position.antarchy$getZ());
if (rate >= 1.0D) {
return;
}
this.antarchy$timeProgress += rate;
if (this.antarchy$timeProgress < 1.0D) {
ci.cancel();
} else {
this.antarchy$timeProgress -= 1.0D;
}
}
}
@@ -1,65 +0,0 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.entity.projectile.AbstractHurtingProjectile;
import net.minecraft.world.entity.projectile.ThrowableProjectile;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;
@Mixin({AbstractArrow.class, ThrowableProjectile.class, AbstractHurtingProjectile.class})
public abstract class ProjectileMovementTimeDilationMixin {
@Unique
private Vec3 antarchy$preTickPos;
@Unique
private Vec3 antarchy$preTickDelta;
@Inject(method = "tick", at = @At("HEAD"))
private void antarchy$recordPreTickMotion(CallbackInfo ci) {
Entity entity = (Entity) (Object) this;
if (TimeDilationApi.getRate(entity) < TimeDilationMath.NORMAL_RATE) {
this.antarchy$preTickPos = entity.position();
this.antarchy$preTickDelta = entity.getDeltaMovement();
} else {
this.antarchy$preTickPos = null;
this.antarchy$preTickDelta = null;
}
}
@Inject(method = "tick", at = @At("RETURN"))
private void antarchy$scaleTickDisplacement(CallbackInfo ci) {
Vec3 pre = this.antarchy$preTickPos;
Vec3 preDelta = this.antarchy$preTickDelta;
this.antarchy$preTickPos = null;
this.antarchy$preTickDelta = null;
if (pre == null) {
return;
}
Entity entity = (Entity) (Object) this;
if (entity.isRemoved()) {
return;
}
double rate = TimeDilationApi.getRate(entity);
if (rate >= TimeDilationMath.NORMAL_RATE) {
return;
}
Vec3 actual = entity.position().subtract(pre);
if (actual.lengthSqr() < 1.0E-12D) {
return;
}
if (preDelta != null && actual.lengthSqr() + 1.0E-9D < preDelta.lengthSqr()) {
return;
}
Vec3 lerped = pre.add(actual.scale(rate));
entity.setPos(lerped.x, lerped.y, lerped.z);
}
}
@@ -0,0 +1,30 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.client.ClientTimeDilationTicker;
import net.minecraft.client.particle.ReversePortalParticle;
import org.spongepowered.asm.mixin.Mixin;
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.callback.CallbackInfo;
@Mixin(ReversePortalParticle.class)
public abstract class ReversePortalParticleTimeDilationMixin {
@Unique
private double antarchy$timeProgress;
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
private void antarchy$slowReversePortalParticle(CallbackInfo ci) {
ParticlePositionAccessor position = (ParticlePositionAccessor) (Object) this;
double rate = ClientTimeDilationTicker.rateAt(position.antarchy$getX(), position.antarchy$getY(), position.antarchy$getZ());
if (rate >= 1.0D) {
return;
}
this.antarchy$timeProgress += rate;
if (this.antarchy$timeProgress < 1.0D) {
ci.cancel();
} else {
this.antarchy$timeProgress -= 1.0D;
}
}
}
@@ -0,0 +1,23 @@
package com.craisinlord.antarchy.mixins.time;
import com.craisinlord.antarchy.content.time.TimeDilationApi;
import com.craisinlord.antarchy.content.time.TimeDilationMath;
import net.minecraft.world.entity.projectile.ThrowableProjectile;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ThrowableProjectile.class)
public abstract class ThrowableProjectileMovementTimeDilationMixin {
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/ThrowableProjectile;setPos(DDD)V"))
private void antarchy$slowPosition(ThrowableProjectile projectile, double x, double y, double z) {
double rate = Math.max(TimeDilationMath.MIN_RATE, TimeDilationApi.getRate(projectile));
if (rate >= TimeDilationMath.NORMAL_RATE) {
projectile.setPos(x, y, z);
return;
}
projectile.setPos(projectile.getX() + (x - projectile.getX()) * rate,
projectile.getY() + (y - projectile.getY()) * rate,
projectile.getZ() + (z - projectile.getZ()) * rate);
}
}
@@ -30,6 +30,7 @@
"GlowVinesUnderLeavesMixin",
"GrindstoneMenuMixin",
"LivingEntityJumpingAccessor",
"RoyalNoRespiteMixin",
"MantisClawWallClimbMixin",
"MilkBucketSizeResetMixin",
"MinecartItemMixin",
@@ -82,12 +83,20 @@
"time.EntityDeltaMovementTimeDilationMixin",
"time.EntityMoveTimeDilationMixin",
"time.EntityTimeDilationStateMixin",
"time.LivingEntityTimeDilationMixin",
"time.EntityRotationTimeDilationMixin",
"time.LivingEntityKnockbackTimeDilationMixin",
"time.CreeperTimeDilationMixin",
"time.EndCrystalTimeDilationMixin",
"time.LivingEntityTimeDilationMixin",
"time.LivingEntityAttackCooldownTimeDilationMixin",
"time.LookControlTimeDilationMixin",
"time.MeleeAttackGoalTimeDilationMixin",
"time.MobAttackTimeDilationMixin",
"time.PrimedTntTimeDilationMixin",
"time.ProjectileMovementTimeDilationMixin",
"time.AbstractArrowMovementTimeDilationMixin",
"time.ThrowableProjectileMovementTimeDilationMixin",
"time.HurtingProjectileMovementTimeDilationMixin",
"time.AbstractArrowActionTimeDilationMixin",
"time.RangedAttackGoalTimeDilationMixin",
"time.RangedBowAttackGoalTimeDilationMixin"
],
@@ -124,7 +133,12 @@
"gravity.client.FallingBlockRendererBlockEntityMixin",
"time.LivingEntityRendererAnimationTimeDilationMixin",
"time.ParticleTimeDilationMixin",
"time.SoundEngineTimeDilationMixin"
"time.PortalParticleTimeDilationMixin",
"time.ReversePortalParticleTimeDilationMixin",
"time.SoundEngineTimeDilationMixin",
"time.ItemEntityRotationTimeDilationMixin",
"time.ItemEntityRendererTimeDilationMixin",
"time.ParticlePositionAccessor"
],
"injectors": {
"defaultRequire": 1
@@ -549,7 +549,6 @@
"item.antarchy.queen_scale": "Queen Scale",
"item.antarchy.jerry_nucleus": "Jerry Nucleus",
"tooltip.antarchy.jerry_nucleus.hatch": "While holding, interact with any bug egg or turtle egg to hatch it instantly.",
"key.antarchy.dorrie_charge_jump": "Dorrie Charge Jump",
"key.antarchy.mount_special": "Mount: Special / Charge",
"key.antarchy.mount_flight_toggle": "Mount: Toggle Flight",
"item.antarchy.jumpy_bug_leg": "Jumpy Bug Leg",
@@ -800,6 +799,10 @@
"advancements.antarchy.cops_n_robbers.description": "This is just a nostalgic structure, no affiliation with Sky :)",
"advancements.antarchy.fallen_king_crown.title": "Chunks would load when I gave the word...",
"advancements.antarchy.fallen_king_crown.description": "Obtain the Fallen King\u0027s Crown.",
"advancements.antarchy.long_live_the_prince.title": "Long Live the Prince",
"advancements.antarchy.long_live_the_prince.description": "Hatch and tame a Prince.",
"advancements.antarchy.long_live_the_princess.title": "Long Live the Princess",
"advancements.antarchy.long_live_the_princess.description": "Hatch and tame a Princess.",
"advancements.antarchy.the_lab.title": "Hey everyone, Dan here",
"advancements.antarchy.the_lab.description": "Discover Dr. Trayaurus\u0027 lab.",
"advancements.antarchy.sweater_dreams.title": "Sweeter Dreams",
@@ -1036,6 +1039,16 @@
"subtitles.antarchy.queen_beam_end": "The Queen's beam fades",
"subtitles.antarchy.queen_black_hole_shoot": "The Queen tears open a singularity",
"subtitles.antarchy.queen_cast_shoot": "The Queen casts",
"subtitles.antarchy.prince_idle": "The Prince chitters",
"subtitles.antarchy.prince_bite": "The Prince bites",
"subtitles.antarchy.prince_shoot": "The Prince spits a volley",
"subtitles.antarchy.prince_fly": "The Prince's wings beat",
"subtitles.antarchy.prince_step": "The Prince's footfalls",
"subtitles.antarchy.princess_idle": "The Princess chitters",
"subtitles.antarchy.princess_bite": "The Princess bites",
"subtitles.antarchy.princess_shoot": "The Princess spits a volley",
"subtitles.antarchy.princess_fly": "The Princess's wings beat",
"subtitles.antarchy.princess_step": "The Princess's footfalls",
"subtitles.antarchy.stinky_fly": "Stinky flies buzz",
"item.minecraft.potion.effect.inversion": "Potion of Inversion",
"item.minecraft.potion.effect.long_inversion": "Potion of Inversion",
@@ -1167,6 +1180,7 @@
"entity.antarchy.king": "The King",
"entity.antarchy.queen": "The Queen",
"entity.antarchy.manticore": "Manticore",
"entity.antarchy.royal_black_hole": "Singularity",
"entity.antarchy.royal_bolt": "Royal Bolt",
"block.antarchy.prince_egg": "Prince Egg",
"block.antarchy.princess_egg": "Princess Egg",
@@ -1323,4 +1337,32 @@
"block.antarchy.chiseled_loam_bricks": "Chiseled Loam Bricks",
"item.antarchy.cooked_cheep": "Cooked Cheep",
"item.antarchy.cheep_bucket": "Bucket of Cheep"
,"decree.antarchy.the_sky_is_mine": "THE SKY IS MINE"
,"decree.antarchy.the_sky_is_mine.subtitle": "Stay grounded."
,"decree.antarchy.do_not_run": "DO NOT RUN"
,"decree.antarchy.do_not_run.subtitle": "Stop sprinting."
,"decree.antarchy.kneel": "KNEEL"
,"decree.antarchy.kneel.subtitle": "Crouch within five seconds."
,"decree.antarchy.close_quarters": "CLOSE QUARTERS"
,"decree.antarchy.close_quarters.subtitle": "Projectiles are forbidden."
,"decree.antarchy.come_no_closer": "COME NO CLOSER"
,"decree.antarchy.come_no_closer.subtitle": "Keep out of the King's inner radius."
,"decree.antarchy.fight_me_coward": "FIGHT ME, COWARD!"
,"decree.antarchy.fight_me_coward.subtitle": "Approach the King."
,"decree.antarchy.no_respite": "NO RESPITE"
,"decree.antarchy.no_respite.subtitle": "Healing is sealed."
,"decree.antarchy.no_retreat": "NO RETREAT"
,"decree.antarchy.no_retreat.subtitle": "Do not flee the King's presence."
,"decree.antarchy.keep_your_distance": "KEEP YOUR DISTANCE"
,"decree.antarchy.keep_your_distance.subtitle": "Stay out of the King's reach."
,"decree.antarchy.stand_tall": "STAND TALL"
,"decree.antarchy.stand_tall.subtitle": "Do not crouch before the King."
,"decree.antarchy.stand_your_ground": "STAND YOUR GROUND"
,"decree.antarchy.stand_your_ground.subtitle": "Do not stray from where you stand."
,"decree.antarchy.hands_off_the_crown": "HANDS OFF THE CROWN"
,"decree.antarchy.hands_off_the_crown.subtitle": "Strike the King and be judged."
,"decree.antarchy.advance": "ADVANCE"
,"decree.antarchy.advance.subtitle": "Close on the King or be judged."
,"decree.antarchy.show_no_mercy": "SHOW NO MERCY"
,"decree.antarchy.show_no_mercy.subtitle": "Keep dealing damage."
}
@@ -2940,5 +2940,83 @@
"name": "antarchy:mob/queen/fireball_shoot"
}
]
},
"prince_idle": {
"subtitle": "subtitles.antarchy.prince_idle",
"sounds": [
{ "name": "antarchy:mob/prince/idle1" },
{ "name": "antarchy:mob/prince/idle2" }
]
},
"prince_bite": {
"subtitle": "subtitles.antarchy.prince_bite",
"sounds": [
{ "name": "antarchy:mob/prince/bite1" }
]
},
"prince_shoot": {
"subtitle": "subtitles.antarchy.prince_shoot",
"sounds": [
{ "name": "antarchy:mob/prince/shoot1" }
]
},
"prince_fly": {
"subtitle": "subtitles.antarchy.prince_fly",
"sounds": [
{ "name": "antarchy:mob/prince/flt4" },
{ "name": "antarchy:mob/prince/fly1" },
{ "name": "antarchy:mob/prince/fly2" },
{ "name": "antarchy:mob/prince/fly3" },
{ "name": "antarchy:mob/prince/fly5" },
{ "name": "antarchy:mob/prince/fly6" }
]
},
"prince_step": {
"subtitle": "subtitles.antarchy.prince_step",
"sounds": [
{ "name": "antarchy:mob/prince/walk1" },
{ "name": "antarchy:mob/prince/walk2" },
{ "name": "antarchy:mob/prince/walk3" },
{ "name": "antarchy:mob/prince/walk4" }
]
},
"princess_idle": {
"subtitle": "subtitles.antarchy.princess_idle",
"sounds": [
{ "name": "antarchy:mob/princess/idle1" },
{ "name": "antarchy:mob/princess/idle2" }
]
},
"princess_bite": {
"subtitle": "subtitles.antarchy.princess_bite",
"sounds": [
{ "name": "antarchy:mob/princess/bite1" }
]
},
"princess_shoot": {
"subtitle": "subtitles.antarchy.princess_shoot",
"sounds": [
{ "name": "antarchy:mob/princess/shoot1" }
]
},
"princess_fly": {
"subtitle": "subtitles.antarchy.princess_fly",
"sounds": [
{ "name": "antarchy:mob/princess/flt4" },
{ "name": "antarchy:mob/princess/fly1" },
{ "name": "antarchy:mob/princess/fly2" },
{ "name": "antarchy:mob/princess/fly3" },
{ "name": "antarchy:mob/princess/fly5" },
{ "name": "antarchy:mob/princess/fly6" }
]
},
"princess_step": {
"subtitle": "subtitles.antarchy.princess_step",
"sounds": [
{ "name": "antarchy:mob/princess/walk1" },
{ "name": "antarchy:mob/princess/walk2" },
{ "name": "antarchy:mob/princess/walk3" },
{ "name": "antarchy:mob/princess/walk4" }
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more