fixed time dilations
king progress beams
This commit is contained in:
1 parent
af0b052b5c
commit
069d90d6e2
135 files changed
+3366
-466
No files matched your search
+22
@@ -20,16 +20,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) {
|
||||
@@ -37,8 +41,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();
|
||||
@@ -68,11 +83,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();
|
||||
}
|
||||
|
||||
@@ -153,6 +173,7 @@ public class AntNestBlockEntity extends BlockEntity {
|
||||
protected void saveAdditional(CompoundTag tag) {
|
||||
super.saveAdditional(tag);
|
||||
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());
|
||||
@@ -164,6 +185,7 @@ public class AntNestBlockEntity extends BlockEntity {
|
||||
public void load(CompoundTag tag) {
|
||||
super.load(tag);
|
||||
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++) {
|
||||
|
||||
@@ -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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+121
-21
@@ -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;
|
||||
}
|
||||
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();
|
||||
|
||||
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 -> {
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
private static void drawBeamQuad(VertexConsumer vertices, PoseStack poseStack, Vec3 start, Vec3 end, Vec3 side) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
+94
-6
@@ -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;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
+414
-42
@@ -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()));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
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];
|
||||
}
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
+252
-24
@@ -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,26 +232,59 @@ 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;
|
||||
|
||||
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 (net.minecraft.world.entity.LivingEntity living : level.getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class,
|
||||
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)));
|
||||
@@ -196,11 +292,133 @@ public class QueenEntity extends RoyalBossEntity {
|
||||
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);
|
||||
}
|
||||
if (this.timeFieldCooldownTicks <= 0) {
|
||||
this.timeFieldCooldownTicks = 260;
|
||||
TimeDilationApi.createField(level, target.position(), 12.0D, 0.45D, 140);
|
||||
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
"GlowVinesUnderLeavesMixin",
|
||||
"GrindstoneMenuMixin",
|
||||
"LivingEntityJumpingAccessor",
|
||||
"RoyalNoRespiteMixin",
|
||||
"MantisClawWallClimbMixin",
|
||||
"MilkBucketSizeResetMixin",
|
||||
"MinecartItemMixin",
|
||||
@@ -82,12 +83,20 @@
|
||||
"time.EntityDeltaMovementTimeDilationMixin",
|
||||
"time.EntityMoveTimeDilationMixin",
|
||||
"time.EntityTimeDilationStateMixin",
|
||||
"time.EntityRotationTimeDilationMixin",
|
||||
"time.LivingEntityKnockbackTimeDilationMixin",
|
||||
"time.CreeperTimeDilationMixin",
|
||||
"time.EndCrystalTimeDilationMixin",
|
||||
"time.LivingEntityTimeDilationMixin",
|
||||
"time.LivingEntityAttackCooldownTimeDilationMixin",
|
||||
"time.LookControlTimeDilationMixin",
|
||||
"time.MeleeAttackGoalTimeDilationMixin",
|
||||
"time.MobAttackTimeDilationMixin",
|
||||
"time.PrimedTntTimeDilationMixin",
|
||||
"time.ProjectileMovementTimeDilationMixin",
|
||||
"time.AbstractArrowMovementTimeDilationMixin",
|
||||
"time.ThrowableProjectileMovementTimeDilationMixin",
|
||||
"time.HurtingProjectileMovementTimeDilationMixin",
|
||||
"time.AbstractArrowActionTimeDilationMixin",
|
||||
"time.RangedAttackGoalTimeDilationMixin",
|
||||
"time.RangedBowAttackGoalTimeDilationMixin"
|
||||
],
|
||||
@@ -124,7 +133,12 @@
|
||||
"gravity.client.FallingBlockRendererBlockEntityMixin",
|
||||
"time.LivingEntityRendererAnimationTimeDilationMixin",
|
||||
"time.ParticleTimeDilationMixin",
|
||||
"time.SoundEngineTimeDilationMixin"
|
||||
"time.PortalParticleTimeDilationMixin",
|
||||
"time.ReversePortalParticleTimeDilationMixin",
|
||||
"time.SoundEngineTimeDilationMixin",
|
||||
"time.ItemEntityRotationTimeDilationMixin",
|
||||
"time.ItemEntityRendererTimeDilationMixin",
|
||||
"time.ParticlePositionAccessor"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
||||
@@ -549,7 +549,6 @@
|
||||
"item.antarchy.queen_scale": "Queen Scale",
|
||||
"item.antarchy.jerry_nucleus": "Jerry Nucleus",
|
||||
"tooltip.antarchy.jerry_nucleus.hatch": "While holding, interact with any bug egg or turtle egg to hatch it instantly.",
|
||||
"key.antarchy.dorrie_charge_jump": "Dorrie Charge Jump",
|
||||
"key.antarchy.mount_special": "Mount: Special / Charge",
|
||||
"key.antarchy.mount_flight_toggle": "Mount: Toggle Flight",
|
||||
"item.antarchy.jumpy_bug_leg": "Jumpy Bug Leg",
|
||||
@@ -800,6 +799,10 @@
|
||||
"advancements.antarchy.cops_n_robbers.description": "This is just a nostalgic structure, no affiliation with Sky :)",
|
||||
"advancements.antarchy.fallen_king_crown.title": "Chunks would load when I gave the word...",
|
||||
"advancements.antarchy.fallen_king_crown.description": "Obtain the Fallen King\u0027s Crown.",
|
||||
"advancements.antarchy.long_live_the_prince.title": "Long Live the Prince",
|
||||
"advancements.antarchy.long_live_the_prince.description": "Hatch and tame a Prince.",
|
||||
"advancements.antarchy.long_live_the_princess.title": "Long Live the Princess",
|
||||
"advancements.antarchy.long_live_the_princess.description": "Hatch and tame a Princess.",
|
||||
"advancements.antarchy.the_lab.title": "Hey everyone, Dan here",
|
||||
"advancements.antarchy.the_lab.description": "Discover Dr. Trayaurus\u0027 lab.",
|
||||
"advancements.antarchy.sweater_dreams.title": "Sweeter Dreams",
|
||||
@@ -1036,6 +1039,16 @@
|
||||
"subtitles.antarchy.queen_beam_end": "The Queen's beam fades",
|
||||
"subtitles.antarchy.queen_black_hole_shoot": "The Queen tears open a singularity",
|
||||
"subtitles.antarchy.queen_cast_shoot": "The Queen casts",
|
||||
"subtitles.antarchy.prince_idle": "The Prince chitters",
|
||||
"subtitles.antarchy.prince_bite": "The Prince bites",
|
||||
"subtitles.antarchy.prince_shoot": "The Prince spits a volley",
|
||||
"subtitles.antarchy.prince_fly": "The Prince's wings beat",
|
||||
"subtitles.antarchy.prince_step": "The Prince's footfalls",
|
||||
"subtitles.antarchy.princess_idle": "The Princess chitters",
|
||||
"subtitles.antarchy.princess_bite": "The Princess bites",
|
||||
"subtitles.antarchy.princess_shoot": "The Princess spits a volley",
|
||||
"subtitles.antarchy.princess_fly": "The Princess's wings beat",
|
||||
"subtitles.antarchy.princess_step": "The Princess's footfalls",
|
||||
"subtitles.antarchy.stinky_fly": "Stinky flies buzz",
|
||||
"item.minecraft.potion.effect.inversion": "Potion of Inversion",
|
||||
"item.minecraft.potion.effect.long_inversion": "Potion of Inversion",
|
||||
@@ -1167,6 +1180,7 @@
|
||||
"entity.antarchy.king": "The King",
|
||||
"entity.antarchy.queen": "The Queen",
|
||||
"entity.antarchy.manticore": "Manticore",
|
||||
"entity.antarchy.royal_black_hole": "Singularity",
|
||||
"entity.antarchy.royal_bolt": "Royal Bolt",
|
||||
"block.antarchy.prince_egg": "Prince Egg",
|
||||
"block.antarchy.princess_egg": "Princess Egg",
|
||||
@@ -1323,4 +1337,32 @@
|
||||
"block.antarchy.chiseled_loam_bricks": "Chiseled Loam Bricks",
|
||||
"item.antarchy.cooked_cheep": "Cooked Cheep",
|
||||
"item.antarchy.cheep_bucket": "Bucket of Cheep"
|
||||
,"decree.antarchy.the_sky_is_mine": "THE SKY IS MINE"
|
||||
,"decree.antarchy.the_sky_is_mine.subtitle": "Stay grounded."
|
||||
,"decree.antarchy.do_not_run": "DO NOT RUN"
|
||||
,"decree.antarchy.do_not_run.subtitle": "Stop sprinting."
|
||||
,"decree.antarchy.kneel": "KNEEL"
|
||||
,"decree.antarchy.kneel.subtitle": "Crouch within five seconds."
|
||||
,"decree.antarchy.close_quarters": "CLOSE QUARTERS"
|
||||
,"decree.antarchy.close_quarters.subtitle": "Projectiles are forbidden."
|
||||
,"decree.antarchy.come_no_closer": "COME NO CLOSER"
|
||||
,"decree.antarchy.come_no_closer.subtitle": "Keep out of the King's inner radius."
|
||||
,"decree.antarchy.fight_me_coward": "FIGHT ME, COWARD!"
|
||||
,"decree.antarchy.fight_me_coward.subtitle": "Approach the King."
|
||||
,"decree.antarchy.no_respite": "NO RESPITE"
|
||||
,"decree.antarchy.no_respite.subtitle": "Healing is sealed."
|
||||
,"decree.antarchy.no_retreat": "NO RETREAT"
|
||||
,"decree.antarchy.no_retreat.subtitle": "Do not flee the King's presence."
|
||||
,"decree.antarchy.keep_your_distance": "KEEP YOUR DISTANCE"
|
||||
,"decree.antarchy.keep_your_distance.subtitle": "Stay out of the King's reach."
|
||||
,"decree.antarchy.stand_tall": "STAND TALL"
|
||||
,"decree.antarchy.stand_tall.subtitle": "Do not crouch before the King."
|
||||
,"decree.antarchy.stand_your_ground": "STAND YOUR GROUND"
|
||||
,"decree.antarchy.stand_your_ground.subtitle": "Do not stray from where you stand."
|
||||
,"decree.antarchy.hands_off_the_crown": "HANDS OFF THE CROWN"
|
||||
,"decree.antarchy.hands_off_the_crown.subtitle": "Strike the King and be judged."
|
||||
,"decree.antarchy.advance": "ADVANCE"
|
||||
,"decree.antarchy.advance.subtitle": "Close on the King or be judged."
|
||||
,"decree.antarchy.show_no_mercy": "SHOW NO MERCY"
|
||||
,"decree.antarchy.show_no_mercy.subtitle": "Keep dealing damage."
|
||||
}
|
||||
@@ -2940,5 +2940,83 @@
|
||||
"name": "antarchy:mob/queen/fireball_shoot"
|
||||
}
|
||||
]
|
||||
},
|
||||
"prince_idle": {
|
||||
"subtitle": "subtitles.antarchy.prince_idle",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/prince/idle1" },
|
||||
{ "name": "antarchy:mob/prince/idle2" }
|
||||
]
|
||||
},
|
||||
"prince_bite": {
|
||||
"subtitle": "subtitles.antarchy.prince_bite",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/prince/bite1" }
|
||||
]
|
||||
},
|
||||
"prince_shoot": {
|
||||
"subtitle": "subtitles.antarchy.prince_shoot",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/prince/shoot1" }
|
||||
]
|
||||
},
|
||||
"prince_fly": {
|
||||
"subtitle": "subtitles.antarchy.prince_fly",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/prince/flt4" },
|
||||
{ "name": "antarchy:mob/prince/fly1" },
|
||||
{ "name": "antarchy:mob/prince/fly2" },
|
||||
{ "name": "antarchy:mob/prince/fly3" },
|
||||
{ "name": "antarchy:mob/prince/fly5" },
|
||||
{ "name": "antarchy:mob/prince/fly6" }
|
||||
]
|
||||
},
|
||||
"prince_step": {
|
||||
"subtitle": "subtitles.antarchy.prince_step",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/prince/walk1" },
|
||||
{ "name": "antarchy:mob/prince/walk2" },
|
||||
{ "name": "antarchy:mob/prince/walk3" },
|
||||
{ "name": "antarchy:mob/prince/walk4" }
|
||||
]
|
||||
},
|
||||
"princess_idle": {
|
||||
"subtitle": "subtitles.antarchy.princess_idle",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/princess/idle1" },
|
||||
{ "name": "antarchy:mob/princess/idle2" }
|
||||
]
|
||||
},
|
||||
"princess_bite": {
|
||||
"subtitle": "subtitles.antarchy.princess_bite",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/princess/bite1" }
|
||||
]
|
||||
},
|
||||
"princess_shoot": {
|
||||
"subtitle": "subtitles.antarchy.princess_shoot",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/princess/shoot1" }
|
||||
]
|
||||
},
|
||||
"princess_fly": {
|
||||
"subtitle": "subtitles.antarchy.princess_fly",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/princess/flt4" },
|
||||
{ "name": "antarchy:mob/princess/fly1" },
|
||||
{ "name": "antarchy:mob/princess/fly2" },
|
||||
{ "name": "antarchy:mob/princess/fly3" },
|
||||
{ "name": "antarchy:mob/princess/fly5" },
|
||||
{ "name": "antarchy:mob/princess/fly6" }
|
||||
]
|
||||
},
|
||||
"princess_step": {
|
||||
"subtitle": "subtitles.antarchy.princess_step",
|
||||
"sounds": [
|
||||
{ "name": "antarchy:mob/princess/walk1" },
|
||||
{ "name": "antarchy:mob/princess/walk2" },
|
||||
{ "name": "antarchy:mob/princess/walk3" },
|
||||
{ "name": "antarchy:mob/princess/walk4" }
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user