fixed time dilations
king progress beams
This commit is contained in:
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;
|
||||
}
|
||||
|
||||
+43
-1
@@ -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"));
|
||||
|
||||
+12
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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++) {
|
||||
|
||||
+118
-46
@@ -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);
|
||||
}
|
||||
}
|
||||
+122
-22
@@ -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);
|
||||
}
|
||||
}
|
||||
+76
@@ -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;
|
||||
}
|
||||
}
|
||||
+95
-7
@@ -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);
|
||||
|
||||
+14
-4
@@ -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
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
+418
-46
@@ -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();
|
||||
}
|
||||
}
|
||||
+43
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
+33
-5
@@ -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();
|
||||
}
|
||||
}
|
||||
+262
-34
@@ -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"));
|
||||
}
|
||||
}
|
||||
+204
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
-1
@@ -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 -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+127
-9
@@ -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"))
|
||||
|
||||
+85
-24
@@ -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) {
|
||||
|
||||
+43
-11
@@ -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;
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.craisinlord.antarchy.content.entity.royal.beam;
|
||||
|
||||
public enum RoyalBeamElement {
|
||||
GENERIC,
|
||||
FIRE,
|
||||
LIGHTNING,
|
||||
ICE
|
||||
}
|
||||
+12
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -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) { }
|
||||
}
|
||||
+11
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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;
|
||||
}
|
||||
}
|
||||
+8
@@ -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) { }
|
||||
}
|
||||
+11
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -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() {
|
||||
}
|
||||
}
|
||||
+13
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -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();
|
||||
}
|
||||
}
|
||||
+46
-23
@@ -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);
|
||||
|
||||
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -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
|
||||
|
||||
+37
-9
@@ -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;
|
||||
|
||||
+4
@@ -10,4 +10,8 @@ public interface TimeDilationEntityAccess {
|
||||
boolean antarchy$isInTimeDilationMove();
|
||||
|
||||
void antarchy$setInTimeDilationMove(boolean inMove);
|
||||
|
||||
boolean antarchy$isApplyingExternalImpulse();
|
||||
|
||||
void antarchy$setApplyingExternalImpulse(boolean applying);
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
}
|
||||
+22
@@ -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);
|
||||
}
|
||||
}
|
||||
+23
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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);
|
||||
}
|
||||
}
|
||||
+16
@@ -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;
|
||||
}
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+32
@@ -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;
|
||||
}
|
||||
}
|
||||
+12
@@ -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;
|
||||
}
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+61
@@ -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);
|
||||
|
||||
+17
@@ -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();
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
-65
@@ -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);
|
||||
}
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user