commit
66e5a77adb
597 files changed
+74648
-18090
No files matched your search
+13
-12
@@ -7,26 +7,27 @@ dependencies {
|
||||
// modImplementation("curse.maven:create-${create_curseforge_id}:${create_curseforge_file_id}")
|
||||
// modImplementation("curse.maven:selene-499980:4754433")
|
||||
compileOnly("curse.maven:intapi-${project.intapi_curseforge_id}:${project.intapi_curseforge_file_id_neoforge}")
|
||||
compileOnly("curse.maven:create-${project.create_curseforge_id_neoforge}:${project.create_curseforge_file_id_neoforge}")
|
||||
compileOnly("curse.maven:marvel-${project.marvel_curseforge_id}:${project.marvel_curseforge_file_id}")
|
||||
compileOnly("curse.maven:stellaris-${project.stellaris_curseforge_id}:${project.stellaris_curseforge_file_id}")
|
||||
compileOnly("curse.maven:aperture-${project.aperture_curseforge_id}:${project.aperture_curseforge_file_id}")
|
||||
compileOnly("curse.maven:inventory_pets-${project.inventory_pets_curseforge_id}:${project.inventory_pets_curseforge_file_id}")
|
||||
compileOnly("curse.maven:cataclysm-${project.cataclysm_curseforge_id}:${project.cataclysm_curseforge_file_id}")
|
||||
compileOnly("curse.maven:iaf-${project.iaf_curseforge_id}:${project.iaf_curseforge_file_id}")
|
||||
compileOnly("curse.maven:backpack-${project.backpack_curseforge_id}:${project.backpack_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:create-${project.create_curseforge_id_neoforge}:${project.create_curseforge_file_id_neoforge}")
|
||||
// compileOnly("curse.maven:marvel-${project.marvel_curseforge_id}:${project.marvel_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:stellaris-${project.stellaris_curseforge_id}:${project.stellaris_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:aperture-${project.aperture_curseforge_id}:${project.aperture_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:inventory_pets-${project.inventory_pets_curseforge_id}:${project.inventory_pets_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:cataclysm-${project.cataclysm_curseforge_id}:${project.cataclysm_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:iaf-${project.iaf_curseforge_id}:${project.iaf_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:backpack-${project.backpack_curseforge_id}:${project.backpack_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:regions_unexplored-${project.regions_unexplored_curseforge_id}:${project.regions_unexplored_curseforge_file_id}")
|
||||
// compileOnly("curse.maven:geckolib-${project.geckolib_curseforge_id}:${project.geckolib_curseforge_file_id}")
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.1+1.21.1-neoforge.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.1+1.21.1-fabric.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/infinity-2.7.1+1.21.1-neoforge-sources.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/gravity-api-1.0.7-1.20.1.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/Cloro's Gravity Gun Mod (Release 1.3.1).jar"))
|
||||
// compileOnly(files("$rootDir/infinite_dimensions_jars/Cloro's Gravity Gun Mod (Release 1.3.1).jar"))
|
||||
// compileOnly(files("$rootDir/infinite_dimensions_jars/antarchy-1.0.0+1.21.1-neoforge.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/VanillaBackport-neoforge-1.21.1-1.1.7.6.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/soul-fire-d-neoforge-1.21-6.1.0.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/prometheus-neoforge-1.21-1.2.5.jar"))
|
||||
// compileOnly(files("$rootDir/infinite_dimensions_jars/VanillaBackport-neoforge-1.21.1-1.1.7.6.jar"))
|
||||
// compileOnly(files("$rootDir/infinite_dimensions_jars/soul-fire-d-neoforge-1.21-6.1.0.jar"))
|
||||
// compileOnly(files("$rootDir/infinite_dimensions_jars/prometheus-neoforge-1.21-1.2.5.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/sable-neoforge-1.21.1-2.0.3.jar"))
|
||||
compileOnly(files("$rootDir/infinite_dimensions_jars/antarchy-1.0.0+1.21.1-neoforge.jar"))
|
||||
compileOnly("software.bernie.geckolib:geckolib-neoforge-${minecraft_version}:${geckolib_version}")
|
||||
//
|
||||
// compileOnly group: 'org.ow2.asm', name: 'asm-tree', version: '9.4'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.craisinlord.antarchy;
|
||||
|
||||
import com.craisinlord.antarchy.content.AttributeRangeInit;
|
||||
import com.craisinlord.integrated_api.events.lifecycle.ServerGoingToStartEvent;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -10,6 +11,7 @@ public class Antarchy {
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
public static void init() {
|
||||
AttributeRangeInit.apply();
|
||||
ServerGoingToStartEvent.EVENT.addListener(Antarchy::serverAboutToStart);
|
||||
}
|
||||
private static void serverAboutToStart(final ServerGoingToStartEvent event) {
|
||||
|
||||
@@ -5,7 +5,7 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class AntarchySettings {
|
||||
private static boolean disableInfinityBookPortalCreation = false;
|
||||
private static boolean disableInfinityBookPortalCreation = true;
|
||||
private static boolean easterBunnyEnabled = true;
|
||||
private static int easterBunnyNaturalSpawnChancePercent = 1;
|
||||
private static boolean rainbowAntsLeadToInfinityDimensions = true;
|
||||
@@ -31,7 +31,7 @@ public final class AntarchySettings {
|
||||
private static double waspHealth = 16.0D;
|
||||
private static double waspAttackDamage = 4.0D;
|
||||
private static double waspMovementSpeed = 0.39D;
|
||||
private static double bomberHealth = 10.0D;
|
||||
private static double bomberHealth = 15.0D;
|
||||
private static double bomberAttackDamage = 4.0D;
|
||||
private static double bomberExplosionDamage = 8.0D;
|
||||
private static double bomberExplosionRadius = 4.0D;
|
||||
@@ -90,6 +90,8 @@ public final class AntarchySettings {
|
||||
private static ResourceKey<Level> rainbowAntNonInfinityFallbackDimension = dimensionKey("antarchy:elythia");
|
||||
private static double krakenHealth = 1500.0D;
|
||||
private static double krakenAttackDamage = 45.0D;
|
||||
private static double octopusBombHealth = 100.0D;
|
||||
private static double octopusBombAttackDamage = 16.0D;
|
||||
private static double brutalflyHealth = 400.0D;
|
||||
private static double brutalflySwipeDamage = 15.0D;
|
||||
private static double brutalflySpitDamage = 5.0D;
|
||||
@@ -98,6 +100,12 @@ public final class AntarchySettings {
|
||||
private static double mantisMovementSpeed = 0.42D;
|
||||
private static double mantisFlyingSpeed = 0.77D;
|
||||
private static boolean mantisIgnoreLightLevel = false;
|
||||
private static double alphaMantisHealth = 500.0D;
|
||||
private static double alphaMantisAttackDamage = 18.0D;
|
||||
private static double alphaMantisMovementSpeed = 0.42D;
|
||||
private static double alphaMantisFlyingSpeed = 0.77D;
|
||||
private static int alphaMantisSummonIntervalTicks = 300;
|
||||
private static int alphaMantisMaxMinions = 4;
|
||||
private static double krakenProjectileDamageTakenMultiplier = 0.5D;
|
||||
private static boolean krakenSquidSpawnEnabled = true;
|
||||
private static boolean krakenMassSpawnEnabled = true;
|
||||
@@ -143,12 +151,12 @@ public final class AntarchySettings {
|
||||
|
||||
private static double nightmareSwordBaseDamage = 15.0D;
|
||||
|
||||
private static double nightmareSwordScalingFactor = 1.0D;
|
||||
private static double nightmareSwordScalingFactor = 1.5D;
|
||||
private static boolean basiliskPetrifyingGazeEnabled = true;
|
||||
private static int basiliskSpawnMaxLightLevel = 5;
|
||||
private static double basiliskHealth = 150.0D;
|
||||
private static double basiliskAttackDamage = 36.0D;
|
||||
private static double basiliskMovementSpeed = 0.30D;
|
||||
private static double basiliskMovementSpeed = 0.225D;
|
||||
private static double basiliskArmor = 6.0D;
|
||||
private static double basiliskKnockbackResistance = 0.5D;
|
||||
private static double basiliskFollowRange = 28.0D;
|
||||
@@ -225,23 +233,23 @@ public final class AntarchySettings {
|
||||
private static final int reverieAmbientTargetMaxAgeTicks = 40;
|
||||
private static final int reverieAmbientPulseIntervalMinTicks = 12;
|
||||
private static final int reverieAmbientPulseIntervalMaxTicks = 19;
|
||||
private static double emperorScorpionHealth = 100.0D;
|
||||
private static double emperorScorpionAttackDamage = 12.0D;
|
||||
private static double emperorScorpionHealth = 400.0D;
|
||||
private static double emperorScorpionAttackDamage = 28.0D;
|
||||
private static double emperorScorpionMovementSpeed = 0.32D;
|
||||
private static double emperorScorpionArmor = 6.0D;
|
||||
private static double emperorScorpionKnockbackResistance = 0.6D;
|
||||
private static double emperorScorpionFollowRange = 32.0D;
|
||||
private static double emperorScorpionArmor = 25.0D;
|
||||
private static double emperorScorpionKnockbackResistance = 0.8D;
|
||||
private static double emperorScorpionFollowRange = 40.0D;
|
||||
private static int emperorScorpionXpReward = 25;
|
||||
private static int emperorScorpionClawAnimTicks = 12;
|
||||
private static int emperorScorpionClawHitTick = 6;
|
||||
private static int emperorScorpionClawCooldownTicks = 16;
|
||||
private static int emperorScorpionStingAnimTicks = 18;
|
||||
private static int emperorScorpionStingHitTick = 9;
|
||||
private static int emperorScorpionStingCooldownTicks = 42;
|
||||
private static int emperorScorpionPoisonTicks = 100;
|
||||
private static int emperorScorpionWeaknessTicks = 80;
|
||||
private static int emperorScorpionSummonIntervalTicks = 180;
|
||||
private static int emperorScorpionMaxSummonedScorpions = 4;
|
||||
private static int emperorScorpionClawAnimTicks = 23;
|
||||
private static int emperorScorpionClawHitTick = 12;
|
||||
private static int emperorScorpionClawCooldownTicks = 28;
|
||||
private static int emperorScorpionStingAnimTicks = 25;
|
||||
private static int emperorScorpionStingHitTick = 13;
|
||||
private static int emperorScorpionStingCooldownTicks = 60;
|
||||
private static int emperorScorpionPoisonTicks = 300;
|
||||
private static int emperorScorpionWeaknessTicks = 120;
|
||||
private static int emperorScorpionSummonIntervalTicks = 140;
|
||||
private static int emperorScorpionMaxSummonedScorpions = 8;
|
||||
private static double scorpionHealth = 30.0D;
|
||||
private static double scorpionAttackDamage = 5.0D;
|
||||
private static double scorpionMovementSpeed = 0.29D;
|
||||
@@ -271,6 +279,10 @@ public final class AntarchySettings {
|
||||
private static double bedBugAttackLungeHorizontalSpeed = 0.42D;
|
||||
private static double bedBugAttackLungeVerticalSpeed = 0.1D;
|
||||
private static float bedBugHealAmount = 2.0F;
|
||||
private static double jumpyBugHealth = 100.0D;
|
||||
private static double jumpyBugPounceDamage = 12.0D;
|
||||
private static double jumpyBugLatchDamage = 1.0D;
|
||||
private static double jumpyBugCamouflageAlpha = 0.18D;
|
||||
private static double brutalflyArmor = 10.0D;
|
||||
private static double brutalflyArmorToughness = 4.0D;
|
||||
private static double brutalflyKnockbackResistance = 0.7D;
|
||||
@@ -426,6 +438,7 @@ public final class AntarchySettings {
|
||||
private static double ductTapeRepairPercentPerUse = 0.25D;
|
||||
private static double ultimateArmorKnockbackResistance = 0.1D;
|
||||
private static double nightmareArmorKnockbackResistance = 0.1D;
|
||||
private static double primordialArmorKnockbackPerPiece = 0.5D;
|
||||
private static int fallenKingCrownArmorValue = 2;
|
||||
private static double fallenKingCrownArmorToughness = 0.0D;
|
||||
private static boolean elythiaFireflyParticlesEnabled = true;
|
||||
@@ -447,14 +460,25 @@ public final class AntarchySettings {
|
||||
private static double toreterrorHealth = 300.0D;
|
||||
private static double toreterrorJumpAttackDamage = 14.0D;
|
||||
private static double toreterrorJumpAttackKnockback = 2.5D;
|
||||
private static double toreterrorSpinDamage = 8.0D;
|
||||
private static double toreterrorSpinKnockback = 1.5D;
|
||||
private static double toreterrorSpinDamage = 6.0D;
|
||||
private static double toreterrorSpinKnockback = 0.9D;
|
||||
private static double toreterrorRangedWaterBombChance = 0.5D;
|
||||
private static double toreterrorProjectileDamageMultiplier = 0.5D;
|
||||
private static double waterBombDamage = 6.0D;
|
||||
private static int waterBombLifetimeTicks = 120;
|
||||
private static double waterBombGravity = 0.12D;
|
||||
private static double waterBombKnockback = 1.2D;
|
||||
private static double waterCannonCooldownSeconds = 1.5D;
|
||||
private static double creepingHorrorHealth = 15.0D;
|
||||
private static double creepingHorrorAttackDamage = 6.0D;
|
||||
private static double lurkingTerrorHealth = 15.0D;
|
||||
private static double lurkingTerrorAttackDamage = 6.0D;
|
||||
private static double cheepHealth = 8.0D;
|
||||
private static double cheepAttackDamage = 3.0D;
|
||||
private static double dorrieHealth = 60.0D;
|
||||
private static double herculesBeetleHealth = 500.0D;
|
||||
private static double herculesBeetleAttackDamage = 30.0D;
|
||||
private static double herculesBeetleChargeDamage = 40.0D;
|
||||
|
||||
private AntarchySettings() {
|
||||
}
|
||||
@@ -723,6 +747,18 @@ public final class AntarchySettings {
|
||||
return krakenAttackDamage;
|
||||
}
|
||||
|
||||
public static double octopusBombHealth() {
|
||||
return octopusBombHealth;
|
||||
}
|
||||
|
||||
public static void setOctopusBombHealth(double value) { octopusBombHealth = value; }
|
||||
|
||||
public static double octopusBombAttackDamage() {
|
||||
return octopusBombAttackDamage;
|
||||
}
|
||||
|
||||
public static void setOctopusBombAttackDamage(double value) { octopusBombAttackDamage = value; }
|
||||
|
||||
public static double brutalflyHealth() {
|
||||
return brutalflyHealth;
|
||||
}
|
||||
@@ -759,6 +795,30 @@ public final class AntarchySettings {
|
||||
mantisIgnoreLightLevel = value;
|
||||
}
|
||||
|
||||
public static double alphaMantisHealth() {
|
||||
return alphaMantisHealth;
|
||||
}
|
||||
|
||||
public static double alphaMantisAttackDamage() {
|
||||
return alphaMantisAttackDamage;
|
||||
}
|
||||
|
||||
public static double alphaMantisMovementSpeed() {
|
||||
return alphaMantisMovementSpeed;
|
||||
}
|
||||
|
||||
public static double alphaMantisFlyingSpeed() {
|
||||
return alphaMantisFlyingSpeed;
|
||||
}
|
||||
|
||||
public static int alphaMantisSummonIntervalTicks() {
|
||||
return alphaMantisSummonIntervalTicks;
|
||||
}
|
||||
|
||||
public static int alphaMantisMaxMinions() {
|
||||
return alphaMantisMaxMinions;
|
||||
}
|
||||
|
||||
public static double waspHealth() {
|
||||
return waspHealth;
|
||||
}
|
||||
@@ -1163,6 +1223,30 @@ public final class AntarchySettings {
|
||||
mantisFlyingSpeed = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisHealth(double value) {
|
||||
alphaMantisHealth = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisAttackDamage(double value) {
|
||||
alphaMantisAttackDamage = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisMovementSpeed(double value) {
|
||||
alphaMantisMovementSpeed = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisFlyingSpeed(double value) {
|
||||
alphaMantisFlyingSpeed = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisSummonIntervalTicks(int value) {
|
||||
alphaMantisSummonIntervalTicks = value;
|
||||
}
|
||||
|
||||
public static void setAlphaMantisMaxMinions(int value) {
|
||||
alphaMantisMaxMinions = value;
|
||||
}
|
||||
|
||||
public static void setKrakenProjectileDamageTakenMultiplier(double value) {
|
||||
krakenProjectileDamageTakenMultiplier = value;
|
||||
}
|
||||
@@ -1810,6 +1894,18 @@ public final class AntarchySettings {
|
||||
public static float bedBugHealAmount() { return bedBugHealAmount; }
|
||||
public static void setBedBugHealAmount(float value) { bedBugHealAmount = value; }
|
||||
|
||||
public static double jumpyBugHealth() { return jumpyBugHealth; }
|
||||
public static void setJumpyBugHealth(double value) { jumpyBugHealth = value; }
|
||||
|
||||
public static double jumpyBugPounceDamage() { return jumpyBugPounceDamage; }
|
||||
public static void setJumpyBugPounceDamage(double value) { jumpyBugPounceDamage = value; }
|
||||
|
||||
public static double jumpyBugLatchDamage() { return jumpyBugLatchDamage; }
|
||||
public static void setJumpyBugLatchDamage(double value) { jumpyBugLatchDamage = value; }
|
||||
|
||||
public static double jumpyBugCamouflageAlpha() { return jumpyBugCamouflageAlpha; }
|
||||
public static void setJumpyBugCamouflageAlpha(double value) { jumpyBugCamouflageAlpha = value; }
|
||||
|
||||
public static double brutalflyArmor() { return brutalflyArmor; }
|
||||
public static void setBrutalflyArmor(double value) { brutalflyArmor = value; }
|
||||
|
||||
@@ -2275,6 +2371,9 @@ public final class AntarchySettings {
|
||||
public static double nightmareArmorKnockbackResistance() { return nightmareArmorKnockbackResistance; }
|
||||
public static void setNightmareArmorKnockbackResistance(double value) { nightmareArmorKnockbackResistance = value; }
|
||||
|
||||
public static double primordialArmorKnockbackPerPiece() { return primordialArmorKnockbackPerPiece; }
|
||||
public static void setPrimordialArmorKnockbackPerPiece(double value) { primordialArmorKnockbackPerPiece = value; }
|
||||
|
||||
public static int fallenKingCrownArmorValue() { return fallenKingCrownArmorValue; }
|
||||
public static void setFallenKingCrownArmorValue(int value) { fallenKingCrownArmorValue = value; }
|
||||
|
||||
@@ -2330,6 +2429,8 @@ public final class AntarchySettings {
|
||||
public static void setToreterrorSpinKnockback(double v) { toreterrorSpinKnockback = v; }
|
||||
public static double toreterrorRangedWaterBombChance() { return toreterrorRangedWaterBombChance; }
|
||||
public static void setToreterrorRangedWaterBombChance(double v) { toreterrorRangedWaterBombChance = v; }
|
||||
public static double toreterrorProjectileDamageMultiplier() { return toreterrorProjectileDamageMultiplier; }
|
||||
public static void setToreterrorProjectileDamageMultiplier(double v) { toreterrorProjectileDamageMultiplier = v; }
|
||||
public static double waterBombDamage() { return waterBombDamage; }
|
||||
public static void setWaterBombDamage(double v) { waterBombDamage = v; }
|
||||
public static int waterBombLifetimeTicks() { return waterBombLifetimeTicks; }
|
||||
@@ -2340,4 +2441,25 @@ public final class AntarchySettings {
|
||||
public static void setWaterBombKnockback(double v) { waterBombKnockback = v; }
|
||||
public static double waterCannonCooldownSeconds() { return waterCannonCooldownSeconds; }
|
||||
public static void setWaterCannonCooldownSeconds(double v) { waterCannonCooldownSeconds = v; }
|
||||
|
||||
public static double creepingHorrorHealth() { return creepingHorrorHealth; }
|
||||
public static void setCreepingHorrorHealth(double v) { creepingHorrorHealth = v; }
|
||||
public static double creepingHorrorAttackDamage() { return creepingHorrorAttackDamage; }
|
||||
public static void setCreepingHorrorAttackDamage(double v) { creepingHorrorAttackDamage = v; }
|
||||
public static double lurkingTerrorHealth() { return lurkingTerrorHealth; }
|
||||
public static void setLurkingTerrorHealth(double v) { lurkingTerrorHealth = v; }
|
||||
public static double lurkingTerrorAttackDamage() { return lurkingTerrorAttackDamage; }
|
||||
public static void setLurkingTerrorAttackDamage(double v) { lurkingTerrorAttackDamage = v; }
|
||||
public static double cheepHealth() { return cheepHealth; }
|
||||
public static void setCheepHealth(double v) { cheepHealth = v; }
|
||||
public static double cheepAttackDamage() { return cheepAttackDamage; }
|
||||
public static void setCheepAttackDamage(double v) { cheepAttackDamage = v; }
|
||||
public static double dorrieHealth() { return dorrieHealth; }
|
||||
public static double herculesBeetleHealth() { return herculesBeetleHealth; }
|
||||
public static void setHerculesBeetleHealth(double v) { herculesBeetleHealth = v; }
|
||||
public static double herculesBeetleAttackDamage() { return herculesBeetleAttackDamage; }
|
||||
public static void setHerculesBeetleAttackDamage(double v) { herculesBeetleAttackDamage = v; }
|
||||
public static double herculesBeetleChargeDamage() { return herculesBeetleChargeDamage; }
|
||||
public static void setHerculesBeetleChargeDamage(double v) { herculesBeetleChargeDamage = v; }
|
||||
public static void setDorrieHealth(double v) { dorrieHealth = v; }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.craisinlord.antarchy.config;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class ConfigResetGuard {
|
||||
public static final int RESET_GENERATION = 1;
|
||||
private static final String MARKER_FILE_NAME = ".config_generation";
|
||||
|
||||
private ConfigResetGuard() {
|
||||
}
|
||||
|
||||
public static void wipeIfNeeded(Path configDir, Path... configFiles) {
|
||||
Path markerPath = configDir.resolve(MARKER_FILE_NAME);
|
||||
if (readGeneration(markerPath) >= RESET_GENERATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Path configFile : configFiles) {
|
||||
try {
|
||||
if (Files.deleteIfExists(configFile)) {
|
||||
Antarchy.LOGGER.info("Reset config file {} to defaults (config generation {})", configFile, RESET_GENERATION);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Antarchy.LOGGER.warn("Failed to delete config file {} during config reset", configFile, e);
|
||||
}
|
||||
}
|
||||
|
||||
writeGeneration(markerPath);
|
||||
}
|
||||
|
||||
private static int readGeneration(Path markerPath) {
|
||||
if (!Files.exists(markerPath)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
return Integer.parseInt(Files.readString(markerPath).trim());
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeGeneration(Path markerPath) {
|
||||
try {
|
||||
Files.createDirectories(markerPath.getParent());
|
||||
Files.writeString(markerPath, Integer.toString(RESET_GENERATION));
|
||||
} catch (IOException e) {
|
||||
Antarchy.LOGGER.warn("Failed to write config generation marker at {}", markerPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,13 @@ import com.craisinlord.antarchy.content.entity.BomberEntity;
|
||||
import com.craisinlord.antarchy.content.entity.ButterflyEntity;
|
||||
import com.craisinlord.antarchy.content.entity.CaterpillarEntity;
|
||||
import com.craisinlord.antarchy.content.entity.EasterBunnyEntity;
|
||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||
import com.craisinlord.antarchy.content.entity.HushProjectileEntity;
|
||||
import com.craisinlord.antarchy.content.entity.StinkBugEntity;
|
||||
import com.craisinlord.antarchy.content.entity.WaterBombEntity;
|
||||
import com.craisinlord.antarchy.content.entity.TriffidEntity;
|
||||
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
|
||||
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
|
||||
import com.craisinlord.antarchy.content.entity.ToreterrorEntity;
|
||||
import com.craisinlord.antarchy.content.entity.WaspEntity;
|
||||
import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity;
|
||||
@@ -15,6 +19,7 @@ import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyOrbEntity;
|
||||
import com.craisinlord.antarchy.content.block.entity.HushweedBlockEntity;
|
||||
import com.craisinlord.antarchy.content.entity.flying_squirrel.FlyingSquirrelEntity;
|
||||
import com.craisinlord.antarchy.content.entity.MissileSquidEntity;
|
||||
import com.craisinlord.antarchy.content.entity.OctopusBombEntity;
|
||||
import com.craisinlord.antarchy.content.entity.kraken.KrakenEntity;
|
||||
import com.craisinlord.antarchy.content.entity.MolewormEntity;
|
||||
import com.craisinlord.antarchy.content.entity.MantisEntity;
|
||||
@@ -25,6 +30,7 @@ import com.craisinlord.antarchy.content.block.entity.DreamCampfireBlockEntity;
|
||||
import com.craisinlord.antarchy.content.block.entity.WaspNestBlockEntity;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.particles.SimpleParticleType;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -49,6 +55,8 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<MissileSquidEntity>> MISSILE_SQUID = (Supplier<EntityType<MissileSquidEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<OctopusBombEntity>> OCTOPUS_BOMB = (Supplier<EntityType<OctopusBombEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<MolewormEntity>> MOLEWORM = (Supplier<EntityType<MolewormEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<MantisEntity>> MANTIS = (Supplier<EntityType<MantisEntity>>) UNBOUND;
|
||||
@@ -79,6 +87,20 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<WaterBombEntity>> WATER_BOMB = (Supplier<EntityType<WaterBombEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<CreepingHorrorEntity>> CREEPING_HORROR = (Supplier<EntityType<CreepingHorrorEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<LurkingTerrorEntity>> LURKING_TERROR = (Supplier<EntityType<LurkingTerrorEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<StinkBugEntity>> STINK_BUG = (Supplier<EntityType<StinkBugEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>> CHEEP = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>> DORRIE = (Supplier<EntityType<com.craisinlord.antarchy.content.entity.DorrieEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<EntityType<HerculesBeetleEntity>> HERCULES_BEETLE = (Supplier<EntityType<HerculesBeetleEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<net.minecraft.world.item.Item> CHEEP_ITEM = (Supplier<net.minecraft.world.item.Item>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> DUPLICATOR_LOG = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> DUPLICATOR_SAPLING = (Supplier<Block>) UNBOUND;
|
||||
@@ -103,6 +125,12 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CRACKED_SHELLSTONE_BRICKS = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> MOSSY_SHELLSTONE_BRICK_STAIRS = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> MOSSY_SHELLSTONE_BRICK_SLAB = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> MOSSY_SHELLSTONE_BRICK_WALL = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> SHELLSTONE_STAIRS = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> SHELLSTONE_SLAB = (Supplier<Block>) UNBOUND;
|
||||
@@ -129,6 +157,10 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> NYXITE_SPIKE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CHITEN_BLOCK = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CHITEN_SPIKE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> POTENT_NYXITE = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> ANTIMETAL = (Supplier<Block>) UNBOUND;
|
||||
@@ -153,6 +185,8 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Item> REVERIE_BOTTLE = (Supplier<Item>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Item> STINK_BUG_ITEM = (Supplier<Item>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Holder<MobEffect>> DREAD = (Supplier<Holder<MobEffect>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Holder<Attribute>> DOUBLE_DAMAGE_CHANCE = (Supplier<Holder<Attribute>>) UNBOUND;
|
||||
@@ -165,6 +199,8 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Holder<MobEffect>> INVERTED_EFFECT = (Supplier<Holder<MobEffect>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Holder<MobEffect>> STINKY_EFFECT = (Supplier<Holder<MobEffect>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> OURANWOOD_ACORN_BLOCK = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> MOSSY_OURANWOOD_LOG = (Supplier<Block>) UNBOUND;
|
||||
@@ -179,12 +215,18 @@ public final class AntarchyObjects {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> BED_BUG_EGG = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> CREEPING_HORROR_EGGS = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> LURKING_TERROR_EGGS = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> WASP_NEST = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> HUSHWEED = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> SQUIRREL_NEST_BLOCK = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<Block> BRUTALFLY_COCOON_SPAWNER = (Supplier<Block>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<BlockEntityType<AntNestBlockEntity>> ANT_NEST_BLOCK_ENTITY = (Supplier<BlockEntityType<AntNestBlockEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<BlockEntityType<DreamCampfireBlockEntity>> DREAM_CAMPFIRE_BLOCK_ENTITY = (Supplier<BlockEntityType<DreamCampfireBlockEntity>>) UNBOUND;
|
||||
@@ -192,6 +234,10 @@ public final class AntarchyObjects {
|
||||
public static Supplier<BlockEntityType<WaspNestBlockEntity>> WASP_NEST_BLOCK_ENTITY = (Supplier<BlockEntityType<WaspNestBlockEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<BlockEntityType<HushweedBlockEntity>> HUSHWEED_BLOCK_ENTITY = (Supplier<BlockEntityType<HushweedBlockEntity>>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<SimpleParticleType> STINKY_GAS = (Supplier<SimpleParticleType>) UNBOUND;
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Supplier<SimpleParticleType> STINKY_FLY = (Supplier<SimpleParticleType>) UNBOUND;
|
||||
|
||||
private AntarchyObjects() {
|
||||
}
|
||||
@@ -216,6 +262,7 @@ public final class AntarchyObjects {
|
||||
Supplier<EntityType<HushProjectileEntity>> hushProjectile,
|
||||
Supplier<EntityType<ToreterrorEntity>> toreterror,
|
||||
Supplier<EntityType<WaterBombEntity>> waterBomb,
|
||||
Supplier<EntityType<com.craisinlord.antarchy.content.entity.CheepEntity>> cheep,
|
||||
Supplier<Block> duplicatorLog,
|
||||
Supplier<Block> duplicatorSapling,
|
||||
Supplier<Block> ductTape,
|
||||
@@ -228,6 +275,9 @@ public final class AntarchyObjects {
|
||||
Supplier<Block> chiseledShellstone,
|
||||
Supplier<Block> mossyShellstoneBricks,
|
||||
Supplier<Block> crackedShellstoneBricks,
|
||||
Supplier<Block> mossyShellstoneBrickStairs,
|
||||
Supplier<Block> mossyShellstoneBrickSlab,
|
||||
Supplier<Block> mossyShellstoneBrickWall,
|
||||
Supplier<Block> shellstoneStairs,
|
||||
Supplier<Block> shellstoneSlab,
|
||||
Supplier<Block> shellstoneWall,
|
||||
@@ -256,6 +306,7 @@ public final class AntarchyObjects {
|
||||
Supplier<Holder<MobEffect>> dread,
|
||||
Supplier<Holder<MobEffect>> paralyzedEffect,
|
||||
Supplier<Holder<MobEffect>> invertedEffect,
|
||||
Supplier<Holder<MobEffect>> stinkyEffect,
|
||||
Supplier<Block> ouranwoodAcornBlock,
|
||||
Supplier<Block> mossyOuranwoodLog,
|
||||
Supplier<Block> mossyOuranwoodWood,
|
||||
@@ -266,10 +317,13 @@ public final class AntarchyObjects {
|
||||
Supplier<Block> waspNest,
|
||||
Supplier<Block> hushweed,
|
||||
Supplier<Block> squirrelNestBlock,
|
||||
Supplier<Block> brutalflyCocoonSpawner,
|
||||
Supplier<BlockEntityType<AntNestBlockEntity>> antNestBlockEntity,
|
||||
Supplier<BlockEntityType<DreamCampfireBlockEntity>> dreamCampfireBlockEntity,
|
||||
Supplier<BlockEntityType<WaspNestBlockEntity>> waspNestBlockEntity,
|
||||
Supplier<BlockEntityType<HushweedBlockEntity>> hushweedBlockEntity,
|
||||
Supplier<SimpleParticleType> stinkyGas,
|
||||
Supplier<SimpleParticleType> stinkyFly,
|
||||
Supplier<Holder<Attribute>> doubleDamageChance,
|
||||
Supplier<Holder<Attribute>> bloodglassMaxHearts,
|
||||
Supplier<Holder<MobEffect>> bloodglassWard
|
||||
@@ -293,6 +347,7 @@ public final class AntarchyObjects {
|
||||
HUSH_PROJECTILE = hushProjectile;
|
||||
TORETERROR = toreterror;
|
||||
WATER_BOMB = waterBomb;
|
||||
CHEEP = cheep;
|
||||
DUPLICATOR_LOG = duplicatorLog;
|
||||
DUPLICATOR_SAPLING = duplicatorSapling;
|
||||
DUCT_TAPE = ductTape;
|
||||
@@ -305,6 +360,9 @@ public final class AntarchyObjects {
|
||||
CHISELED_SHELLSTONE = chiseledShellstone;
|
||||
MOSSY_SHELLSTONE_BRICKS = mossyShellstoneBricks;
|
||||
CRACKED_SHELLSTONE_BRICKS = crackedShellstoneBricks;
|
||||
MOSSY_SHELLSTONE_BRICK_STAIRS = mossyShellstoneBrickStairs;
|
||||
MOSSY_SHELLSTONE_BRICK_SLAB = mossyShellstoneBrickSlab;
|
||||
MOSSY_SHELLSTONE_BRICK_WALL = mossyShellstoneBrickWall;
|
||||
SHELLSTONE_STAIRS = shellstoneStairs;
|
||||
SHELLSTONE_SLAB = shellstoneSlab;
|
||||
SHELLSTONE_WALL = shellstoneWall;
|
||||
@@ -333,6 +391,7 @@ public final class AntarchyObjects {
|
||||
DREAD = dread;
|
||||
PARALYZED_EFFECT = paralyzedEffect;
|
||||
INVERTED_EFFECT = invertedEffect;
|
||||
STINKY_EFFECT = stinkyEffect;
|
||||
OURANWOOD_ACORN_BLOCK = ouranwoodAcornBlock;
|
||||
MOSSY_OURANWOOD_LOG = mossyOuranwoodLog;
|
||||
MOSSY_OURANWOOD_WOOD = mossyOuranwoodWood;
|
||||
@@ -343,10 +402,13 @@ public final class AntarchyObjects {
|
||||
WASP_NEST = waspNest;
|
||||
HUSHWEED = hushweed;
|
||||
SQUIRREL_NEST_BLOCK = squirrelNestBlock;
|
||||
BRUTALFLY_COCOON_SPAWNER = brutalflyCocoonSpawner;
|
||||
ANT_NEST_BLOCK_ENTITY = antNestBlockEntity;
|
||||
DREAM_CAMPFIRE_BLOCK_ENTITY = dreamCampfireBlockEntity;
|
||||
WASP_NEST_BLOCK_ENTITY = waspNestBlockEntity;
|
||||
HUSHWEED_BLOCK_ENTITY = hushweedBlockEntity;
|
||||
STINKY_GAS = stinkyGas;
|
||||
STINKY_FLY = stinkyFly;
|
||||
DOUBLE_DAMAGE_CHANCE = doubleDamageChance;
|
||||
BLOODGLASS_MAX_HEARTS = bloodglassMaxHearts;
|
||||
BLOODGLASS_WARD = bloodglassWard;
|
||||
@@ -356,6 +418,14 @@ public final class AntarchyObjects {
|
||||
return state.is(DUPLICATOR_LOG.get()) || state.is(DUPLICATOR_SAPLING.get());
|
||||
}
|
||||
|
||||
public static void setOctopusBomb(Supplier<EntityType<OctopusBombEntity>> supplier) {
|
||||
OCTOPUS_BOMB = supplier;
|
||||
}
|
||||
|
||||
public static void setHerculesBeetle(Supplier<EntityType<HerculesBeetleEntity>> supplier) {
|
||||
HERCULES_BEETLE = supplier;
|
||||
}
|
||||
|
||||
public static BlockBehaviour.Properties shellstoneProperties() {
|
||||
return BlockBehaviour.Properties.ofFullCopy(Blocks.STONE).requiresCorrectToolForDrops();
|
||||
}
|
||||
|
||||
@@ -119,6 +119,23 @@ public final class AntarchySoundEvents {
|
||||
public static Supplier<SoundEvent> MOLEVORE_HURT = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> MOLEVORE_ATTACK = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> MOLEVORE_DIG = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> BOMBER_WALK = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> BOMBER_KNOCK = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> BOMBER_EXPLODE = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_IDLE = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_HURT = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_DEATH = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_BOMBER_FIRE = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_JUMP_UP = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> TORETERROR_JUMP_LAND = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> STINKY_FLY = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> CREEPING_HORROR_GROWL = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> CREEPING_HORROR_HURT = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> CREEPING_HORROR_BITE = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> LURKING_TERROR_SNARL = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> LURKING_TERROR_HURT = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> LURKING_TERROR_BITE = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> LURKING_TERROR_FLY_LOOP = (Supplier<SoundEvent>) UNBOUND;
|
||||
public static Supplier<SoundEvent> DUCT_TAPE_USE = (Supplier<SoundEvent>) UNBOUND;
|
||||
|
||||
private AntarchySoundEvents() {
|
||||
@@ -235,6 +252,23 @@ public final class AntarchySoundEvents {
|
||||
Supplier<SoundEvent> molevoreHurt,
|
||||
Supplier<SoundEvent> molevoreAttack,
|
||||
Supplier<SoundEvent> molevoreDig,
|
||||
Supplier<SoundEvent> bomberWalk,
|
||||
Supplier<SoundEvent> bomberKnock,
|
||||
Supplier<SoundEvent> bomberExplode,
|
||||
Supplier<SoundEvent> toreterrorIdle,
|
||||
Supplier<SoundEvent> toreterrorHurt,
|
||||
Supplier<SoundEvent> toreterrorDeath,
|
||||
Supplier<SoundEvent> toreterrorBomberFire,
|
||||
Supplier<SoundEvent> toreterrorJumpUp,
|
||||
Supplier<SoundEvent> toreterrorJumpLand,
|
||||
Supplier<SoundEvent> stinkyFly,
|
||||
Supplier<SoundEvent> creepingHorrorGrowl,
|
||||
Supplier<SoundEvent> creepingHorrorHurt,
|
||||
Supplier<SoundEvent> creepingHorrorBite,
|
||||
Supplier<SoundEvent> lurkingTerrorSnarl,
|
||||
Supplier<SoundEvent> lurkingTerrorHurt,
|
||||
Supplier<SoundEvent> lurkingTerrorBite,
|
||||
Supplier<SoundEvent> lurkingTerrorFlyLoop,
|
||||
Supplier<SoundEvent> ductTapeUse
|
||||
) {
|
||||
SQUIDZOOKA_FIRE = squidzookaFire;
|
||||
@@ -347,6 +381,23 @@ public final class AntarchySoundEvents {
|
||||
MOLEVORE_HURT = molevoreHurt;
|
||||
MOLEVORE_ATTACK = molevoreAttack;
|
||||
MOLEVORE_DIG = molevoreDig;
|
||||
BOMBER_WALK = bomberWalk;
|
||||
BOMBER_KNOCK = bomberKnock;
|
||||
BOMBER_EXPLODE = bomberExplode;
|
||||
TORETERROR_IDLE = toreterrorIdle;
|
||||
TORETERROR_HURT = toreterrorHurt;
|
||||
TORETERROR_DEATH = toreterrorDeath;
|
||||
TORETERROR_BOMBER_FIRE = toreterrorBomberFire;
|
||||
TORETERROR_JUMP_UP = toreterrorJumpUp;
|
||||
TORETERROR_JUMP_LAND = toreterrorJumpLand;
|
||||
STINKY_FLY = stinkyFly;
|
||||
CREEPING_HORROR_GROWL = creepingHorrorGrowl;
|
||||
CREEPING_HORROR_HURT = creepingHorrorHurt;
|
||||
CREEPING_HORROR_BITE = creepingHorrorBite;
|
||||
LURKING_TERROR_SNARL = lurkingTerrorSnarl;
|
||||
LURKING_TERROR_HURT = lurkingTerrorHurt;
|
||||
LURKING_TERROR_BITE = lurkingTerrorBite;
|
||||
LURKING_TERROR_FLY_LOOP = lurkingTerrorFlyLoop;
|
||||
DUCT_TAPE_USE = ductTapeUse;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Block> MOLEVORE_SEE_THROUGH_BLOCKS = TagKey.create(Registries.BLOCK, id("molevore_see_through_blocks"));
|
||||
public static final TagKey<Block> TERMITE_FOODS = TagKey.create(Registries.BLOCK, id("termite_foods"));
|
||||
public static final TagKey<Block> UMBRAL_MOSS_REPLACEABLE = TagKey.create(Registries.BLOCK, id("umbral_moss_replaceable"));
|
||||
public static final TagKey<Block> HERCULES_BEETLE_CHARGE_IMMUNE_BLOCKS = TagKey.create(Registries.BLOCK, id("hercules_beetle_charge_immune_blocks"));
|
||||
|
||||
private Blocks() {
|
||||
}
|
||||
@@ -55,6 +56,7 @@ public final class AntarchyTags {
|
||||
public static final TagKey<EntityType<?>> PARALYSIS_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("paralysis_immune"));
|
||||
public static final TagKey<EntityType<?>> HUSHWEED_TARGETS = TagKey.create(Registries.ENTITY_TYPE, id("hushweed_targets"));
|
||||
public static final TagKey<EntityType<?>> TRIFFID_PREY = TagKey.create(Registries.ENTITY_TYPE, id("triffid_prey"));
|
||||
public static final TagKey<EntityType<?>> STINKY_REPELLED_HOSTILES = TagKey.create(Registries.ENTITY_TYPE, id("stinky_repelled_hostiles"));
|
||||
public static final TagKey<EntityType<?>> SCORPION_WHIP_IMMUNE = TagKey.create(Registries.ENTITY_TYPE, id("scorpion_whip_immune"));
|
||||
public static final TagKey<EntityType<?>> SCORPION_WHIP_BLACKLIST = TagKey.create(Registries.ENTITY_TYPE, id("scorpion_whip_blacklist"));
|
||||
|
||||
@@ -75,6 +77,7 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Item> DUCT_TAPE_BLACKLIST = TagKey.create(Registries.ITEM, id("duct_tape_blacklist"));
|
||||
public static final TagKey<Item> BROWN_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("brown_ant_activation_items"));
|
||||
public static final TagKey<Item> BROWN_ANT_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("brown_ant_breeding_foods"));
|
||||
public static final TagKey<Item> MULTISHOT_ENCHANTABLE = TagKey.create(Registries.ITEM, id("multishot_enchantable"));
|
||||
public static final TagKey<Item> RED_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("red_ant_activation_items"));
|
||||
public static final TagKey<Item> RED_ANT_BREEDING_FOODS = TagKey.create(Registries.ITEM, id("red_ant_breeding_foods"));
|
||||
public static final TagKey<Item> RAINBOW_ANT_ACTIVATION_ITEMS = TagKey.create(Registries.ITEM, id("rainbow_ant_activation_items"));
|
||||
@@ -86,6 +89,7 @@ public final class AntarchyTags {
|
||||
public static final TagKey<Item> CATERPILLAR_FOODS = TagKey.create(Registries.ITEM, id("caterpillar_foods"));
|
||||
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"));
|
||||
|
||||
private Items() {
|
||||
}
|
||||
@@ -94,6 +98,7 @@ public final class AntarchyTags {
|
||||
public static final class Biomes {
|
||||
public static final TagKey<Biome> MANTIS_SPAWN_BIOMES = TagKey.create(Registries.BIOME, id("mantis_spawn_biomes"));
|
||||
public static final TagKey<Biome> MANTIS_OVERWORLD_SPAWN_BIOMES = TagKey.create(Registries.BIOME, id("mantis_overworld_spawn_biomes"));
|
||||
public static final TagKey<Biome> ELYTHIA_FIREFLY_PARTICLE_BIOMES = TagKey.create(Registries.BIOME, id("elythia_firefly_particle_biomes"));
|
||||
|
||||
private Biomes() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.craisinlord.antarchy.content;
|
||||
|
||||
import com.craisinlord.antarchy.mixins.AccessorRangedAttribute;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.RangedAttribute;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class AttributeRangeInit {
|
||||
|
||||
private static final double MAX = 11_000_000D;
|
||||
|
||||
private static final Set<ResourceLocation> TARGETS = ImmutableSet.of(
|
||||
ResourceLocation.fromNamespaceAndPath("minecraft", "generic.max_health"),
|
||||
ResourceLocation.fromNamespaceAndPath("minecraft", "generic.armor"),
|
||||
ResourceLocation.fromNamespaceAndPath("minecraft", "generic.armor_toughness"),
|
||||
ResourceLocation.fromNamespaceAndPath("minecraft", "generic.attack_damage"),
|
||||
ResourceLocation.fromNamespaceAndPath("minecraft", "generic.attack_knockback")
|
||||
);
|
||||
|
||||
public static void apply() {
|
||||
for (ResourceLocation id : TARGETS) {
|
||||
Attribute attribute = BuiltInRegistries.ATTRIBUTE.get(id);
|
||||
if (attribute instanceof RangedAttribute ranged && ranged instanceof AccessorRangedAttribute accessor) {
|
||||
if (ranged.getMaxValue() < MAX) {
|
||||
accessor.antarchyAttributefix$setMaxValue(MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package com.craisinlord.antarchy.content;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.EntityTypeTags;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.PathfinderMob;
|
||||
import net.minecraft.world.entity.monster.Enemy;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class StinkyBehavior {
|
||||
public static final double STINK_BUG_AURA_RADIUS = 5.0D;
|
||||
public static final double STINKY_NAUSEA_RADIUS = 4.0D;
|
||||
private static final int TRAIL_INTERVAL_TICKS = 4;
|
||||
private static final int REACTION_INTERVAL_TICKS = 10;
|
||||
private static final double ARTHROPOD_ALERT_RADIUS = 28.0D;
|
||||
private static final double FLEE_RADIUS = 10.0D;
|
||||
private static final double FLEE_DISTANCE = 8.0D;
|
||||
private static final int STINKY_NAUSEA_DURATION_TICKS = 60;
|
||||
private static final int STINK_BUG_BURST_NAUSEA_DURATION_TICKS = 100;
|
||||
|
||||
private StinkyBehavior() {
|
||||
}
|
||||
|
||||
public static void tickStinkyTrail(LivingEntity entity) {
|
||||
if (!(entity.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
if (!entity.isAlive() || entity.tickCount % TRAIL_INTERVAL_TICKS != 0 || !entity.hasEffect(AntarchyObjects.STINKY_EFFECT.get())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 motion = entity.getDeltaMovement();
|
||||
Vec3 horizontalMotion = new Vec3(motion.x, 0.0D, motion.z);
|
||||
Vec3 direction = horizontalMotion.lengthSqr() > 1.0E-4D
|
||||
? horizontalMotion.normalize()
|
||||
: entity.getLookAngle().multiply(1.0D, 0.0D, 1.0D);
|
||||
if (direction.lengthSqr() <= 1.0E-4D) {
|
||||
direction = new Vec3(0.0D, 0.0D, 1.0D);
|
||||
} else {
|
||||
direction = direction.normalize();
|
||||
}
|
||||
|
||||
Vec3 origin = entity.position()
|
||||
.subtract(direction.scale(0.35D))
|
||||
.add(0.0D, Math.max(0.15D, entity.getBbHeight() * 0.2D), 0.0D);
|
||||
serverLevel.sendParticles(
|
||||
AntarchyObjects.STINKY_GAS.get(),
|
||||
origin.x,
|
||||
origin.y,
|
||||
origin.z,
|
||||
2,
|
||||
entity.getBbWidth() * 0.12D,
|
||||
0.04D,
|
||||
entity.getBbWidth() * 0.12D,
|
||||
0.005D
|
||||
);
|
||||
serverLevel.sendParticles(
|
||||
AntarchyObjects.STINKY_FLY.get(),
|
||||
entity.getX(),
|
||||
entity.getY(0.45D),
|
||||
entity.getZ(),
|
||||
1,
|
||||
entity.getBbWidth() * 0.35D,
|
||||
entity.getBbHeight() * 0.2D,
|
||||
entity.getBbWidth() * 0.35D,
|
||||
0.0D
|
||||
);
|
||||
applyNearbyNausea(entity, STINKY_NAUSEA_RADIUS, STINKY_NAUSEA_DURATION_TICKS);
|
||||
if (entity.tickCount % REACTION_INTERVAL_TICKS == 0) {
|
||||
reactToStinkySource(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public static void emitBurst(LivingEntity entity, int count) {
|
||||
if (!(entity.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
serverLevel.sendParticles(
|
||||
AntarchyObjects.STINKY_GAS.get(),
|
||||
entity.getX(),
|
||||
entity.getY(0.45D),
|
||||
entity.getZ(),
|
||||
count,
|
||||
entity.getBbWidth() * 0.35D,
|
||||
entity.getBbHeight() * 0.25D,
|
||||
entity.getBbWidth() * 0.35D,
|
||||
0.045D
|
||||
);
|
||||
serverLevel.sendParticles(
|
||||
AntarchyObjects.STINKY_FLY.get(),
|
||||
entity.getX(),
|
||||
entity.getY(0.45D),
|
||||
entity.getZ(),
|
||||
Math.max(2, count / 6),
|
||||
entity.getBbWidth() * 0.45D,
|
||||
entity.getBbHeight() * 0.25D,
|
||||
entity.getBbWidth() * 0.45D,
|
||||
0.0D
|
||||
);
|
||||
applyNearbyNausea(entity, STINK_BUG_AURA_RADIUS, STINK_BUG_BURST_NAUSEA_DURATION_TICKS);
|
||||
}
|
||||
|
||||
public static void applyBurstStinkyEffect(LivingEntity source, double radius, int durationTicks) {
|
||||
if (source.level().isClientSide()) {
|
||||
return;
|
||||
}
|
||||
for (LivingEntity entity : source.level().getEntitiesOfClass(
|
||||
LivingEntity.class,
|
||||
source.getBoundingBox().inflate(radius),
|
||||
e -> e.isAlive() && e != source
|
||||
)) {
|
||||
entity.addEffect(new MobEffectInstance(AntarchyObjects.STINKY_EFFECT.get(), durationTicks, 0, false, true, true));
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyNearbyNausea(LivingEntity source, double radius, int durationTicks) {
|
||||
if (source.level().isClientSide()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player player : source.level().getEntitiesOfClass(
|
||||
Player.class,
|
||||
source.getBoundingBox().inflate(radius),
|
||||
player -> player.isAlive() && !player.isSpectator() && player != source
|
||||
)) {
|
||||
player.addEffect(new MobEffectInstance(MobEffects.CONFUSION, durationTicks, 0, false, true, true));
|
||||
}
|
||||
}
|
||||
|
||||
private static void reactToStinkySource(LivingEntity source) {
|
||||
if (source.level().isClientSide() || !source.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Mob mob : source.level().getEntitiesOfClass(
|
||||
Mob.class,
|
||||
source.getBoundingBox().inflate(ARTHROPOD_ALERT_RADIUS),
|
||||
mob -> mob.isAlive() && mob != source
|
||||
)) {
|
||||
if (mob.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) {
|
||||
if (source.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) {
|
||||
continue;
|
||||
}
|
||||
attractArthropod(mob, source);
|
||||
continue;
|
||||
}
|
||||
if (shouldRepelMob(mob, source)) {
|
||||
repelPassiveOrNeutralMob(mob, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void attractArthropod(Mob mob, LivingEntity source) {
|
||||
if (!isValidStinkyAttackSourceForMob(mob, source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LivingEntity currentTarget = mob.getTarget();
|
||||
if (currentTarget != null && currentTarget.isAlive() && currentTarget.hasEffect(AntarchyObjects.STINKY_EFFECT.get())
|
||||
&& mob.distanceToSqr(currentTarget) <= ARTHROPOD_ALERT_RADIUS * ARTHROPOD_ALERT_RADIUS) {
|
||||
return;
|
||||
}
|
||||
|
||||
mob.setTarget(source);
|
||||
}
|
||||
|
||||
private static void repelPassiveOrNeutralMob(Mob mob, LivingEntity source) {
|
||||
if (!(mob instanceof PathfinderMob pathfinderMob) || !isValidStinkyRepelSourceForMob(mob, source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 away = mob.position().subtract(source.position());
|
||||
if (away.lengthSqr() < 1.0E-4D) {
|
||||
away = new Vec3(mob.getRandom().nextDouble() - 0.5D, 0.0D, mob.getRandom().nextDouble() - 0.5D);
|
||||
}
|
||||
|
||||
Vec3 escapeTarget = mob.position().add(away.normalize().scale(FLEE_DISTANCE));
|
||||
pathfinderMob.setTarget(null);
|
||||
pathfinderMob.getNavigation().moveTo(escapeTarget.x, escapeTarget.y, escapeTarget.z, 1.2D);
|
||||
}
|
||||
|
||||
private static boolean shouldRepelMob(Mob mob, LivingEntity source) {
|
||||
if (mob.hasEffect(AntarchyObjects.STINKY_EFFECT.get()) || mob.distanceToSqr(source) > FLEE_RADIUS * FLEE_RADIUS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mob.getType().is(AntarchyTags.Entities.STINKY_REPELLED_HOSTILES)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(mob instanceof Enemy);
|
||||
}
|
||||
|
||||
private static boolean isValidStinkyAttackSourceForMob(Mob mob, LivingEntity source) {
|
||||
if (!isValidStinkyRepelSourceForMob(mob, source)) {
|
||||
return false;
|
||||
}
|
||||
if (source == mob || !source.isAlive() || !source.hasEffect(AntarchyObjects.STINKY_EFFECT.get())) {
|
||||
return false;
|
||||
}
|
||||
if (source instanceof Player player && (player.isCreative() || player.isSpectator())) {
|
||||
return false;
|
||||
}
|
||||
return !mob.isAlliedTo(source);
|
||||
}
|
||||
|
||||
private static boolean isValidStinkyRepelSourceForMob(Mob mob, LivingEntity source) {
|
||||
if (source == mob || !source.isAlive() || !source.hasEffect(AntarchyObjects.STINKY_EFFECT.get())) {
|
||||
return false;
|
||||
}
|
||||
if (source instanceof Player player && player.isSpectator()) {
|
||||
return false;
|
||||
}
|
||||
return !mob.isAlliedTo(source);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.BonemealableBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
|
||||
public class AmberMossBlock extends Block implements BonemealableBlock {
|
||||
public static final MapCodec<AmberMossBlock> CODEC = Block.simpleCodec(AmberMossBlock::new);
|
||||
private static final ResourceKey<ConfiguredFeature<?, ?>> AMBER_MOSS_PATCH_BONEMEAL = ResourceKey.create(
|
||||
Registries.CONFIGURED_FEATURE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "amber_moss_patch_bonemeal")
|
||||
);
|
||||
|
||||
public AmberMossBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<AmberMossBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
|
||||
return level.getBlockState(pos.above()).isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
|
||||
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
|
||||
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
|
||||
.getOrThrow(AMBER_MOSS_PATCH_BONEMEAL);
|
||||
feature.value().place(level, level.getChunkSource().getGenerator(), random, pos);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class BedBugEggBlock extends Block {
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (!this.shouldUpdateHatchLevel(level) || !hasHatchSupport(level, pos)) {
|
||||
if (!this.shouldUpdateHatchLevel(level)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.entity.brutalfly.BrutalflyEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public class BrutalflyCocoonSpawnerBlock extends Block {
|
||||
|
||||
public BrutalflyCocoonSpawnerBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RenderShape getRenderShape(BlockState state) {
|
||||
return RenderShape.INVISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
||||
|
||||
BrutalflyEntity brutalfly = AntarchyObjects.BRUTALFLY.get().create(level);
|
||||
if (brutalfly == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos anchor = pos.above();
|
||||
brutalfly.moveTo(
|
||||
anchor.getX() + 0.5D,
|
||||
anchor.getY() - 1.5D,
|
||||
anchor.getZ() + 0.5D,
|
||||
random.nextFloat() * 360.0F,
|
||||
0.0F
|
||||
);
|
||||
brutalfly.setCocooned(true, anchor);
|
||||
brutalfly.setHealth(brutalfly.getMaxHealth());
|
||||
level.addFreshEntity(brutalfly);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.PointedDripstoneBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.DripstoneThickness;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
public class ChitenSpikeBlock extends PointedDripstoneBlock {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final MapCodec<PointedDripstoneBlock> CODEC = (MapCodec) Block.simpleCodec(ChitenSpikeBlock::new);
|
||||
|
||||
public ChitenSpikeBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<PointedDripstoneBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
LevelAccessor level = context.getLevel();
|
||||
BlockPos pos = context.getClickedPos();
|
||||
Direction direction = calculateTipDirection(level, pos, context.getNearestLookingVerticalDirection().getOpposite());
|
||||
if (direction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean mergeTips = !context.isSecondaryUseActive();
|
||||
DripstoneThickness thickness = calculateDripstoneThickness(level, pos, direction, mergeTips);
|
||||
if (thickness == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.defaultBlockState()
|
||||
.setValue(TIP_DIRECTION, direction)
|
||||
.setValue(THICKNESS, thickness)
|
||||
.setValue(WATERLOGGED, level.getFluidState(pos).getType() == Fluids.WATER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(
|
||||
BlockState state,
|
||||
Direction direction,
|
||||
BlockState neighborState,
|
||||
LevelAccessor level,
|
||||
BlockPos pos,
|
||||
BlockPos neighborPos
|
||||
) {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
|
||||
}
|
||||
|
||||
if (direction != Direction.UP && direction != Direction.DOWN) {
|
||||
return state;
|
||||
}
|
||||
|
||||
Direction tipDirection = state.getValue(TIP_DIRECTION);
|
||||
if (tipDirection == Direction.DOWN && level.getBlockTicks().hasScheduledTick(pos, this)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
if (direction == tipDirection.getOpposite() && !this.canSurvive(state, level, pos)) {
|
||||
if (tipDirection == Direction.DOWN) {
|
||||
level.scheduleTick(pos, this, 2);
|
||||
} else {
|
||||
level.scheduleTick(pos, this, 1);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
boolean mergeTips = state.getValue(THICKNESS) == DripstoneThickness.TIP_MERGE;
|
||||
return state.setValue(THICKNESS, calculateDripstoneThickness(level, pos, tipDirection, mergeTips));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||
return isValidPointedDripstonePlacement(level, pos, state.getValue(TIP_DIRECTION));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (this.canSurvive(state, level, pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.getValue(TIP_DIRECTION) == Direction.DOWN) {
|
||||
BlockPos.MutableBlockPos root = pos.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(root.above()), Direction.DOWN)) {
|
||||
root.move(Direction.UP);
|
||||
}
|
||||
|
||||
BlockPos.MutableBlockPos cursor = root.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(cursor), Direction.DOWN)) {
|
||||
BlockState fallingState = level.getBlockState(cursor);
|
||||
if (isChitenSpikeWithDirection(fallingState, Direction.DOWN)) {
|
||||
net.minecraft.world.entity.item.FallingBlockEntity.fall(level, cursor, fallingState);
|
||||
}
|
||||
cursor.move(Direction.DOWN);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos.MutableBlockPos cursor = pos.mutable();
|
||||
while (isChitenSpikeWithDirection(level.getBlockState(cursor), Direction.UP)) {
|
||||
BlockState spikeState = level.getBlockState(cursor);
|
||||
dropResources(spikeState, level, cursor);
|
||||
level.removeBlock(cursor, false);
|
||||
cursor.move(Direction.UP);
|
||||
}
|
||||
}
|
||||
|
||||
private static Direction calculateTipDirection(LevelReader level, BlockPos pos, Direction preferredDirection) {
|
||||
if (isValidPointedDripstonePlacement(level, pos, preferredDirection)) {
|
||||
return preferredDirection;
|
||||
}
|
||||
|
||||
Direction opposite = preferredDirection.getOpposite();
|
||||
return isValidPointedDripstonePlacement(level, pos, opposite) ? opposite : null;
|
||||
}
|
||||
|
||||
private static DripstoneThickness calculateDripstoneThickness(LevelReader level, BlockPos pos, Direction direction, boolean mergeTips) {
|
||||
Direction opposite = direction.getOpposite();
|
||||
BlockState forwardState = level.getBlockState(pos.relative(direction));
|
||||
if (isChitenSpikeWithDirection(forwardState, opposite)) {
|
||||
return !mergeTips && forwardState.getValue(THICKNESS) != DripstoneThickness.TIP_MERGE
|
||||
? DripstoneThickness.TIP
|
||||
: DripstoneThickness.TIP_MERGE;
|
||||
}
|
||||
|
||||
if (!isChitenSpikeWithDirection(forwardState, direction)) {
|
||||
return DripstoneThickness.TIP;
|
||||
}
|
||||
|
||||
DripstoneThickness forwardThickness = forwardState.getValue(THICKNESS);
|
||||
if (forwardThickness != DripstoneThickness.TIP && forwardThickness != DripstoneThickness.TIP_MERGE) {
|
||||
BlockState backState = level.getBlockState(pos.relative(opposite));
|
||||
return !isChitenSpikeWithDirection(backState, direction) ? DripstoneThickness.BASE : DripstoneThickness.MIDDLE;
|
||||
}
|
||||
|
||||
return DripstoneThickness.FRUSTUM;
|
||||
}
|
||||
|
||||
private static boolean isValidPointedDripstonePlacement(LevelReader level, BlockPos pos, Direction direction) {
|
||||
BlockPos supportPos = pos.relative(direction.getOpposite());
|
||||
BlockState supportState = level.getBlockState(supportPos);
|
||||
return supportState.isFaceSturdy(level, supportPos, direction) || isChitenSpikeWithDirection(supportState, direction);
|
||||
}
|
||||
|
||||
private static boolean isChitenSpikeWithDirection(BlockState state, Direction direction) {
|
||||
return state.is(AntarchyObjects.CHITEN_SPIKE.get()) && state.getValue(TIP_DIRECTION) == direction;
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
|
||||
import com.craisinlord.antarchy.content.block.AmberMossBlock;
|
||||
import com.craisinlord.antarchy.content.block.LurkingTerrorEggBlock;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ambient.Bat;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
public class CreepingHorrorEggBlock extends Block {
|
||||
public static final MapCodec<CreepingHorrorEggBlock> CODEC = Block.simpleCodec(CreepingHorrorEggBlock::new);
|
||||
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
|
||||
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
|
||||
private static final float PANIC_HATCH_CHANCE = 0.50F;
|
||||
private static final int MAX_HATCH = 2;
|
||||
private static final int MAX_EGGS = 4;
|
||||
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
|
||||
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
|
||||
|
||||
public CreepingHorrorEggBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<CreepingHorrorEggBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
|
||||
return !context.isSecondaryUseActive()
|
||||
&& context.getItemInHand().is(this.asItem())
|
||||
&& state.getValue(EGGS) < MAX_EGGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
|
||||
if (existingState.is(this)) {
|
||||
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
|
||||
}
|
||||
return this.defaultBlockState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRandomlyTicking(BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (!this.shouldUpdateHatchLevel(level, pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int hatch = state.getValue(HATCH);
|
||||
if (hatch < MAX_HATCH) {
|
||||
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
|
||||
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
panicHatchNearby(serverLevel, pos);
|
||||
}
|
||||
}
|
||||
super.stepOn(level, pos, state, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
||||
this.destroyEgg(level, state, pos, entity, 3);
|
||||
super.fallOn(level, state, pos, entity, fallDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
|
||||
if (hasHatchSupport(level, pos) && !level.isClientSide) {
|
||||
level.levelEvent(2012, pos, 15);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
|
||||
super.playerDestroy(level, player, pos, state, blockEntity, stack);
|
||||
this.decreaseEggs(level, pos, state);
|
||||
}
|
||||
|
||||
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
|
||||
int radius = 6;
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dy = -radius; dy <= radius; dy++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
|
||||
BlockState nearby = level.getBlockState(mutable);
|
||||
BlockPos immutable = mutable.immutable();
|
||||
if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
} else if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(HATCH, EGGS);
|
||||
}
|
||||
|
||||
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
|
||||
return isSupportedGround(level, pos.below());
|
||||
}
|
||||
|
||||
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
|
||||
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
|
||||
}
|
||||
|
||||
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
|
||||
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
|
||||
return false;
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
|
||||
this.decreaseEggs(level, pos, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
|
||||
if (hatchedEggs <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, hatchedEggs);
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
|
||||
if (eggsToHatch >= state.getValue(EGGS)) {
|
||||
level.removeBlock(pos, false);
|
||||
} else {
|
||||
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
|
||||
|
||||
for (int i = 0; i < eggsToHatch; i++) {
|
||||
CreepingHorrorEntity entity = AntarchyObjects.CREEPING_HORROR.get().create(level);
|
||||
if (entity == null) {
|
||||
continue;
|
||||
}
|
||||
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
|
||||
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
|
||||
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
|
||||
level.addFreshEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private static int rollPanickedEggs(int eggs, RandomSource random) {
|
||||
int hatchedEggs = 0;
|
||||
for (int i = 0; i < eggs; i++) {
|
||||
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
|
||||
hatchedEggs++;
|
||||
}
|
||||
}
|
||||
return hatchedEggs;
|
||||
}
|
||||
|
||||
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
|
||||
int eggs = state.getValue(EGGS);
|
||||
if (eggs <= 1) {
|
||||
level.destroyBlock(pos, false);
|
||||
return;
|
||||
}
|
||||
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
|
||||
}
|
||||
|
||||
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
|
||||
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
|
||||
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
|
||||
}
|
||||
|
||||
private boolean canDestroyEgg(Level level, Entity entity) {
|
||||
if (entity instanceof CreepingHorrorEntity || entity instanceof Bat) {
|
||||
return false;
|
||||
}
|
||||
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
|
||||
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
package com.craisinlord.antarchy.content.block;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
|
||||
import com.craisinlord.antarchy.content.block.AmberMossBlock;
|
||||
import com.craisinlord.antarchy.content.block.CreepingHorrorEggBlock;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ambient.Bat;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
|
||||
public class LurkingTerrorEggBlock extends Block {
|
||||
public static final MapCodec<LurkingTerrorEggBlock> CODEC = Block.simpleCodec(LurkingTerrorEggBlock::new);
|
||||
public static final IntegerProperty HATCH = BlockStateProperties.HATCH;
|
||||
public static final IntegerProperty EGGS = BlockStateProperties.EGGS;
|
||||
private static final float PANIC_HATCH_CHANCE = 0.50F;
|
||||
private static final int MAX_HATCH = 2;
|
||||
private static final int MAX_EGGS = 4;
|
||||
private static final VoxelShape SINGLE_SHAPE = Block.box(3.0D, 0.0D, 3.0D, 13.0D, 7.0D, 13.0D);
|
||||
private static final VoxelShape MULTI_SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 7.0D, 15.0D);
|
||||
|
||||
public LurkingTerrorEggBlock(BlockBehaviour.Properties properties) {
|
||||
super(properties);
|
||||
this.registerDefaultState(this.stateDefinition.any().setValue(HATCH, 0).setValue(EGGS, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapCodec<LurkingTerrorEggBlock> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||
return state.getValue(EGGS) > 1 ? MULTI_SHAPE : SINGLE_SHAPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
|
||||
return level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
|
||||
return state.canSurvive(level, pos) ? super.updateShape(state, direction, neighborState, level, pos, neighborPos) : Blocks.AIR.defaultBlockState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
|
||||
return !context.isSecondaryUseActive()
|
||||
&& context.getItemInHand().is(this.asItem())
|
||||
&& state.getValue(EGGS) < MAX_EGGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||
BlockState existingState = context.getLevel().getBlockState(context.getClickedPos());
|
||||
if (existingState.is(this)) {
|
||||
return existingState.setValue(EGGS, Math.min(MAX_EGGS, existingState.getValue(EGGS) + 1));
|
||||
}
|
||||
return this.defaultBlockState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRandomlyTicking(BlockState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
|
||||
if (!this.shouldUpdateHatchLevel(level, pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int hatch = state.getValue(HATCH);
|
||||
if (hatch < MAX_HATCH) {
|
||||
level.setBlock(pos, state.setValue(HATCH, hatch + 1), 2);
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_CRACK, SoundSource.BLOCKS, 0.7F, 0.95F + random.nextFloat() * 0.1F);
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, state.getValue(EGGS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
|
||||
if (entity instanceof Player && !entity.isSteppingCarefully() && this.destroyEgg(level, state, pos, entity, 100)) {
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
panicHatchNearby(serverLevel, pos);
|
||||
}
|
||||
}
|
||||
super.stepOn(level, pos, state, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fallOn(Level level, BlockState state, BlockPos pos, Entity entity, float fallDistance) {
|
||||
this.destroyEgg(level, state, pos, entity, 3);
|
||||
super.fallOn(level, state, pos, entity, fallDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
|
||||
if (hasHatchSupport(level, pos) && !level.isClientSide) {
|
||||
level.levelEvent(2012, pos, 15);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerDestroy(Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, net.minecraft.world.item.ItemStack stack) {
|
||||
super.playerDestroy(level, player, pos, state, blockEntity, stack);
|
||||
this.decreaseEggs(level, pos, state);
|
||||
}
|
||||
|
||||
private static void panicHatchNearby(ServerLevel level, BlockPos origin) {
|
||||
int radius = 6;
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dy = -radius; dy <= radius; dy++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
if (dx == 0 && dy == 0 && dz == 0) continue;
|
||||
mutable.set(origin.getX() + dx, origin.getY() + dy, origin.getZ() + dz);
|
||||
BlockState nearby = level.getBlockState(mutable);
|
||||
BlockPos immutable = mutable.immutable();
|
||||
if (nearby.getBlock() instanceof LurkingTerrorEggBlock b) {
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
} else if (nearby.getBlock() instanceof CreepingHorrorEggBlock b) {
|
||||
b.hatchPanickedEggs(level, immutable, nearby, level.random);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(HATCH, EGGS);
|
||||
}
|
||||
|
||||
public static boolean hasHatchSupport(BlockGetter level, BlockPos pos) {
|
||||
return isSupportedGround(level, pos.below());
|
||||
}
|
||||
|
||||
public static boolean isSupportedGround(BlockGetter level, BlockPos pos) {
|
||||
return level.getBlockState(pos).is(BlockTags.SAND) || level.getBlockState(pos).blocksMotion();
|
||||
}
|
||||
|
||||
private boolean destroyEgg(Level level, BlockState state, BlockPos pos, Entity entity, int chance) {
|
||||
if (level.isClientSide || !this.canDestroyEgg(level, entity) || level.random.nextInt(chance) != 0) {
|
||||
return false;
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
|
||||
this.decreaseEggs(level, pos, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
void hatchPanickedEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
|
||||
int hatchedEggs = rollPanickedEggs(state.getValue(EGGS), random);
|
||||
if (hatchedEggs <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hatchEggs(level, pos, state, random, hatchedEggs);
|
||||
}
|
||||
|
||||
void hatchEggs(ServerLevel level, BlockPos pos, BlockState state, RandomSource random, int eggsToHatch) {
|
||||
if (eggsToHatch >= state.getValue(EGGS)) {
|
||||
level.removeBlock(pos, false);
|
||||
} else {
|
||||
level.setBlock(pos, state.setValue(EGGS, state.getValue(EGGS) - eggsToHatch), 2);
|
||||
}
|
||||
level.playSound(null, pos, SoundEvents.TURTLE_EGG_HATCH, SoundSource.BLOCKS, 0.75F, 0.95F + random.nextFloat() * 0.1F);
|
||||
|
||||
for (int i = 0; i < eggsToHatch; i++) {
|
||||
LurkingTerrorEntity entity = AntarchyObjects.LURKING_TERROR.get().create(level);
|
||||
if (entity == null) {
|
||||
continue;
|
||||
}
|
||||
double spawnX = pos.getX() + 0.25D + random.nextDouble() * 0.5D;
|
||||
double spawnZ = pos.getZ() + 0.25D + random.nextDouble() * 0.5D;
|
||||
entity.moveTo(spawnX, pos.getY() + 0.1D, spawnZ, random.nextFloat() * 360.0F, 0.0F);
|
||||
level.addFreshEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private static int rollPanickedEggs(int eggs, RandomSource random) {
|
||||
int hatchedEggs = 0;
|
||||
for (int i = 0; i < eggs; i++) {
|
||||
if (random.nextFloat() < PANIC_HATCH_CHANCE) {
|
||||
hatchedEggs++;
|
||||
}
|
||||
}
|
||||
return hatchedEggs;
|
||||
}
|
||||
|
||||
private void decreaseEggs(Level level, BlockPos pos, BlockState state) {
|
||||
int eggs = state.getValue(EGGS);
|
||||
if (eggs <= 1) {
|
||||
level.destroyBlock(pos, false);
|
||||
return;
|
||||
}
|
||||
level.setBlock(pos, state.setValue(EGGS, eggs - 1), 2);
|
||||
}
|
||||
|
||||
private boolean shouldUpdateHatchLevel(Level level, BlockPos pos) {
|
||||
boolean onAmberMoss = level.getBlockState(pos.below()).getBlock() instanceof AmberMossBlock;
|
||||
return level.random.nextInt(onAmberMoss ? 150 : 500) == 0;
|
||||
}
|
||||
|
||||
private boolean canDestroyEgg(Level level, Entity entity) {
|
||||
if (entity instanceof LurkingTerrorEntity || entity instanceof Bat) {
|
||||
return false;
|
||||
}
|
||||
if (entity instanceof net.minecraft.world.entity.LivingEntity) {
|
||||
return entity instanceof Player || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class CameraShakeClientState {
|
||||
public static final double TORETERROR_RANGE = 48.0D;
|
||||
public static final float TORETERROR_STRENGTH = 1.0F;
|
||||
public static final double NIGHTMARE_RANGE = 32.0D;
|
||||
public static final float NIGHTMARE_STRENGTH = 1.35F;
|
||||
|
||||
private record Source(double range, float strength, BooleanSupplier active) {
|
||||
}
|
||||
|
||||
private static final Map<LivingEntity, Source> SOURCES = new HashMap<>();
|
||||
|
||||
private CameraShakeClientState() {
|
||||
}
|
||||
|
||||
public static void register(LivingEntity entity, double range, float strength, BooleanSupplier active) {
|
||||
SOURCES.putIfAbsent(entity, new Source(range, strength, active));
|
||||
}
|
||||
|
||||
public static float getStrength(Vec3 cameraPos) {
|
||||
if (SOURCES.isEmpty()) {
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
float total = 0.0F;
|
||||
Iterator<Map.Entry<LivingEntity, Source>> iterator = SOURCES.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<LivingEntity, Source> entry = iterator.next();
|
||||
LivingEntity entity = entry.getKey();
|
||||
Source source = entry.getValue();
|
||||
if (entity.isRemoved() || !entity.isAlive() || !source.active().getAsBoolean()) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
double distance = Math.sqrt(cameraPos.distanceToSqr(entity.position().add(0.0D, entity.getBbHeight() * 0.5D, 0.0D)));
|
||||
if (distance > source.range()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
total += (float) ((1.0D - distance / source.range()) * source.strength());
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
SOURCES.clear();
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
public final class HerculesBeetleImpactShakeClientState {
|
||||
private static int shakeTicks;
|
||||
|
||||
private HerculesBeetleImpactShakeClientState() {
|
||||
}
|
||||
|
||||
public static void trigger(int ticks) {
|
||||
shakeTicks = Math.max(shakeTicks, Math.max(0, ticks));
|
||||
}
|
||||
|
||||
public static void tick() {
|
||||
if (shakeTicks > 0) {
|
||||
shakeTicks--;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTicks() {
|
||||
return shakeTicks;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
shakeTicks = 0;
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.craisinlord.antarchy.content.client;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.item.NightmareSwordItem;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
public final class NightmareSwordTooltipHelper {
|
||||
private NightmareSwordTooltipHelper() {
|
||||
}
|
||||
|
||||
public static Component damageLine() {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
float damage = player != null
|
||||
? NightmareSwordItem.calculateDamage(player)
|
||||
: (float) AntarchySettings.nightmareSwordBaseDamage();
|
||||
return Component.translatable("tooltip.antarchy.nightmare_sword_damage", formatDamage(damage)).withStyle(ChatFormatting.RED);
|
||||
}
|
||||
|
||||
private static String formatDamage(float damage) {
|
||||
float rounded = Math.round(damage * 10.0F) / 10.0F;
|
||||
if (rounded == (int) rounded) {
|
||||
return String.valueOf((int) rounded);
|
||||
}
|
||||
return String.valueOf(rounded);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.AlphaMantisEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class AlphaMantisModel extends GeoModel<AlphaMantisEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(AlphaMantisEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/mantis.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(AlphaMantisEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/mantis_alpha.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(AlphaMantisEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/mantis.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.CheepEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class CheepModel extends GeoModel<CheepEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/cheep.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/cheep/cheep_magenta.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(CheepEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/cheep.animation.json");
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -6,6 +6,11 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class CloudSharkModel extends GeoModel<CloudSharkEntity> {
|
||||
public static final ResourceLocation BASE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/cloud_shark/cloud_shark.png");
|
||||
public static final ResourceLocation EMISSIVE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/cloud_shark/cloud_shark_emissive.png");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(CloudSharkEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/cloud_shark.geo.json");
|
||||
@@ -13,7 +18,7 @@ public class CloudSharkModel extends GeoModel<CloudSharkEntity> {
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(CloudSharkEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/cloud_shark.png");
|
||||
return BASE_TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class CreepingHorrorModel extends GeoModel<CreepingHorrorEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(CreepingHorrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/creeping_horror.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(CreepingHorrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/creeping_horror.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(CreepingHorrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/creeping_horror.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.DorrieEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class DorrieModel extends GeoModel<DorrieEntity> {
|
||||
private static final ResourceLocation TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/dorrie.png");
|
||||
private static final ResourceLocation TEXTURE_SADDLED =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/dorrie_saddle.png");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(DorrieEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/dorrie.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(DorrieEntity animatable) {
|
||||
return animatable.hasSaddle() ? TEXTURE_SADDLED : TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(DorrieEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/dorrie.animation.json");
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -7,6 +7,9 @@ import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class EmperorScorpionModel extends GeoModel<EmperorScorpionEntity> {
|
||||
|
||||
public static final ResourceLocation EMISSIVE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/emperor_scorpion/emperor_scorpion_emissive.png");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(EmperorScorpionEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/emperor_scorpion.geo.json");
|
||||
@@ -14,7 +17,7 @@ public class EmperorScorpionModel extends GeoModel<EmperorScorpionEntity> {
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(EmperorScorpionEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/emperor_scorpion.png");
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/emperor_scorpion/emperor_scorpion.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class HerculesBeetleModel extends GeoModel<HerculesBeetleEntity> {
|
||||
private static final ResourceLocation TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hercules_beetle/hercules_beetle.png");
|
||||
private static final ResourceLocation TEXTURE_SADDLED =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hercules_beetle/hercules_beetle_saddle.png");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(HerculesBeetleEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/hercules_beetle.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(HerculesBeetleEntity animatable) {
|
||||
return animatable.hasSaddle() ? TEXTURE_SADDLED : TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(HerculesBeetleEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/hercules_beetle.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.JumpyBugEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class JumpyBugModel extends GeoModel<JumpyBugEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(JumpyBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/jumpy_bug.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(JumpyBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/jumpy_bug.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(JumpyBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/jumpy_bug.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ public class KrakenModel extends GeoModel<KrakenEntity> {
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(KrakenEntity animatable) {
|
||||
return animatable.isPhaseTwo()
|
||||
? ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/kraken_purple.png")
|
||||
: ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/kraken.png");
|
||||
return ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/kraken.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class LurkingTerrorModel extends GeoModel<LurkingTerrorEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(LurkingTerrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/lurking_terror.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(LurkingTerrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/lurking_terror.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(LurkingTerrorEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/lurking_terror.animation.json");
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.content.entity.OctopusBombEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class OctopusBombModel extends GeoModel<OctopusBombEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(OctopusBombEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath("antarchy", "geo/octopus_bomb.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(OctopusBombEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath("antarchy", "textures/entity/octopus_bomb.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(OctopusBombEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath("antarchy", "animations/octopus_bomb.animation.json");
|
||||
}
|
||||
}
|
||||
+23
-3
@@ -6,19 +6,39 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class ScorpionModel extends GeoModel<ScorpionEntity> {
|
||||
private static final ResourceLocation MODEL =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/scorpion.geo.json");
|
||||
private static final ResourceLocation BLUE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/scorpion/scorpion_blue.png");
|
||||
private static final ResourceLocation GREEN_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/scorpion/scorpion_green.png");
|
||||
private static final ResourceLocation BLUE_EMISSIVE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/scorpion/scorpion_blue_emissive.png");
|
||||
private static final ResourceLocation GREEN_EMISSIVE_TEXTURE =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/scorpion/scorpion_green_emissive.png");
|
||||
private static final ResourceLocation ANIMATION =
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/scorpion.animation.json");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(ScorpionEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/scorpion.geo.json");
|
||||
return MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(ScorpionEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/scorpion.png");
|
||||
return textureFor(animatable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(ScorpionEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/scorpion.animation.json");
|
||||
return ANIMATION;
|
||||
}
|
||||
|
||||
public static ResourceLocation textureFor(ScorpionEntity animatable) {
|
||||
return animatable.getTextureVariant() == ScorpionEntity.GREEN_VARIANT ? GREEN_TEXTURE : BLUE_TEXTURE;
|
||||
}
|
||||
|
||||
public static ResourceLocation emissiveTextureFor(ScorpionEntity animatable) {
|
||||
return animatable.getTextureVariant() == ScorpionEntity.GREEN_VARIANT ? GREEN_EMISSIVE_TEXTURE : BLUE_EMISSIVE_TEXTURE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.SpitBugEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class SpitBugModel extends GeoModel<SpitBugEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(SpitBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/spit_bug.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(SpitBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/spit_bug.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(SpitBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/spit_bug.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.StinkBugEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class StinkBugModel extends GeoModel<StinkBugEntity> {
|
||||
@Override
|
||||
public ResourceLocation getModelResource(StinkBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/stink_bug.geo.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(StinkBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/stink_bug.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(StinkBugEntity animatable) {
|
||||
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/stink_bug.animation.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.craisinlord.antarchy.content.client.model;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.entity.ant.TermiteEntity;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import software.bernie.geckolib.model.GeoModel;
|
||||
|
||||
public class TermiteModel extends GeoModel<TermiteEntity> {
|
||||
private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/termite.geo.json");
|
||||
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/termite.png");
|
||||
private static final ResourceLocation ANIMATIONS = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/termite.animation.json");
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelResource(TermiteEntity animatable) {
|
||||
return MODEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureResource(TermiteEntity animatable) {
|
||||
return TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAnimationResource(TermiteEntity animatable) {
|
||||
return ANIMATIONS;
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.craisinlord.antarchy.content.client.particle;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.particle.ParticleProvider;
|
||||
import net.minecraft.client.particle.ParticleRenderType;
|
||||
import net.minecraft.client.particle.SpriteSet;
|
||||
import net.minecraft.client.particle.TextureSheetParticle;
|
||||
import net.minecraft.core.particles.SimpleParticleType;
|
||||
|
||||
public final class OrangeAshParticle extends TextureSheetParticle {
|
||||
private final SpriteSet sprites;
|
||||
|
||||
private OrangeAshParticle(ClientLevel level, double x, double y, double z, double xd, double yd, double zd, SpriteSet sprites) {
|
||||
super(level, x, y, z, xd, yd, zd);
|
||||
this.sprites = sprites;
|
||||
this.friction = 0.98F;
|
||||
this.gravity = 0.0025F;
|
||||
this.speedUpWhenYMotionIsBlocked = false;
|
||||
this.hasPhysics = false;
|
||||
this.xd = xd * 0.08D + (this.random.nextDouble() - 0.5D) * 0.01D;
|
||||
this.yd = yd * 0.02D - 0.01D - this.random.nextDouble() * 0.01D;
|
||||
this.zd = zd * 0.08D + (this.random.nextDouble() - 0.5D) * 0.01D;
|
||||
this.quadSize *= 0.75F + this.random.nextFloat() * 0.5F;
|
||||
this.lifetime = 40 + this.random.nextInt(28);
|
||||
this.setColor(0.96F, 0.46F, 0.12F);
|
||||
this.setAlpha(0.72F);
|
||||
this.pickSprite(sprites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
this.pickSprite(this.sprites);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleRenderType getRenderType() {
|
||||
return ParticleRenderType.PARTICLE_SHEET_OPAQUE;
|
||||
}
|
||||
|
||||
public static final class Provider implements ParticleProvider<SimpleParticleType> {
|
||||
private final SpriteSet sprites;
|
||||
|
||||
public Provider(SpriteSet sprites) {
|
||||
this.sprites = sprites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Particle createParticle(SimpleParticleType type, ClientLevel level, double x, double y, double z, double xd, double yd, double zd) {
|
||||
return new OrangeAshParticle(level, x, y, z, xd, yd, zd, this.sprites);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.AlphaMantisModel;
|
||||
import com.craisinlord.antarchy.content.entity.AlphaMantisEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class AlphaMantisRenderer extends GeoEntityRenderer<AlphaMantisEntity> {
|
||||
public AlphaMantisRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new AlphaMantisModel());
|
||||
this.shadowRadius = 1.5F;
|
||||
// Mantis renders at 1.25x; the alpha is 1.5x bigger overall
|
||||
this.withScale(1.875F);
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -35,11 +35,10 @@ public class AntCarriedFoodGeoLayer extends BlockAndItemGeoLayer<BaseAntEntity>
|
||||
|
||||
@Override
|
||||
protected void renderStackForBone(PoseStack poseStack, GeoBone bone, ItemStack stack, BaseAntEntity animatable, MultiBufferSource bufferSource, float partialTick, int packedLight, int packedOverlay) {
|
||||
double yOffset = animatable.isDancing() ? -0.055D : -0.04D;
|
||||
poseStack.translate(0.0D, yOffset, -0.42D);
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90.0F));
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
|
||||
poseStack.scale(0.4F, 0.4F, 0.4F);
|
||||
double yOffset = animatable.isDancing() ? 0.58D : 0.15D;
|
||||
poseStack.translate(0.0D, yOffset, -0.50D);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(90.0F));
|
||||
poseStack.scale(0.5F, 0.5F, 0.5F);
|
||||
super.renderStackForBone(poseStack, bone, stack, animatable, bufferSource, partialTick, packedLight, packedOverlay);
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.entity.ant.BaseAntEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
import net.minecraft.client.model.SilverfishModel;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.renderer.entity.RenderLayerParent;
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class AntCarriedFoodLayer<T extends BaseAntEntity> extends RenderLayer<T, SilverfishModel<T>> {
|
||||
private final ItemRenderer itemRenderer;
|
||||
|
||||
public AntCarriedFoodLayer(RenderLayerParent<T, SilverfishModel<T>> renderer, ItemRenderer itemRenderer) {
|
||||
super(renderer);
|
||||
this.itemRenderer = itemRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
PoseStack poseStack,
|
||||
MultiBufferSource buffer,
|
||||
int packedLight,
|
||||
T ant,
|
||||
float limbSwing,
|
||||
float limbSwingAmount,
|
||||
float partialTick,
|
||||
float ageInTicks,
|
||||
float netHeadYaw,
|
||||
float headPitch
|
||||
) {
|
||||
ItemStack carriedFood = ant.getCarriedFood();
|
||||
if (carriedFood.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.0F, 0.08F, -0.06F);
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90.0F));
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
|
||||
poseStack.scale(0.22F, 0.22F, 0.22F);
|
||||
this.itemRenderer.renderStatic(
|
||||
ant,
|
||||
carriedFood,
|
||||
ItemDisplayContext.FIXED,
|
||||
false,
|
||||
poseStack,
|
||||
buffer,
|
||||
ant.level(),
|
||||
packedLight,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
ant.getId()
|
||||
);
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.BufferUploader;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.MeshData;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
import com.mojang.blaze3d.vertex.VertexBuffer;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import java.util.Random;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public final class CavarynSkyRenderer {
|
||||
public static final ResourceKey<Level> CAVARYN = ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "cavaryn")
|
||||
);
|
||||
|
||||
@org.jetbrains.annotations.Nullable
|
||||
private static VertexBuffer orangeStarBuffer;
|
||||
|
||||
private CavarynSkyRenderer() {
|
||||
}
|
||||
|
||||
public static boolean shouldRender(ClientLevel level) {
|
||||
return level.dimension().equals(CAVARYN);
|
||||
}
|
||||
|
||||
public static void render(
|
||||
Minecraft minecraft,
|
||||
ClientLevel level,
|
||||
PoseStack poseStack,
|
||||
org.joml.Matrix4f projectionMatrix,
|
||||
float partialTick,
|
||||
Camera camera,
|
||||
boolean isFoggy,
|
||||
Runnable setupFog,
|
||||
VertexBuffer skyBuffer,
|
||||
VertexBuffer darkBuffer
|
||||
) {
|
||||
setupFog.run();
|
||||
if (isFoggy || minecraft.gameRenderer.getMainCamera() != camera) {
|
||||
return;
|
||||
}
|
||||
|
||||
float red = 0.015F;
|
||||
float green = 0.085F;
|
||||
float blue = 0.035F;
|
||||
|
||||
RenderSystem.depthMask(false);
|
||||
RenderSystem.setShaderColor(red, green, blue, 1.0F);
|
||||
skyBuffer.bind();
|
||||
skyBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionShader());
|
||||
VertexBuffer.unbind();
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
float starBrightness = 0.8F;
|
||||
if (orangeStarBuffer == null) {
|
||||
orangeStarBuffer = buildOrangeStarBuffer();
|
||||
}
|
||||
|
||||
RenderSystem.enableBlend();
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.setShaderColor(starBrightness, starBrightness, starBrightness, starBrightness);
|
||||
orangeStarBuffer.bind();
|
||||
orangeStarBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionColorShader());
|
||||
VertexBuffer.unbind();
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
RenderSystem.disableBlend();
|
||||
|
||||
if (camera.getEntity() != null) {
|
||||
double horizon = camera.getEntity().getEyePosition(partialTick).y - level.getLevelData().getHorizonHeight(level);
|
||||
if (horizon < 0.0D) {
|
||||
poseStack.pushPose();
|
||||
poseStack.translate(0.0F, 12.0F, 0.0F);
|
||||
darkBuffer.bind();
|
||||
darkBuffer.drawWithShader(poseStack.last().pose(), projectionMatrix, GameRenderer.getPositionShader());
|
||||
VertexBuffer.unbind();
|
||||
poseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
RenderSystem.depthMask(true);
|
||||
}
|
||||
|
||||
private static VertexBuffer buildOrangeStarBuffer() {
|
||||
Random random = new Random(548721L);
|
||||
BufferBuilder builder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||
|
||||
for (int i = 0; i < 1800; i++) {
|
||||
double x = random.nextFloat() * 2.0F - 1.0F;
|
||||
double y = random.nextFloat() * 2.0F - 1.0F;
|
||||
double z = random.nextFloat() * 2.0F - 1.0F;
|
||||
double size = 0.12F + random.nextFloat() * 0.12F;
|
||||
double lenSq = x * x + y * y + z * z;
|
||||
if (lenSq < 1.0D && lenSq > 0.01D) {
|
||||
double len = 1.0D / Math.sqrt(lenSq);
|
||||
x *= len;
|
||||
y *= len;
|
||||
z *= len;
|
||||
double sx = x * 100.0D;
|
||||
double sy = y * 100.0D;
|
||||
double sz = z * 100.0D;
|
||||
double yaw = Math.atan2(x, z);
|
||||
double sinYaw = Math.sin(yaw);
|
||||
double cosYaw = Math.cos(yaw);
|
||||
double pitch = Math.atan2(Math.sqrt(x * x + z * z), y);
|
||||
double sinPitch = Math.sin(pitch);
|
||||
double cosPitch = Math.cos(pitch);
|
||||
double rot = random.nextDouble() * Math.PI * 2.0D;
|
||||
double sinRot = Math.sin(rot);
|
||||
double cosRot = Math.cos(rot);
|
||||
|
||||
float[] color = pickStarColor(random);
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
double u = (j & 2) - 1;
|
||||
double v = (j + 1 & 2) - 1;
|
||||
double px = u * cosRot - v * sinRot;
|
||||
double py = v * cosRot + u * sinRot;
|
||||
double qx = px * sinPitch;
|
||||
double qy = -px * cosPitch;
|
||||
double rx = qy * sinYaw - py * cosYaw;
|
||||
double ry = py * sinYaw + qy * cosYaw;
|
||||
builder.addVertex((float) (sx + rx * size), (float) (sy + qx * size), (float) (sz + ry * size))
|
||||
.setColor(color[0], color[1], color[2], 1.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshData mesh = builder.buildOrThrow();
|
||||
VertexBuffer buffer = new VertexBuffer(VertexBuffer.Usage.STATIC);
|
||||
buffer.bind();
|
||||
buffer.upload(mesh);
|
||||
VertexBuffer.unbind();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static float[] pickStarColor(Random random) {
|
||||
float roll = random.nextFloat();
|
||||
if (roll < 0.70F) return new float[]{1.00F, 0.55F, 0.14F};
|
||||
if (roll < 0.90F) return new float[]{1.00F, 0.70F, 0.24F};
|
||||
return new float[]{1.00F, 0.84F, 0.40F};
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.CheepModel;
|
||||
import com.craisinlord.antarchy.content.entity.CheepEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class CheepRenderer extends GeoEntityRenderer<CheepEntity> {
|
||||
public CheepRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new CheepModel());
|
||||
}
|
||||
}
|
||||
+41
@@ -2,8 +2,17 @@ package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.CloudSharkModel;
|
||||
import com.craisinlord.antarchy.content.entity.cloud_shark.CloudSharkEntity;
|
||||
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.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
import software.bernie.geckolib.renderer.layer.GeoRenderLayer;
|
||||
|
||||
public class CloudSharkRenderer extends GeoEntityRenderer<CloudSharkEntity> {
|
||||
private static final float SHADOW_RADIUS = 0.6F;
|
||||
@@ -11,5 +20,37 @@ public class CloudSharkRenderer extends GeoEntityRenderer<CloudSharkEntity> {
|
||||
public CloudSharkRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new CloudSharkModel());
|
||||
this.shadowRadius = SHADOW_RADIUS;
|
||||
this.addRenderLayer(new CloudSharkEmissiveLayer(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderType getRenderType(CloudSharkEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
return RenderType.entityCutoutNoCull(texture);
|
||||
}
|
||||
|
||||
private static final class CloudSharkEmissiveLayer extends GeoRenderLayer<CloudSharkEntity> {
|
||||
private CloudSharkEmissiveLayer(GeoEntityRenderer<CloudSharkEntity> renderer) {
|
||||
super(renderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack poseStack, CloudSharkEntity animatable, BakedGeoModel bakedModel, @Nullable RenderType renderType,
|
||||
MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, float partialTick,
|
||||
int packedLight, int packedOverlay) {
|
||||
RenderType emissiveType = RenderType.eyes(CloudSharkModel.EMISSIVE_TEXTURE);
|
||||
VertexConsumer emissiveBuffer = bufferSource.getBuffer(emissiveType);
|
||||
this.getRenderer().reRender(
|
||||
bakedModel,
|
||||
poseStack,
|
||||
bufferSource,
|
||||
animatable,
|
||||
emissiveType,
|
||||
emissiveBuffer,
|
||||
partialTick,
|
||||
0xF000F0,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
0xFFFFFFFF
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.CreepingHorrorModel;
|
||||
import com.craisinlord.antarchy.content.entity.CreepingHorrorEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Axis;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class CreepingHorrorRenderer extends GeoEntityRenderer<CreepingHorrorEntity> {
|
||||
public CreepingHorrorRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new CreepingHorrorModel());
|
||||
this.shadowRadius = 0.5F;
|
||||
this.withScale(0.85F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRender(PoseStack poseStack, CreepingHorrorEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
|
||||
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
|
||||
if (animatable.isWallClimbing()) {
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(-90.0F));
|
||||
poseStack.translate(0, -0.5, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.DorrieModel;
|
||||
import com.craisinlord.antarchy.content.entity.DorrieEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class DorrieRenderer extends GeoEntityRenderer<DorrieEntity> {
|
||||
public DorrieRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new DorrieModel());
|
||||
}
|
||||
}
|
||||
+46
-1
@@ -2,14 +2,59 @@ package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.EmperorScorpionModel;
|
||||
import com.craisinlord.antarchy.content.entity.EmperorScorpionEntity;
|
||||
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.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
import software.bernie.geckolib.renderer.layer.GeoRenderLayer;
|
||||
|
||||
public class EmperorScorpionRenderer extends GeoEntityRenderer<EmperorScorpionEntity> {
|
||||
|
||||
public EmperorScorpionRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new EmperorScorpionModel());
|
||||
this.shadowRadius = 3.0F;
|
||||
this.withScale(1.5F);
|
||||
this.addRenderLayer(new EmperorScorpionEmissiveLayer(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderType getRenderType(EmperorScorpionEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
return RenderType.entityCutoutNoCull(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDeathMaxRotation(EmperorScorpionEntity animatable) {
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
private static final class EmperorScorpionEmissiveLayer extends GeoRenderLayer<EmperorScorpionEntity> {
|
||||
private EmperorScorpionEmissiveLayer(GeoEntityRenderer<EmperorScorpionEntity> renderer) {
|
||||
super(renderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack poseStack, EmperorScorpionEntity animatable, BakedGeoModel bakedModel, @Nullable RenderType renderType,
|
||||
MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, float partialTick,
|
||||
int packedLight, int packedOverlay) {
|
||||
RenderType emissiveType = RenderType.eyes(EmperorScorpionModel.EMISSIVE_TEXTURE);
|
||||
VertexConsumer emissiveBuffer = bufferSource.getBuffer(emissiveType);
|
||||
this.getRenderer().reRender(
|
||||
bakedModel,
|
||||
poseStack,
|
||||
bufferSource,
|
||||
animatable,
|
||||
emissiveType,
|
||||
emissiveBuffer,
|
||||
partialTick,
|
||||
0xF000F0,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
0xFFFFFFFF
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.HerculesBeetleModel;
|
||||
import com.craisinlord.antarchy.content.entity.HerculesBeetleEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class HerculesBeetleRenderer extends GeoEntityRenderer<HerculesBeetleEntity> {
|
||||
public HerculesBeetleRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new HerculesBeetleModel());
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.JumpyBugModel;
|
||||
import com.craisinlord.antarchy.content.entity.JumpyBugEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Axis;
|
||||
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 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 JumpyBugRenderer extends GeoEntityRenderer<JumpyBugEntity> {
|
||||
public JumpyBugRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new JumpyBugModel());
|
||||
this.shadowRadius = 6.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderType getRenderType(JumpyBugEntity animatable, ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
return animatable.isCamouflaged() ? RenderType.entityTranslucent(texture) : RenderType.entityCutoutNoCull(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getRenderColor(JumpyBugEntity animatable, float partialTick, int packedLight) {
|
||||
int alpha = Math.max(10, Math.min(255, Math.round(animatable.getVisualAlpha(partialTick) * 255.0F)));
|
||||
return Color.ofRGBA(255, 255, 255, alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRender(PoseStack poseStack, JumpyBugEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
|
||||
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
|
||||
this.shadowRadius = animatable.isCamouflaged() ? 1.0F : 6.0F;
|
||||
|
||||
if (animatable.isClingingToCeiling()) {
|
||||
poseStack.translate(0.0F, animatable.getBbHeight() + 0.05F, 0.0F);
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -8,7 +8,7 @@ import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
public class KrakenRenderer extends GeoEntityRenderer<KrakenEntity> {
|
||||
public KrakenRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new KrakenModel());
|
||||
this.shadowRadius = 3.6F;
|
||||
this.withScale(3.0F);
|
||||
this.shadowRadius = 2.0F;
|
||||
this.withScale(1.5F);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.LurkingTerrorModel;
|
||||
import com.craisinlord.antarchy.content.entity.LurkingTerrorEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class LurkingTerrorRenderer extends GeoEntityRenderer<LurkingTerrorEntity> {
|
||||
public LurkingTerrorRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new LurkingTerrorModel());
|
||||
this.shadowRadius = 0.5F;
|
||||
this.withScale(0.85F);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.OctopusBombModel;
|
||||
import com.craisinlord.antarchy.content.entity.OctopusBombEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class OctopusBombRenderer extends GeoEntityRenderer<OctopusBombEntity> {
|
||||
public OctopusBombRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new OctopusBombModel());
|
||||
this.shadowRadius = 1.2F;
|
||||
}
|
||||
}
|
||||
+40
@@ -2,13 +2,53 @@ package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.ScorpionModel;
|
||||
import com.craisinlord.antarchy.content.entity.ScorpionEntity;
|
||||
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.client.renderer.texture.OverlayTexture;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
import software.bernie.geckolib.renderer.layer.GeoRenderLayer;
|
||||
|
||||
public class ScorpionRenderer extends GeoEntityRenderer<ScorpionEntity> {
|
||||
|
||||
public ScorpionRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new ScorpionModel());
|
||||
this.shadowRadius = 0.6F;
|
||||
this.addRenderLayer(new ScorpionEmissiveLayer(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderType getRenderType(ScorpionEntity animatable, net.minecraft.resources.ResourceLocation texture, @Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
return RenderType.entityCutoutNoCull(texture);
|
||||
}
|
||||
|
||||
private static final class ScorpionEmissiveLayer extends GeoRenderLayer<ScorpionEntity> {
|
||||
private ScorpionEmissiveLayer(GeoEntityRenderer<ScorpionEntity> renderer) {
|
||||
super(renderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack poseStack, ScorpionEntity animatable, BakedGeoModel bakedModel, @Nullable RenderType renderType,
|
||||
MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, float partialTick,
|
||||
int packedLight, int packedOverlay) {
|
||||
RenderType emissiveType = RenderType.eyes(ScorpionModel.emissiveTextureFor(animatable));
|
||||
VertexConsumer emissiveBuffer = bufferSource.getBuffer(emissiveType);
|
||||
this.getRenderer().reRender(
|
||||
bakedModel,
|
||||
poseStack,
|
||||
bufferSource,
|
||||
animatable,
|
||||
emissiveType,
|
||||
emissiveBuffer,
|
||||
partialTick,
|
||||
0xF000F0,
|
||||
OverlayTexture.NO_OVERLAY,
|
||||
0xFFFFFFFF
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.entity.SpitBugProjectileEntity;
|
||||
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.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
public class SpitBugProjectileRenderer extends EntityRenderer<SpitBugProjectileEntity> {
|
||||
private static final ResourceLocation PROJECTILE_TEXTURE =
|
||||
ResourceLocation.withDefaultNamespace("textures/entity/enderdragon/dragon_fireball.png");
|
||||
|
||||
public SpitBugProjectileRenderer(EntityRendererProvider.Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getBlockLightLevel(SpitBugProjectileEntity entity, BlockPos pos) {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpitBugProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight) {
|
||||
poseStack.pushPose();
|
||||
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
|
||||
|
||||
float scale = entity.getRenderScale(partialTick);
|
||||
poseStack.scale(scale, scale, scale);
|
||||
|
||||
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(PROJECTILE_TEXTURE));
|
||||
PoseStack.Pose pose = poseStack.last();
|
||||
Matrix4f matrix4f = pose.pose();
|
||||
float alpha = entity.isLingering() ? 0.78F : 0.95F;
|
||||
|
||||
addVertex(vertexConsumer, pose, matrix4f, -0.5F, -0.5F, 0.0F, 1.0F, alpha, packedLight);
|
||||
addVertex(vertexConsumer, pose, matrix4f, 0.5F, -0.5F, 1.0F, 1.0F, alpha, packedLight);
|
||||
addVertex(vertexConsumer, pose, matrix4f, 0.5F, 0.5F, 1.0F, 0.0F, alpha, packedLight);
|
||||
addVertex(vertexConsumer, pose, matrix4f, -0.5F, 0.5F, 0.0F, 0.0F, alpha, packedLight);
|
||||
|
||||
poseStack.popPose();
|
||||
super.render(entity, entityYaw, partialTick, poseStack, buffer, packedLight);
|
||||
}
|
||||
|
||||
private static void addVertex(
|
||||
VertexConsumer vertexConsumer,
|
||||
PoseStack.Pose pose,
|
||||
Matrix4f matrix4f,
|
||||
float x,
|
||||
float y,
|
||||
float u,
|
||||
float v,
|
||||
float alpha,
|
||||
int packedLight
|
||||
) {
|
||||
vertexConsumer.addVertex(matrix4f, x, y, 0.0F)
|
||||
.setColor(0.55F, 1.0F, 0.35F, alpha)
|
||||
.setUv(u, v)
|
||||
.setOverlay(OverlayTexture.NO_OVERLAY)
|
||||
.setLight(packedLight)
|
||||
.setNormal(pose, 0.0F, 0.0F, 1.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(SpitBugProjectileEntity entity) {
|
||||
return PROJECTILE_TEXTURE;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.SpitBugModel;
|
||||
import com.craisinlord.antarchy.content.entity.SpitBugEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class SpitBugRenderer extends GeoEntityRenderer<SpitBugEntity> {
|
||||
public SpitBugRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new SpitBugModel());
|
||||
this.shadowRadius = 1.6F;
|
||||
this.withScale(0.85F);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.StinkBugModel;
|
||||
import com.craisinlord.antarchy.content.entity.StinkBugEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class StinkBugRenderer extends GeoEntityRenderer<StinkBugEntity> {
|
||||
private static final float ADULT_SCALE = 0.95F;
|
||||
private static final float ADULT_SHADOW_RADIUS = 0.2F;
|
||||
|
||||
public StinkBugRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new StinkBugModel());
|
||||
this.shadowRadius = ADULT_SHADOW_RADIUS;
|
||||
this.withScale(ADULT_SCALE);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.craisinlord.antarchy.content.client.renderer;
|
||||
|
||||
import com.craisinlord.antarchy.content.client.model.TermiteModel;
|
||||
import com.craisinlord.antarchy.content.entity.ant.TermiteEntity;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.cache.object.BakedGeoModel;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class TermiteRenderer extends GeoEntityRenderer<TermiteEntity> {
|
||||
private static final float ADULT_SHADOW_RADIUS = 0.3F;
|
||||
private static final float ADULT_SCALE = 0.85F;
|
||||
private static final float BABY_SCALE = 0.5F;
|
||||
|
||||
public TermiteRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new TermiteModel());
|
||||
this.shadowRadius = ADULT_SHADOW_RADIUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRender(PoseStack poseStack, TermiteEntity animatable, BakedGeoModel model, @Nullable MultiBufferSource bufferSource, @Nullable VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, int colour) {
|
||||
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, colour);
|
||||
|
||||
if (animatable.isBaby()) {
|
||||
poseStack.scale(ADULT_SCALE * BABY_SCALE, ADULT_SCALE * BABY_SCALE, ADULT_SCALE * BABY_SCALE);
|
||||
this.shadowRadius = ADULT_SHADOW_RADIUS * BABY_SCALE;
|
||||
} else {
|
||||
poseStack.scale(ADULT_SCALE, ADULT_SCALE, ADULT_SCALE);
|
||||
this.shadowRadius = ADULT_SHADOW_RADIUS;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@ public class ToreterrorRenderer extends GeoEntityRenderer<ToreterrorEntity> {
|
||||
|
||||
public ToreterrorRenderer(EntityRendererProvider.Context context) {
|
||||
super(context, new ToreterrorModel());
|
||||
this.shadowRadius = 2.0F;
|
||||
this.shadowRadius = 1.6F;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-1
@@ -24,7 +24,6 @@ public class UpwardFallingBlockRenderer extends EntityRenderer<UpwardFallingBloc
|
||||
if (blockState.isAir()) return;
|
||||
|
||||
poseStack.pushPose();
|
||||
// Center the block model on the entity origin
|
||||
poseStack.translate(-0.5, 0.0, -0.5);
|
||||
Minecraft.getInstance().getBlockRenderer().renderSingleBlock(
|
||||
blockState, poseStack, bufferSource, light, OverlayTexture.NO_OVERLAY
|
||||
|
||||
+2
-1
@@ -29,7 +29,8 @@ public class WaterBombRenderer extends EntityRenderer<WaterBombEntity> {
|
||||
public void render(WaterBombEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight) {
|
||||
poseStack.pushPose();
|
||||
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
|
||||
poseStack.scale(0.5F, 0.5F, 0.5F);
|
||||
float scale = entity.isHuge() ? 1.5F : 0.5F;
|
||||
poseStack.scale(scale, scale, scale);
|
||||
|
||||
VertexConsumer vertexConsumer = buffer.getBuffer(RenderType.entityTranslucent(WATER_TEXTURE));
|
||||
PoseStack.Pose pose = poseStack.last();
|
||||
|
||||
+29
@@ -5,6 +5,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class AntarchyDamageSources {
|
||||
public static DamageSource krakenMauling(ServerLevel level, Entity attacker) {
|
||||
@@ -87,6 +88,34 @@ public final class AntarchyDamageSources {
|
||||
);
|
||||
}
|
||||
|
||||
public static DamageSource toreterrorSpin(ServerLevel level, Entity attacker) {
|
||||
return new DamageSource(
|
||||
level.registryAccess()
|
||||
.registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolderOrThrow(AntarchyDamageTypes.TORETERROR_SPIN),
|
||||
attacker
|
||||
);
|
||||
}
|
||||
|
||||
public static DamageSource toreterrorJump(ServerLevel level, Entity attacker) {
|
||||
return new DamageSource(
|
||||
level.registryAccess()
|
||||
.registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolderOrThrow(AntarchyDamageTypes.TORETERROR_JUMP),
|
||||
attacker
|
||||
);
|
||||
}
|
||||
|
||||
public static DamageSource waterSoaked(Level level, @Nullable Entity directEntity, @Nullable Entity causingEntity) {
|
||||
return new DamageSource(
|
||||
level.registryAccess()
|
||||
.registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolderOrThrow(AntarchyDamageTypes.WATER_SOAKED),
|
||||
directEntity,
|
||||
causingEntity
|
||||
);
|
||||
}
|
||||
|
||||
private AntarchyDamageSources() {
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,24 @@ public final class AntarchyDamageTypes {
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "cornea_stalk_prick")
|
||||
);
|
||||
|
||||
public static final ResourceKey<DamageType> TORETERROR_SPIN =
|
||||
ResourceKey.create(
|
||||
Registries.DAMAGE_TYPE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "toreterror_spin")
|
||||
);
|
||||
|
||||
public static final ResourceKey<DamageType> TORETERROR_JUMP =
|
||||
ResourceKey.create(
|
||||
Registries.DAMAGE_TYPE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "toreterror_jump")
|
||||
);
|
||||
|
||||
public static final ResourceKey<DamageType> WATER_SOAKED =
|
||||
ResourceKey.create(
|
||||
Registries.DAMAGE_TYPE,
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "water_soaked")
|
||||
);
|
||||
|
||||
private AntarchyDamageTypes() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.effect;
|
||||
|
||||
import com.craisinlord.antarchy.content.StinkyBehavior;
|
||||
import net.minecraft.world.effect.MobEffect;
|
||||
import net.minecraft.world.effect.MobEffectCategory;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
|
||||
public final class StinkyMobEffect extends MobEffect {
|
||||
public StinkyMobEffect() {
|
||||
super(MobEffectCategory.HARMFUL, 0x66773A);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applyEffectTick(LivingEntity entity, int amplifier) {
|
||||
StinkyBehavior.tickStinkyTrail(entity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyEffectTickThisTick(int duration, int amplifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerBossEvent;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.BossEvent;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class AlphaMantisEntity extends MantisEntity {
|
||||
private final ServerBossEvent bossEvent =
|
||||
new ServerBossEvent(Component.translatable("entity.antarchy.alpha_mantis"), BossEvent.BossBarColor.GREEN, BossEvent.BossBarOverlay.PROGRESS);
|
||||
|
||||
private int summonCooldownTicks;
|
||||
|
||||
public AlphaMantisEntity(EntityType<? extends AlphaMantisEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.xpReward = 60;
|
||||
this.summonCooldownTicks = AntarchySettings.alphaMantisSummonIntervalTicks() / 2;
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.alphaMantisHealth())
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.alphaMantisAttackDamage())
|
||||
.add(Attributes.MOVEMENT_SPEED, AntarchySettings.alphaMantisMovementSpeed())
|
||||
.add(Attributes.FLYING_SPEED, AntarchySettings.alphaMantisFlyingSpeed())
|
||||
.add(Attributes.FOLLOW_RANGE, 48.0D)
|
||||
.add(Attributes.ARMOR, 8.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.8D);
|
||||
}
|
||||
|
||||
public static boolean checkAlphaMantisSpawnRules(EntityType<AlphaMantisEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (level.getDifficulty() == Difficulty.PEACEFUL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spawnReason != MobSpawnType.NATURAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean atSurface = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, pos).getY() <= pos.getY();
|
||||
boolean posOpen = level.isEmptyBlock(pos) && level.isEmptyBlock(pos.above()) && level.getFluidState(pos).isEmpty();
|
||||
boolean onSolidGround = level.getBlockState(pos.below()).isFaceSturdy(level, pos.below(), Direction.UP)
|
||||
&& !(level.getBlockState(pos.below()).getBlock() instanceof LeavesBlock);
|
||||
|
||||
if (!posOpen || !onSolidGround || !atSurface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only one alpha at a time in the area
|
||||
if (!level.getLevel().getEntitiesOfClass(AlphaMantisEntity.class, new AABB(pos).inflate(128.0D), Entity::isAlive).isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ignoreLightLevel = AntarchySettings.mantisIgnoreLightLevel();
|
||||
|
||||
// Daytime overworld spawns can't pass the vanilla darkness check, so no light gate here
|
||||
if (level.getLevel().isDay() && level.getBiome(pos).is(AntarchyTags.Biomes.MANTIS_OVERWORLD_SPAWN_BIOMES)) {
|
||||
return level.canSeeSky(pos);
|
||||
}
|
||||
|
||||
if (level.getLevel().isNight() && level.getBiome(pos).is(AntarchyTags.Biomes.MANTIS_SPAWN_BIOMES)) {
|
||||
return ignoreLightLevel || Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (this.level().isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isDeadOrDying()) {
|
||||
this.bossEvent.setProgress(0.0F);
|
||||
return;
|
||||
}
|
||||
|
||||
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
|
||||
this.tickMinionSummon();
|
||||
}
|
||||
|
||||
private void tickMinionSummon() {
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.summonCooldownTicks > 0) {
|
||||
this.summonCooldownTicks--;
|
||||
return;
|
||||
}
|
||||
this.summonCooldownTicks = AntarchySettings.alphaMantisSummonIntervalTicks();
|
||||
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int nearbyMinions = serverLevel.getEntitiesOfClass(
|
||||
MantisEntity.class,
|
||||
this.getBoundingBox().inflate(32.0D),
|
||||
mantis -> mantis.isAlive() && !(mantis instanceof AlphaMantisEntity)
|
||||
).size();
|
||||
int maxMinions = AntarchySettings.alphaMantisMaxMinions();
|
||||
if (nearbyMinions >= maxMinions) {
|
||||
return;
|
||||
}
|
||||
|
||||
int toSummon = Math.min(1 + this.random.nextInt(2), maxMinions - nearbyMinions);
|
||||
for (int i = 0; i < toSummon; i++) {
|
||||
MantisEntity minion = AntarchyObjects.MANTIS.get().create(serverLevel);
|
||||
if (minion == null) {
|
||||
continue;
|
||||
}
|
||||
double angle = this.random.nextDouble() * Math.PI * 2.0D;
|
||||
double distance = 3.0D + this.random.nextDouble() * 3.0D;
|
||||
minion.moveTo(
|
||||
this.getX() + Math.cos(angle) * distance,
|
||||
this.getY() + 0.5D,
|
||||
this.getZ() + Math.sin(angle) * distance,
|
||||
this.random.nextFloat() * 360.0F,
|
||||
0.0F
|
||||
);
|
||||
minion.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(minion.blockPosition()), MobSpawnType.MOB_SUMMONED, null);
|
||||
minion.setTarget(target);
|
||||
serverLevel.addFreshEntity(minion);
|
||||
}
|
||||
this.playSound(com.craisinlord.antarchy.content.AntarchySoundEvents.MANTIS_AMBIENT.get(), 2.0F, 0.6F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTarget(@Nullable LivingEntity target) {
|
||||
super.setTarget(target);
|
||||
// A boss that despawns mid-fight is no boss — stay loaded once engaged
|
||||
if (target != null) {
|
||||
this.setPersistenceRequired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSeenByPlayer(ServerPlayer player) {
|
||||
super.startSeenByPlayer(player);
|
||||
this.bossEvent.addPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSeenByPlayer(ServerPlayer player) {
|
||||
super.stopSeenByPlayer(player);
|
||||
this.bossEvent.removePlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomName(@Nullable Component name) {
|
||||
super.setCustomName(name);
|
||||
this.bossEvent.setName(this.getDisplayName());
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.animal.Cow;
|
||||
import net.minecraft.world.entity.Shearable;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -21,7 +20,6 @@ import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
@@ -47,7 +45,7 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
private static final String NAP_COOLDOWN_KEY = "NapCooldown";
|
||||
private static final String QUIRK_TICKS_KEY = "QuirkTicks";
|
||||
private static final String SHEARED_KEY = "Sheared";
|
||||
private static final String GRASS_EAT_TICKS_KEY = "GrassEatTicks";
|
||||
private static final String REGROW_TICKS_KEY = "RegrowTicks";
|
||||
private static final String MAIN_CONTROLLER = "main_controller";
|
||||
private static final String SLEEP_TRANSITION_CONTROLLER = "sleep_transition_controller";
|
||||
private static final String START_SLEEP_TRIGGER = "start_sleep";
|
||||
@@ -60,7 +58,7 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
private static final int DAY_NAP_MAX_TICKS = 20 * 60 * 2;
|
||||
private static final int DAY_NAP_COOLDOWN_TICKS = 20 * 60 * 10;
|
||||
private static final int NIGHT_NAP_COOLDOWN_TICKS = 20 * 60 * 8;
|
||||
private static final int GRASS_REGROW_TICKS = 20 * 60 * 3;
|
||||
private static final int APPLE_REGROW_TICKS = 20 * 60 * 3;
|
||||
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
@@ -73,7 +71,7 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
private int sleepTicksRemaining;
|
||||
private int napCooldownTicks;
|
||||
private int idleAnimationCycleTicks;
|
||||
private int grassEatTicks;
|
||||
private int regrowTicks;
|
||||
|
||||
protected AppleCowEntity(EntityType<? extends Cow> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
@@ -92,7 +90,6 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
protected void registerGoals() {
|
||||
super.registerGoals();
|
||||
this.goalSelector.addGoal(0, new AppleCowSleepGoal());
|
||||
this.goalSelector.addGoal(1, new AppleCowGrazeGoal());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -148,8 +145,12 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSheared()) {
|
||||
this.grassEatTicks = 0;
|
||||
if (this.isSheared()) {
|
||||
this.regrowTicks++;
|
||||
if (this.regrowTicks >= APPLE_REGROW_TICKS) {
|
||||
this.setSheared(false);
|
||||
this.regrowTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldStartNap()) {
|
||||
@@ -190,7 +191,7 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
tag.putInt(NAP_COOLDOWN_KEY, this.napCooldownTicks);
|
||||
tag.putInt(QUIRK_TICKS_KEY, this.getQuirkTicks());
|
||||
tag.putBoolean(SHEARED_KEY, this.isSheared());
|
||||
tag.putInt(GRASS_EAT_TICKS_KEY, this.grassEatTicks);
|
||||
tag.putInt(REGROW_TICKS_KEY, this.regrowTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -201,7 +202,7 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
this.napCooldownTicks = tag.contains(NAP_COOLDOWN_KEY) ? tag.getInt(NAP_COOLDOWN_KEY) : randomNapCooldown();
|
||||
this.setQuirkTicks(tag.getInt(QUIRK_TICKS_KEY));
|
||||
this.setSheared(tag.getBoolean(SHEARED_KEY));
|
||||
this.grassEatTicks = tag.getInt(GRASS_EAT_TICKS_KEY);
|
||||
this.regrowTicks = tag.getInt(REGROW_TICKS_KEY);
|
||||
}
|
||||
|
||||
public boolean isSleeping() {
|
||||
@@ -346,12 +347,24 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
}
|
||||
|
||||
this.setSheared(true);
|
||||
this.grassEatTicks = 0;
|
||||
this.regrowTicks = 0;
|
||||
this.stopCowSleeping();
|
||||
this.playSound(SoundEvents.SHEEP_SHEAR, 1.0F, 1.0F);
|
||||
this.spawnAtLocation(new ItemStack(this.shearDropItem(), 2), this.getBbHeight() * 0.66F);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropCustomDeathLoot(ServerLevel level, net.minecraft.world.damagesource.DamageSource damageSource, boolean recentlyHit) {
|
||||
super.dropCustomDeathLoot(level, damageSource, recentlyHit);
|
||||
|
||||
if (this.isBaby() || this.isSheared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = this instanceof AppleCowEntityVariants.EnchantedGoldenAppleCow ? 1 : 1 + this.random.nextInt(2);
|
||||
this.spawnAtLocation(new ItemStack(this.shearDropItem(), count));
|
||||
}
|
||||
|
||||
private net.minecraft.world.item.Item shearDropItem() {
|
||||
if (this instanceof AppleCowEntityVariants.EnchantedGoldenAppleCow) {
|
||||
return Items.ENCHANTED_GOLDEN_APPLE;
|
||||
@@ -389,57 +402,4 @@ public abstract class AppleCowEntity extends Cow implements GeoEntity, Shearable
|
||||
AppleCowEntity.this.applySleepStillness();
|
||||
}
|
||||
}
|
||||
|
||||
private final class AppleCowGrazeGoal extends Goal {
|
||||
private AppleCowGrazeGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return AppleCowEntity.this.isSheared()
|
||||
&& AppleCowEntity.this.isOnGrassBlock()
|
||||
&& AppleCowEntity.this.isEligibleForSleep()
|
||||
&& AppleCowEntity.this.random.nextInt(20) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return AppleCowEntity.this.isSheared()
|
||||
&& AppleCowEntity.this.isOnGrassBlock()
|
||||
&& AppleCowEntity.this.grassEatTicks < GRASS_REGROW_TICKS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
AppleCowEntity.this.getNavigation().stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
AppleCowEntity.this.getNavigation().stop();
|
||||
if (!AppleCowEntity.this.level().isClientSide && AppleCowEntity.this.isSheared() && AppleCowEntity.this.isOnGrassBlock()) {
|
||||
AppleCowEntity.this.grassEatTicks++;
|
||||
if (AppleCowEntity.this.grassEatTicks >= GRASS_REGROW_TICKS) {
|
||||
AppleCowEntity.this.setSheared(false);
|
||||
AppleCowEntity.this.grassEatTicks = 0;
|
||||
AppleCowEntity.this.playSound(SoundEvents.GENERIC_EAT, 0.7F, 1.0F + AppleCowEntity.this.random.nextFloat() * 0.2F);
|
||||
} else if (AppleCowEntity.this.grassEatTicks % 40 == 0) {
|
||||
AppleCowEntity.this.playSound(SoundEvents.GENERIC_EAT, 0.4F, 1.0F + AppleCowEntity.this.random.nextFloat() * 0.15F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (!AppleCowEntity.this.isSheared()) {
|
||||
AppleCowEntity.this.grassEatTicks = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOnGrassBlock() {
|
||||
BlockPos below = this.blockPosition().below();
|
||||
return this.level().getBlockState(below).is(Blocks.GRASS_BLOCK);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
@@ -48,17 +54,21 @@ import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.animation.keyframe.event.builtin.AutoPlayingSoundKeyframeHandler;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class BomberEntity extends Monster implements GeoEntity {
|
||||
private static final ResourceLocation INTENTIONALLY_EMPTY_SOUND_ID = ResourceLocation.withDefaultNamespace("intentionally_empty");
|
||||
private static final EntityDataAccessor<Integer> FUSE_TICKS_DATA =
|
||||
SynchedEntityData.defineId(BomberEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> DETONATING_DATA =
|
||||
SynchedEntityData.defineId(BomberEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private static final int DEFAULT_FUSE_TICKS = 80;
|
||||
private static final int DEFAULT_FUSE_TICKS = 120;
|
||||
private static final int EXPLODE_ANIM_START_TICKS = 25;
|
||||
private static final int FLASH_INTERVAL_TICKS = 5;
|
||||
private static final double KNOCKBACK_MULTIPLIER = 5.5D;
|
||||
private static final float EXPLOSION_RADIUS_MULTIPLIER = 0.45F;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
@@ -117,25 +127,25 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.MAX_HEALTH)).setBaseValue(maxHealth);
|
||||
this.setHealth((float) maxHealth);
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.ATTACK_DAMAGE)).setBaseValue(attackDamage);
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
|
||||
SpawnGroupData spawnData = super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
|
||||
this.primeFuse();
|
||||
return spawnData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHurtTarget(Entity target) {
|
||||
if (!(target instanceof LivingEntity livingTarget)) {
|
||||
if (!(target instanceof LivingEntity)) {
|
||||
return super.doHurtTarget(target);
|
||||
}
|
||||
|
||||
livingTarget.hurt(this.damageSources().mobAttack(this), (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE));
|
||||
this.primeFuse();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
if (source.is(DamageTypeTags.IS_FIRE)) {
|
||||
if (source.is(DamageTypeTags.IS_FIRE) || source.is(DamageTypeTags.IS_EXPLOSION)) {
|
||||
this.primeFuse();
|
||||
return false;
|
||||
}
|
||||
|
||||
Entity directEntity = source.getDirectEntity();
|
||||
@@ -143,7 +153,19 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
this.primeFuse();
|
||||
}
|
||||
|
||||
return super.hurt(source, amount);
|
||||
this.playSound(AntarchySoundEvents.BOMBER_KNOCK.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||
this.pushAwayFrom(source);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void knockback(double strength, double x, double z) {
|
||||
super.knockback(strength * KNOCKBACK_MULTIPLIER, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return AntarchySoundEvents.BOMBER_KNOCK.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,7 +198,10 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 0, this::mainAnimController));
|
||||
controllers.add(new AnimationController<>(this, "movement_controller", 0, this::movementAnimController)
|
||||
.setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>()));
|
||||
controllers.add(new AnimationController<>(this, "explode_controller", 0, this::explodeAnimController)
|
||||
.setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,11 +213,12 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (!this.level().isClientSide && this.detonating) {
|
||||
if (this.detonating && this.fuseTicks <= EXPLODE_ANIM_START_TICKS) {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(0.0D, 0.0D, 0.0D);
|
||||
this.hasImpulse = false;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
|
||||
if (!this.level().isClientSide && this.detonating) {
|
||||
if (this.fuseTicks > 0) {
|
||||
this.fuseTicks--;
|
||||
this.entityData.set(FUSE_TICKS_DATA, this.fuseTicks);
|
||||
@@ -218,15 +244,23 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
&& (this.getFuseTicks() / FLASH_INTERVAL_TICKS) % 2 == 0;
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<BomberEntity> state) {
|
||||
private PlayState movementAnimController(AnimationState<BomberEntity> state) {
|
||||
if (this.isDetonating() && this.getFuseTicks() <= EXPLODE_ANIM_START_TICKS) {
|
||||
return PlayState.STOP;
|
||||
}
|
||||
|
||||
return state.setAndContinue(this.isMovingForAnimation() ? WALK_ANIM : IDLE_ANIM);
|
||||
}
|
||||
|
||||
private PlayState explodeAnimController(AnimationState<BomberEntity> state) {
|
||||
if (this.isDetonating() && this.getFuseTicks() <= EXPLODE_ANIM_START_TICKS) {
|
||||
return state.setAndContinue(EXPLODE_ANIM);
|
||||
}
|
||||
|
||||
return state.setAndContinue(state.isMoving() ? WALK_ANIM : IDLE_ANIM);
|
||||
return PlayState.STOP;
|
||||
}
|
||||
|
||||
private void primeFuse() {
|
||||
public void primeFuse() {
|
||||
if (this.detonating) {
|
||||
return;
|
||||
}
|
||||
@@ -235,10 +269,7 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
this.fuseTicks = DEFAULT_FUSE_TICKS;
|
||||
this.entityData.set(DETONATING_DATA, true);
|
||||
this.entityData.set(FUSE_TICKS_DATA, this.fuseTicks);
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.hasImpulse = false;
|
||||
this.playSound(SoundEvents.TNT_PRIMED, 1.0F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||
this.playSound(AntarchySoundEvents.BOMBER_EXPLODE.get(), 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
private void detonate() {
|
||||
@@ -250,8 +281,22 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
double x = this.getX();
|
||||
double y = this.getY(0.0625D);
|
||||
double z = this.getZ();
|
||||
float radius = (float) AntarchySettings.bomberExplosionRadius();
|
||||
serverLevel.explode(this, x, y, z, radius, ExplosionInteraction.TNT);
|
||||
float radius = (float) (AntarchySettings.bomberExplosionRadius() * EXPLOSION_RADIUS_MULTIPLIER);
|
||||
serverLevel.playSound(null, x, y, z, SoundEvents.GENERIC_EXPLODE.value(), SoundSource.HOSTILE, 0.55F, 1.0F);
|
||||
serverLevel.explode(
|
||||
this,
|
||||
null,
|
||||
null,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
radius,
|
||||
false,
|
||||
ExplosionInteraction.TNT,
|
||||
ParticleTypes.EXPLOSION_EMITTER,
|
||||
ParticleTypes.EXPLOSION,
|
||||
BuiltInRegistries.SOUND_EVENT.getHolder(INTENTIONALLY_EMPTY_SOUND_ID).orElseThrow()
|
||||
);
|
||||
|
||||
double extraDamage = AntarchySettings.bomberExplosionDamage();
|
||||
if (extraDamage > 0.0D) {
|
||||
@@ -274,6 +319,30 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
this.discard();
|
||||
}
|
||||
|
||||
private void pushAwayFrom(DamageSource source) {
|
||||
Entity sourceEntity = source.getDirectEntity();
|
||||
if (sourceEntity == null) {
|
||||
sourceEntity = source.getEntity();
|
||||
}
|
||||
if (sourceEntity == null || sourceEntity == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
double dx = this.getX() - sourceEntity.getX();
|
||||
double dz = this.getZ() - sourceEntity.getZ();
|
||||
double distance = Math.sqrt(dx * dx + dz * dz);
|
||||
if (distance < 1.0E-4D) {
|
||||
return;
|
||||
}
|
||||
|
||||
double strength = 0.4D * KNOCKBACK_MULTIPLIER;
|
||||
this.push(dx / distance * strength, 0.08D, dz / distance * strength);
|
||||
}
|
||||
|
||||
private boolean isMovingForAnimation() {
|
||||
return this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-4D || this.getNavigation().isInProgress();
|
||||
}
|
||||
|
||||
private final class BomberAttackGoal extends MeleeAttackGoal {
|
||||
private BomberAttackGoal() {
|
||||
super(BomberEntity.this, 1.1D, true);
|
||||
@@ -281,12 +350,12 @@ public class BomberEntity extends Monster implements GeoEntity {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return !BomberEntity.this.detonating && super.canUse();
|
||||
return !BomberEntity.this.isDetonating() || BomberEntity.this.getFuseTicks() > EXPLODE_ANIM_START_TICKS && super.canUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return !BomberEntity.this.detonating && super.canContinueToUse();
|
||||
return !BomberEntity.this.isDetonating() || BomberEntity.this.getFuseTicks() > EXPLODE_ANIM_START_TICKS && super.canContinueToUse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
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.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.animal.AbstractFish;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class CheepEntity extends AbstractFish implements GeoEntity {
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenLoop("attack");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
public CheepEntity(EntityType<? extends CheepEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
super.registerGoals();
|
||||
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.2D, true));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return AbstractFish.createAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.cheepHealth())
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.cheepAttackDamage())
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.5D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getBucketItemStack() {
|
||||
return new ItemStack(Items.WATER_BUCKET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.COD_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.COD_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource source) {
|
||||
return SoundEvents.COD_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getFlopSound() {
|
||||
return SoundEvents.COD_FLOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<CheepEntity> state) {
|
||||
if (this.isAggressive()) {
|
||||
return state.setAndContinue(ATTACK_ANIM);
|
||||
}
|
||||
if (state.isMoving()) {
|
||||
return state.setAndContinue(SWIM_ANIM);
|
||||
}
|
||||
return state.setAndContinue(IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class CreepingHorrorEntity extends Monster implements GeoEntity {
|
||||
private static final EntityDataAccessor<Boolean> CLIMBING =
|
||||
SynchedEntityData.defineId(CreepingHorrorEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenLoop("attack");
|
||||
private static final byte ATTACK_ANIM_EVENT = 4;
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
private int attackAnimTicks = 0;
|
||||
|
||||
public CreepingHorrorEntity(EntityType<? extends CreepingHorrorEntity> type, Level level) {
|
||||
super(type, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float maxUpStep() {
|
||||
return 1.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(CLIMBING, false);
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.creepingHorrorHealth())
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.38D)
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.creepingHorrorAttackDamage())
|
||||
.add(Attributes.FOLLOW_RANGE, 16.0D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<CreepingHorrorEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockPos belowPos = pos.below();
|
||||
BlockState belowState = level.getBlockState(belowPos);
|
||||
boolean validSupport = !belowState.is(Blocks.BEDROCK)
|
||||
&& belowState.blocksMotion()
|
||||
&& belowState.isFaceSturdy(level, belowPos, Direction.UP)
|
||||
&& belowState.isCollisionShapeFullBlock(level, belowPos);
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& validSupport
|
||||
&& level.isEmptyBlock(pos)
|
||||
&& level.isEmptyBlock(pos.above())
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
|
||||
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.85D));
|
||||
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
||||
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClimbable() {
|
||||
return this.entityData.get(CLIMBING);
|
||||
}
|
||||
|
||||
public boolean isWallClimbing() {
|
||||
return this.entityData.get(CLIMBING) && !this.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 3, this::mainAnimController));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<CreepingHorrorEntity> state) {
|
||||
if (attackAnimTicks > 0) {
|
||||
return state.setAndContinue(ATTACK_ANIM);
|
||||
}
|
||||
if (state.isMoving()) {
|
||||
return state.setAndContinue(WALK_ANIM);
|
||||
}
|
||||
return state.setAndContinue(IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHurtTarget(net.minecraft.world.entity.Entity target) {
|
||||
boolean result = super.doHurtTarget(target);
|
||||
if (result) {
|
||||
this.playSound(AntarchySoundEvents.CREEPING_HORROR_BITE.get(), 1.0F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
attackAnimTicks = 20;
|
||||
this.level().broadcastEntityEvent(this, ATTACK_ANIM_EVENT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return AntarchySoundEvents.CREEPING_HORROR_GROWL.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(net.minecraft.world.damagesource.DamageSource source) {
|
||||
return AntarchySoundEvents.CREEPING_HORROR_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return AntarchySoundEvents.CREEPING_HORROR_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEntityEvent(byte id) {
|
||||
if (id == ATTACK_ANIM_EVENT) {
|
||||
attackAnimTicks = 20;
|
||||
return;
|
||||
}
|
||||
super.handleEntityEvent(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (attackAnimTicks > 0) attackAnimTicks--;
|
||||
if (this.level().isClientSide()) return;
|
||||
|
||||
boolean climbing = this.horizontalCollision;
|
||||
this.entityData.set(CLIMBING, climbing);
|
||||
|
||||
if (climbing && this.getTarget() != null) {
|
||||
LivingEntity target = this.getTarget();
|
||||
Vec3 toTarget = target.position().subtract(this.position());
|
||||
double hDist = Math.sqrt(toTarget.x * toTarget.x + toTarget.z * toTarget.z);
|
||||
double speed = this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 2.8D;
|
||||
if (hDist > 0.01D) {
|
||||
this.setDeltaMovement(
|
||||
(toTarget.x / hDist) * speed,
|
||||
0.22D,
|
||||
(toTarget.z / hDist) * speed
|
||||
);
|
||||
} else {
|
||||
Vec3 mov = this.getDeltaMovement();
|
||||
this.setDeltaMovement(mov.x, 0.22D, mov.z);
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
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.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Entity.MoveFunction;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.control.SmoothSwimmingLookControl;
|
||||
import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.BreathAirGoal;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomSwimmingGoal;
|
||||
import net.minecraft.world.entity.ai.goal.TryFindWaterGoal;
|
||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
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.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class DorrieEntity extends Animal implements GeoEntity {
|
||||
private static final ResourceLocation CHEEP_ITEM_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "cheep");
|
||||
|
||||
private static final EntityDataAccessor<Boolean> HAS_SADDLE =
|
||||
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Boolean> TAMED =
|
||||
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Integer> JUMP_CHARGE =
|
||||
SynchedEntityData.defineId(DorrieEntity.class, EntityDataSerializers.INT);
|
||||
|
||||
private static final int MAX_CHARGE_TICKS = 40;
|
||||
private static final float WATER_RIDE_SPEED = 1.26F;
|
||||
private static final float LAND_BEACH_SPEED = 0.04F;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation JUMP_START_ANIM = RawAnimation.begin().thenPlay("jump_1");
|
||||
private static final RawAnimation JUMP_LOOP_ANIM = RawAnimation.begin().thenLoop("jump_2");
|
||||
private static final RawAnimation JUMP_LAND_ANIM = RawAnimation.begin().thenPlay("jump_3");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private static final int RIDES_TO_TAME = 4;
|
||||
private static final int BUCK_DELAY_TICKS = 60;
|
||||
|
||||
private int chargeTicks = 0;
|
||||
private boolean isCharging = false;
|
||||
private boolean isLeaping = false;
|
||||
private boolean wasInAir = false;
|
||||
private int landAnimTicks = 0;
|
||||
private int rideAttempts = 0;
|
||||
private int buckTimer = 0;
|
||||
private int swimForwardTicks = 0;
|
||||
|
||||
public DorrieEntity(EntityType<? extends DorrieEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.moveControl = new SmoothSwimmingMoveControl(this, 85, 10, 0.02F, 1.0F, true);
|
||||
this.lookControl = new SmoothSwimmingLookControl(this, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(HAS_SADDLE, false);
|
||||
builder.define(TAMED, false);
|
||||
builder.define(JUMP_CHARGE, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
return new WaterBoundPathNavigation(this, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new BreathAirGoal(this));
|
||||
this.goalSelector.addGoal(2, new TryFindWaterGoal(this));
|
||||
this.goalSelector.addGoal(3, new HuntCheepGoal(this));
|
||||
this.goalSelector.addGoal(4, new RandomSwimmingGoal(this, 1.0D, 10));
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.dorrieHealth())
|
||||
.add(Attributes.MOVEMENT_SPEED, 1.0D)
|
||||
.add(Attributes.FOLLOW_RANGE, 16.0D)
|
||||
.add(Attributes.ATTACK_DAMAGE, 4.0D);
|
||||
}
|
||||
|
||||
public boolean hasSaddle() {
|
||||
return this.entityData.get(HAS_SADDLE);
|
||||
}
|
||||
|
||||
public void setSaddle(boolean saddle) {
|
||||
this.entityData.set(HAS_SADDLE, saddle);
|
||||
}
|
||||
|
||||
public boolean isTamed() {
|
||||
return this.entityData.get(TAMED);
|
||||
}
|
||||
|
||||
public void setTamed(boolean tamed) {
|
||||
this.entityData.set(TAMED, tamed);
|
||||
}
|
||||
|
||||
public int getJumpCharge() {
|
||||
return this.entityData.get(JUMP_CHARGE);
|
||||
}
|
||||
|
||||
private void setJumpCharge(int charge) {
|
||||
this.entityData.set(JUMP_CHARGE, charge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putBoolean("Tamed", this.isTamed());
|
||||
tag.putBoolean("HasSaddle", this.hasSaddle());
|
||||
tag.putInt("RideAttempts", this.rideAttempts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.setTamed(tag.getBoolean("Tamed"));
|
||||
this.setSaddle(tag.getBoolean("HasSaddle"));
|
||||
this.rideAttempts = tag.getInt("RideAttempts");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
if (!this.isTamed()) {
|
||||
if (!this.level().isClientSide) {
|
||||
player.startRiding(this);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(this.level().isClientSide);
|
||||
}
|
||||
|
||||
if (this.hasSaddle() && stack.isEmpty() && !player.isPassengerOfSameVehicle(this)) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setSaddle(false);
|
||||
this.spawnAtLocation(new ItemStack(Items.SADDLE));
|
||||
this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(this.level().isClientSide);
|
||||
}
|
||||
|
||||
if (!this.hasSaddle() && stack.is(Items.SADDLE)) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setSaddle(true);
|
||||
if (!player.getAbilities().instabuild) {
|
||||
stack.shrink(1);
|
||||
}
|
||||
this.playSound(SoundEvents.HORSE_SADDLE, 1.0F, 1.0F);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(this.level().isClientSide);
|
||||
}
|
||||
|
||||
if (this.hasSaddle() && !player.isPassengerOfSameVehicle(this)) {
|
||||
if (!this.level().isClientSide) {
|
||||
player.startRiding(this);
|
||||
}
|
||||
return InteractionResult.sidedSuccess(this.level().isClientSide);
|
||||
}
|
||||
|
||||
return super.mobInteract(player, hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public LivingEntity getControllingPassenger() {
|
||||
if (!this.isTamed() || !this.hasSaddle()) {
|
||||
return null;
|
||||
}
|
||||
Entity passenger = this.getFirstPassenger();
|
||||
if (passenger instanceof Player player) {
|
||||
return player;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vec3 getRiddenInput(Player player, Vec3 travelVector) {
|
||||
float forward = player.zza;
|
||||
float strafe = player.xxa * 0.5F;
|
||||
return new Vec3(strafe, 0, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getRiddenSpeed(Player player) {
|
||||
float speed = (this.isInWater() || this.isInLava())
|
||||
? WATER_RIDE_SPEED * (1.0F + Math.min(this.swimForwardTicks, 60) * 0.025F)
|
||||
: LAND_BEACH_SPEED;
|
||||
return (float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.getControllingPassenger() instanceof Player player) {
|
||||
this.setYRot(player.getYRot());
|
||||
this.yRotO = this.getYRot();
|
||||
this.yBodyRot = this.getYRot();
|
||||
this.yHeadRot = this.getYRot();
|
||||
if (this.isInWater() || this.isInLava()) {
|
||||
if (player.zza > 0.05F) {
|
||||
this.swimForwardTicks = Math.min(this.swimForwardTicks + 1, 60);
|
||||
} else {
|
||||
this.swimForwardTicks = Math.max(this.swimForwardTicks - 2, 0);
|
||||
}
|
||||
} else {
|
||||
this.swimForwardTicks = 0;
|
||||
}
|
||||
} else {
|
||||
this.swimForwardTicks = 0;
|
||||
}
|
||||
|
||||
if (!this.level().isClientSide && !this.isTamed() && this.getFirstPassenger() instanceof Player rider) {
|
||||
buckTimer++;
|
||||
if (buckTimer >= BUCK_DELAY_TICKS) {
|
||||
rider.stopRiding();
|
||||
buckTimer = 0;
|
||||
rideAttempts++;
|
||||
if (rideAttempts >= RIDES_TO_TAME) {
|
||||
this.setTamed(true);
|
||||
this.playSound(SoundEvents.HORSE_BREATHE, 0.8F, 1.0F);
|
||||
} else {
|
||||
this.playSound(SoundEvents.HORSE_ANGRY, 0.8F, 1.0F);
|
||||
}
|
||||
}
|
||||
} else if (!this.isTamed() && this.getFirstPassenger() == null) {
|
||||
buckTimer = 0;
|
||||
}
|
||||
|
||||
if (!this.level().isClientSide && this.getControllingPassenger() instanceof Player && isCharging) {
|
||||
chargeTicks = Math.min(chargeTicks + 1, MAX_CHARGE_TICKS);
|
||||
setJumpCharge((int) ((chargeTicks / (float) MAX_CHARGE_TICKS) * 100));
|
||||
}
|
||||
|
||||
if (this.isInWater() && !isLeaping) {
|
||||
Vec3 mov = this.getDeltaMovement();
|
||||
double push = this.isUnderWater() ? 0.07D : 0.015D;
|
||||
double cappedY = this.isUnderWater() ? 0.3D : 0.05D;
|
||||
this.setDeltaMovement(mov.x, Math.min(mov.y + push, cappedY), mov.z);
|
||||
}
|
||||
|
||||
if (landAnimTicks > 0) {
|
||||
landAnimTicks--;
|
||||
}
|
||||
|
||||
boolean inAirNow = !this.onGround() && !this.isInWater() && !this.isInLava();
|
||||
if (wasInAir && !inAirNow && isLeaping) {
|
||||
isLeaping = false;
|
||||
landAnimTicks = 12;
|
||||
}
|
||||
wasInAir = inAirNow;
|
||||
}
|
||||
|
||||
public void setPressingJump(boolean pressing) {}
|
||||
|
||||
public void startJumpCharge() {
|
||||
if (!this.level().isClientSide) {
|
||||
isCharging = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseJump() {
|
||||
if (!this.level().isClientSide && isCharging) {
|
||||
float power = chargeTicks / (float) MAX_CHARGE_TICKS;
|
||||
if (power > 0.05F) {
|
||||
Vec3 look = this.getLookAngle();
|
||||
Vec3 current = this.getDeltaMovement();
|
||||
double forwardAdd = power * 0.5D;
|
||||
double vertical = 0.55D + power * 0.9D;
|
||||
this.setDeltaMovement(
|
||||
current.x + look.x * forwardAdd,
|
||||
vertical,
|
||||
current.z + look.z * forwardAdd
|
||||
);
|
||||
this.hasImpulse = true;
|
||||
isLeaping = true;
|
||||
}
|
||||
isCharging = false;
|
||||
chargeTicks = 0;
|
||||
setJumpCharge(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void positionRider(Entity passenger, MoveFunction moveFunction) {
|
||||
if (this.hasPassenger(passenger)) {
|
||||
Vec3 forward = this.getLookAngle().multiply(1.0D, 0.0D, 1.0D);
|
||||
moveFunction.accept(passenger,
|
||||
this.getX() - forward.x * 0.25D,
|
||||
this.getY() + 0.5D,
|
||||
this.getZ() - forward.z * 0.25D);
|
||||
}
|
||||
}
|
||||
|
||||
public double getPassengersRidingOffset() {
|
||||
return 0.6D;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dropEquipment() {
|
||||
super.dropEquipment();
|
||||
if (this.hasSaddle()) {
|
||||
this.spawnAtLocation(new ItemStack(Items.SADDLE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public DorrieEntity getBreedOffspring(ServerLevel level, AgeableMob mob) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.DOLPHIN_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource source) {
|
||||
return SoundEvents.DOLPHIN_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.DOLPHIN_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getSwimSound() {
|
||||
return SoundEvents.DOLPHIN_SWIM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<DorrieEntity> state) {
|
||||
if (landAnimTicks > 0) {
|
||||
return state.setAndContinue(JUMP_LAND_ANIM);
|
||||
}
|
||||
if (isLeaping) {
|
||||
return state.setAndContinue(wasInAir ? JUMP_LOOP_ANIM : JUMP_START_ANIM);
|
||||
}
|
||||
return state.setAndContinue(state.isMoving() ? SWIM_ANIM : IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFood(ItemStack stack) {
|
||||
return this.isCheepItem(stack);
|
||||
}
|
||||
|
||||
private boolean isCheepItem(ItemStack stack) {
|
||||
return !stack.isEmpty() && stack.is(BuiltInRegistries.ITEM.getOptional(CHEEP_ITEM_ID).orElse(Items.AIR));
|
||||
}
|
||||
|
||||
private static class HuntCheepGoal extends Goal {
|
||||
private static final int COOLDOWN_TICKS = 600;
|
||||
private static final double HUNT_RANGE = 16.0D;
|
||||
private static final double ATTACK_RANGE_SQ = 2.5D * 2.5D;
|
||||
|
||||
private final DorrieEntity dorrie;
|
||||
private CheepEntity target;
|
||||
private int cooldown = 0;
|
||||
|
||||
HuntCheepGoal(DorrieEntity dorrie) {
|
||||
this.dorrie = dorrie;
|
||||
setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (dorrie.level().isClientSide) {
|
||||
return false;
|
||||
}
|
||||
if (cooldown > 0) {
|
||||
cooldown--;
|
||||
return false;
|
||||
}
|
||||
if (dorrie.getControllingPassenger() != null) {
|
||||
return false;
|
||||
}
|
||||
List<CheepEntity> nearby = dorrie.level().getEntitiesOfClass(
|
||||
CheepEntity.class, dorrie.getBoundingBox().inflate(HUNT_RANGE));
|
||||
if (nearby.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
nearby.sort((a, b) -> Double.compare(a.distanceToSqr(dorrie), b.distanceToSqr(dorrie)));
|
||||
target = nearby.get(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return target != null && target.isAlive()
|
||||
&& dorrie.getControllingPassenger() == null
|
||||
&& dorrie.distanceToSqr(target) <= HUNT_RANGE * HUNT_RANGE * 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
dorrie.getNavigation().moveTo(target, 1.4D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
dorrie.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
dorrie.getNavigation().moveTo(target, 1.4D);
|
||||
if (dorrie.distanceToSqr(target) <= ATTACK_RANGE_SQ) {
|
||||
target.hurt(dorrie.damageSources().mobAttack(dorrie),
|
||||
(float) dorrie.getAttributeValue(Attributes.ATTACK_DAMAGE));
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
target = null;
|
||||
dorrie.getNavigation().stop();
|
||||
cooldown = COOLDOWN_TICKS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package com.craisinlord.antarchy.content.entity;
|
||||
import com.craisinlord.antarchy.content.entity.trades.DrTrayaurusTradeManager;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
@@ -80,6 +83,21 @@ public class DrTrayaurusEntity extends AbstractVillager {
|
||||
this.overrideXp(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.VILLAGER_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return SoundEvents.VILLAGER_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.VILLAGER_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
+99
-46
@@ -48,19 +48,24 @@ import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
private static final byte CLAW_ATTACK_ANIM_EVENT = 4;
|
||||
private static final byte SNAP_ATTACK_ANIM_EVENT = 4;
|
||||
private static final byte STING_ATTACK_ANIM_EVENT = 5;
|
||||
private static final byte AGRO_ANIM_EVENT = 6;
|
||||
|
||||
private static final int CLAW_ATTACK_ANIM_TICKS = 12;
|
||||
private static final int CLAW_ATTACK_HIT_TICK = 6;
|
||||
private static final int CLAW_ATTACK_COOLDOWN_TICKS = 16;
|
||||
private static final int STING_ATTACK_ANIM_TICKS = 18;
|
||||
private static final int STING_ATTACK_HIT_TICK = 9;
|
||||
private static final int STING_ATTACK_COOLDOWN_TICKS = 42;
|
||||
private static final int POISON_DURATION_TICKS = 100;
|
||||
private static final int WEAKNESS_DURATION_TICKS = 80;
|
||||
private static final int SUMMON_INTERVAL_TICKS = 180;
|
||||
private static final int MAX_SUMMONED_SCORPIONS = 4;
|
||||
private static final int SNAP_ATTACK_ANIM_TICKS = 23;
|
||||
private static final int SNAP_ATTACK_HIT_TICK = 12;
|
||||
private static final int SNAP_ATTACK_COOLDOWN_TICKS = 28;
|
||||
private static final int STING_ATTACK_ANIM_TICKS = 25;
|
||||
private static final int STING_ATTACK_HIT_TICK = 13;
|
||||
private static final int STING_ATTACK_COOLDOWN_TICKS = 60;
|
||||
private static final int AGRO_ANIM_TICKS = 30;
|
||||
private static final int DEATH_ANIM_TICKS = 40;
|
||||
private static final float STING_DAMAGE = 18.0F;
|
||||
private static final double STING_AOE_RADIUS = 5.0D;
|
||||
private static final int POISON_DURATION_TICKS = 300;
|
||||
private static final int WEAKNESS_DURATION_TICKS = 120;
|
||||
private static final int SUMMON_INTERVAL_TICKS = 140;
|
||||
private static final int MAX_SUMMONED_SCORPIONS = 8;
|
||||
private static final String EMPEROR_SUMMON_TAG = "antarchy_emperor_summoned";
|
||||
|
||||
private static final ResourceKey<Level> THORAXIS_KEY =
|
||||
@@ -69,19 +74,24 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack");
|
||||
private static final RawAnimation SNAP_ANIM = RawAnimation.begin().thenPlay("snap");
|
||||
private static final RawAnimation STING_ANIM = RawAnimation.begin().thenPlay("sting");
|
||||
private static final RawAnimation AGRO_ANIM = RawAnimation.begin().thenPlay("agro");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlay("death");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
private final ServerBossEvent bossEvent =
|
||||
new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.PROGRESS);
|
||||
new ServerBossEvent(this.getDisplayName(), BossEvent.BossBarColor.PURPLE, BossEvent.BossBarOverlay.PROGRESS);
|
||||
|
||||
private int attackAnimTicks = 0;
|
||||
private int attackHitTick = 0;
|
||||
private int attackCooldownTicks = 0;
|
||||
private int stingCooldownTicks = 0;
|
||||
private int agroAnimTicks = 0;
|
||||
private int summonCooldown = 0;
|
||||
private boolean attackDamageApplied = false;
|
||||
private boolean stingAttackActive = false;
|
||||
private boolean hasAgroed = false;
|
||||
@Nullable private LivingEntity attackTarget;
|
||||
|
||||
public EmperorScorpionEntity(EntityType<? extends EmperorScorpionEntity> entityType, Level level) {
|
||||
@@ -92,12 +102,13 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Monster.createMonsterAttributes()
|
||||
.add(Attributes.MAX_HEALTH, 200.0D)
|
||||
.add(Attributes.MAX_HEALTH, 400.0D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.32D)
|
||||
.add(Attributes.ATTACK_DAMAGE, 18.0D)
|
||||
.add(Attributes.ARMOR, 6.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.6D)
|
||||
.add(Attributes.FOLLOW_RANGE, 32.0D);
|
||||
.add(Attributes.ATTACK_DAMAGE, 28.0D)
|
||||
.add(Attributes.ARMOR, 25.0D)
|
||||
.add(Attributes.ARMOR_TOUGHNESS, 10.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.8D)
|
||||
.add(Attributes.FOLLOW_RANGE, 40.0D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<EmperorScorpionEntity> entityType, ServerLevelAccessor level,
|
||||
@@ -154,10 +165,27 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
this.resetAttackState();
|
||||
}
|
||||
}
|
||||
if (this.agroAnimTicks > 0) {
|
||||
this.agroAnimTicks--;
|
||||
}
|
||||
if (this.summonCooldown > 0) {
|
||||
this.summonCooldown--;
|
||||
}
|
||||
|
||||
if (!this.level().isClientSide) {
|
||||
if (this.getTarget() != null) {
|
||||
if (!this.hasAgroed) {
|
||||
this.hasAgroed = true;
|
||||
this.agroAnimTicks = AGRO_ANIM_TICKS;
|
||||
this.getNavigation().stop();
|
||||
this.level().broadcastEntityEvent(this, AGRO_ANIM_EVENT);
|
||||
this.playSound(AntarchySoundEvents.EMPEROR_SCORPION_ROAR.get(), 2.0F, 0.7F);
|
||||
}
|
||||
} else {
|
||||
this.hasAgroed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getTarget() != null
|
||||
&& this.summonCooldown <= 0
|
||||
&& !this.isAttackLocked()
|
||||
@@ -175,7 +203,7 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
int toSpawn = 1 + this.random.nextInt(2);
|
||||
int toSpawn = 2 + this.random.nextInt(2);
|
||||
EntityType<ScorpionEntity> scorpionType = AntarchyObjects.SCORPION.get();
|
||||
for (int i = 0; i < toSpawn; i++) {
|
||||
double offsetX = (this.random.nextDouble() - 0.5D) * 6.0D;
|
||||
@@ -198,10 +226,10 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
|
||||
@Override
|
||||
public boolean doHurtTarget(Entity target) {
|
||||
return this.performStingAttack(target);
|
||||
return this.performSnapAttack(target);
|
||||
}
|
||||
|
||||
private boolean performClawAttack(Entity target) {
|
||||
private boolean performSnapAttack(Entity target) {
|
||||
boolean hurt = super.doHurtTarget(target);
|
||||
if (hurt) {
|
||||
this.playSound(AntarchySoundEvents.EMPEROR_SCORPION_ATTACK.get(), 0.9F, 0.95F + this.random.nextFloat() * 0.08F);
|
||||
@@ -210,32 +238,39 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean performStingAttack(Entity target) {
|
||||
boolean hurt;
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
hurt = target.hurt(AntarchyDamageSources.emperorScorpionSting(serverLevel, this),
|
||||
(float) this.getAttributeValue(Attributes.ATTACK_DAMAGE));
|
||||
} else {
|
||||
hurt = super.doHurtTarget(target);
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return false;
|
||||
}
|
||||
if (hurt && target instanceof LivingEntity living) {
|
||||
boolean hurtAny = false;
|
||||
for (LivingEntity living : serverLevel.getEntitiesOfClass(LivingEntity.class,
|
||||
this.getBoundingBox().inflate(STING_AOE_RADIUS),
|
||||
entity -> entity != this
|
||||
&& entity.isAlive()
|
||||
&& !(entity instanceof EmperorScorpionEntity)
|
||||
&& !(entity instanceof ScorpionEntity))) {
|
||||
if (living.hurt(AntarchyDamageSources.emperorScorpionSting(serverLevel, this), STING_DAMAGE)) {
|
||||
hurtAny = true;
|
||||
int poisonDuration = this.level().getDifficulty() == Difficulty.HARD
|
||||
? POISON_DURATION_TICKS * 2 : POISON_DURATION_TICKS;
|
||||
living.addEffect(new MobEffectInstance(MobEffects.POISON, poisonDuration, 0));
|
||||
living.addEffect(new MobEffectInstance(MobEffects.POISON, poisonDuration, 1));
|
||||
living.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, WEAKNESS_DURATION_TICKS, 0));
|
||||
}
|
||||
}
|
||||
if (hurtAny) {
|
||||
this.playSound(AntarchySoundEvents.EMPEROR_SCORPION_ATTACK.get(), 1.0F, 0.9F + this.random.nextFloat() * 0.08F);
|
||||
}
|
||||
return hurt;
|
||||
return hurtAny;
|
||||
}
|
||||
|
||||
private void beginClawAttack(LivingEntity target) {
|
||||
private void beginSnapAttack(LivingEntity target) {
|
||||
this.attackTarget = target;
|
||||
this.attackAnimTicks = CLAW_ATTACK_ANIM_TICKS;
|
||||
this.attackHitTick = CLAW_ATTACK_HIT_TICK;
|
||||
this.attackCooldownTicks = CLAW_ATTACK_COOLDOWN_TICKS;
|
||||
this.attackAnimTicks = SNAP_ATTACK_ANIM_TICKS;
|
||||
this.attackHitTick = SNAP_ATTACK_HIT_TICK;
|
||||
this.attackCooldownTicks = SNAP_ATTACK_COOLDOWN_TICKS;
|
||||
this.attackDamageApplied = false;
|
||||
this.stingAttackActive = false;
|
||||
this.getNavigation().stop();
|
||||
this.level().broadcastEntityEvent(this, CLAW_ATTACK_ANIM_EVENT);
|
||||
this.level().broadcastEntityEvent(this, SNAP_ATTACK_ANIM_EVENT);
|
||||
this.commitLunge(target, 0.42D, 0.10D);
|
||||
}
|
||||
|
||||
@@ -259,14 +294,13 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
this.getLookControl().setLookAt(this.attackTarget, 30.0F, 30.0F);
|
||||
if (!this.attackDamageApplied
|
||||
&& this.attackAnimTicks == this.attackHitTick
|
||||
&& this.distanceToSqr(this.attackTarget) <= this.getAttackReachSqr(this.attackTarget)) {
|
||||
this.attackDamageApplied = true;
|
||||
if (!this.attackDamageApplied && this.attackAnimTicks == this.attackHitTick) {
|
||||
if (this.stingAttackActive) {
|
||||
this.attackDamageApplied = true;
|
||||
this.performStingAttack(this.attackTarget);
|
||||
} else {
|
||||
this.performClawAttack(this.attackTarget);
|
||||
} else if (this.distanceToSqr(this.attackTarget) <= this.getAttackReachSqr(this.attackTarget)) {
|
||||
this.attackDamageApplied = true;
|
||||
this.performSnapAttack(this.attackTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,13 +340,13 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean isAttackLocked() {
|
||||
return this.attackAnimTicks > 0;
|
||||
return this.attackAnimTicks > 0 || this.agroAnimTicks > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEntityEvent(byte id) {
|
||||
if (id == CLAW_ATTACK_ANIM_EVENT) {
|
||||
this.attackAnimTicks = CLAW_ATTACK_ANIM_TICKS;
|
||||
if (id == SNAP_ATTACK_ANIM_EVENT) {
|
||||
this.attackAnimTicks = SNAP_ATTACK_ANIM_TICKS;
|
||||
this.attackDamageApplied = false;
|
||||
this.stingAttackActive = false;
|
||||
return;
|
||||
@@ -323,6 +357,10 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
this.stingAttackActive = true;
|
||||
return;
|
||||
}
|
||||
if (id == AGRO_ANIM_EVENT) {
|
||||
this.agroAnimTicks = AGRO_ANIM_TICKS;
|
||||
return;
|
||||
}
|
||||
super.handleEntityEvent(id);
|
||||
}
|
||||
|
||||
@@ -332,8 +370,14 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private PlayState mainController(AnimationState<EmperorScorpionEntity> state) {
|
||||
if (this.isDeadOrDying()) {
|
||||
return state.setAndContinue(DEATH_ANIM);
|
||||
}
|
||||
if (this.attackAnimTicks > 0) {
|
||||
return state.setAndContinue(ATTACK_ANIM);
|
||||
return state.setAndContinue(this.stingAttackActive ? STING_ANIM : SNAP_ANIM);
|
||||
}
|
||||
if (this.agroAnimTicks > 0) {
|
||||
return state.setAndContinue(AGRO_ANIM);
|
||||
}
|
||||
if (state.isMoving()) {
|
||||
return state.setAndContinue(WALK_ANIM);
|
||||
@@ -341,6 +385,15 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
return state.setAndContinue(IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tickDeath() {
|
||||
this.deathTime++;
|
||||
if (this.deathTime >= DEATH_ANIM_TICKS && !this.level().isClientSide && !this.isRemoved()) {
|
||||
this.level().broadcastEntityEvent(this, (byte) 60);
|
||||
this.remove(RemovalReason.KILLED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
@@ -421,7 +474,7 @@ public class EmperorScorpionEntity extends Monster implements GeoEntity {
|
||||
if (EmperorScorpionEntity.this.shouldUseSting(enemy)) {
|
||||
EmperorScorpionEntity.this.beginStingAttack(enemy);
|
||||
} else {
|
||||
EmperorScorpionEntity.this.beginClawAttack(enemy);
|
||||
EmperorScorpionEntity.this.beginSnapAttack(enemy);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+1270
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,737 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.mojang.serialization.Codec;
|
||||
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.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.util.ByIdMap;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
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.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.MoverType;
|
||||
import net.minecraft.world.entity.SpawnGroupData;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomStrollGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class JumpyBugEntity extends Monster implements GeoEntity {
|
||||
private static final EntityDataAccessor<Boolean> CAMOUFLAGED =
|
||||
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Boolean> CLINGING_TO_CEILING =
|
||||
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Integer> ATTACK_STATE =
|
||||
SynchedEntityData.defineId(JumpyBugEntity.class, EntityDataSerializers.INT);
|
||||
|
||||
private static final String POUNCE_COOLDOWN_KEY = "PounceCooldown";
|
||||
private static final String RECOVERY_TICKS_KEY = "RecoveryTicks";
|
||||
private static final String POUNCE_TICKS_KEY = "PounceTicks";
|
||||
private static final String LEAPING_TICKS_KEY = "LeapingTicks";
|
||||
|
||||
private static final double DEFAULT_MOVEMENT_SPEED = 0.25D;
|
||||
private static final double DEFAULT_FOLLOW_RANGE = 16.0D;
|
||||
private static final double MELEE_WALK_RANGE = 4.0D;
|
||||
private static final double CHASE_LEAP_RANGE_SQR = 8.0D * 8.0D;
|
||||
private static final int DEFAULT_POUNCE_COOLDOWN = 60;
|
||||
private static final int DEFAULT_RECOVERY_TICKS = 20;
|
||||
private static final int CHASE_JUMP_COOLDOWN = 25;
|
||||
private static final double POUNCE_HORIZONTAL_SPEED = 0.92D;
|
||||
private static final double POUNCE_UPWARD_SPEED = 0.75D;
|
||||
private static final double CHASE_LEAP_HORIZONTAL = 1.5D;
|
||||
private static final double CHASE_LEAP_VERTICAL = 0.55D;
|
||||
private static final double QUICK_MOVE_SQR = 0.14D * 0.14D;
|
||||
private static final double POUNCE_LAND_DAMAGE_RANGE = 5.0D;
|
||||
private static final int CEILING_SEARCH_RANGE = 30;
|
||||
private static final double CAMOUFLAGE_PLAYER_RANGE = 24.0D;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation JUMP_ANIM = RawAnimation.begin().thenLoop("jump");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private int pounceCooldownTicks;
|
||||
private int recoveryTicks;
|
||||
private int pounceTicks;
|
||||
private int leapingTicks;
|
||||
private int chaseJumpCooldownTicks;
|
||||
private float visualAlpha = 1.0F;
|
||||
private float previousVisualAlpha = 1.0F;
|
||||
|
||||
public JumpyBugEntity(EntityType<? extends JumpyBugEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.xpReward = 6;
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.jumpyBugHealth())
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.jumpyBugPounceDamage())
|
||||
.add(Attributes.MOVEMENT_SPEED, DEFAULT_MOVEMENT_SPEED)
|
||||
.add(Attributes.FOLLOW_RANGE, DEFAULT_FOLLOW_RANGE);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<JumpyBugEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(CAMOUFLAGED, false);
|
||||
builder.define(CLINGING_TO_CEILING, false);
|
||||
builder.define(ATTACK_STATE, AttackState.GROUND.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new CloseMeleeGoal());
|
||||
this.goalSelector.addGoal(2, new PounceAttackGoal());
|
||||
this.goalSelector.addGoal(3, new HideOnCeilingGoal());
|
||||
this.goalSelector.addGoal(4, new ChaseTargetGoal());
|
||||
this.goalSelector.addGoal(5, new WanderGoal());
|
||||
this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 10.0F));
|
||||
this.goalSelector.addGoal(7, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt(POUNCE_COOLDOWN_KEY, this.pounceCooldownTicks);
|
||||
tag.putInt(RECOVERY_TICKS_KEY, this.recoveryTicks);
|
||||
tag.putInt(POUNCE_TICKS_KEY, this.pounceTicks);
|
||||
tag.putInt(LEAPING_TICKS_KEY, this.leapingTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.pounceCooldownTicks = tag.getInt(POUNCE_COOLDOWN_KEY);
|
||||
this.recoveryTicks = tag.getInt(RECOVERY_TICKS_KEY);
|
||||
this.pounceTicks = tag.getInt(POUNCE_TICKS_KEY);
|
||||
this.leapingTicks = tag.getInt(LEAPING_TICKS_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnGroupData) {
|
||||
this.setHealth((float) AntarchySettings.jumpyBugHealth());
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, spawnGroupData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
this.previousVisualAlpha = this.visualAlpha;
|
||||
float targetAlpha = this.getTargetVisualAlpha();
|
||||
if (this.visualAlpha != targetAlpha) {
|
||||
this.visualAlpha += (targetAlpha - this.visualAlpha) * 0.25F;
|
||||
}
|
||||
|
||||
super.tick();
|
||||
|
||||
if (this.level().isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pounceCooldownTicks > 0) {
|
||||
this.pounceCooldownTicks--;
|
||||
}
|
||||
if (this.recoveryTicks > 0) {
|
||||
this.recoveryTicks--;
|
||||
}
|
||||
if (this.chaseJumpCooldownTicks > 0) {
|
||||
this.chaseJumpCooldownTicks--;
|
||||
}
|
||||
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.tickClinging();
|
||||
} else if (this.isPouncing()) {
|
||||
this.tickPouncing();
|
||||
} else if (this.isLeapingToCeiling()) {
|
||||
this.tickLeapingToCeiling();
|
||||
} else {
|
||||
this.setNoGravity(false);
|
||||
this.noPhysics = false;
|
||||
if (this.getAttackState() != AttackState.RECOVERING || this.recoveryTicks <= 0) {
|
||||
this.setAttackState(AttackState.GROUND);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateCamouflage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void travel(Vec3 travelVector) {
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
return;
|
||||
}
|
||||
super.travel(travelVector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
boolean hurt = super.hurt(source, amount);
|
||||
if (!hurt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setCamouflaged(false);
|
||||
this.recoveryTicks = Math.max(this.recoveryTicks, DEFAULT_RECOVERY_TICKS);
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.stopClinging();
|
||||
}
|
||||
if (this.isPouncing()) {
|
||||
this.finishPounce(false);
|
||||
}
|
||||
if (this.isLeapingToCeiling()) {
|
||||
this.cancelLeap();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.SPIDER_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return SoundEvents.SPIDER_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.SPIDER_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playStepSound(BlockPos pos, BlockState blockState) {
|
||||
this.playSound(SoundEvents.SPIDER_STEP, 0.12F, 1.1F);
|
||||
}
|
||||
|
||||
public boolean isCamouflaged() {
|
||||
return this.entityData.get(CAMOUFLAGED);
|
||||
}
|
||||
|
||||
public boolean isClingingToCeiling() {
|
||||
return this.entityData.get(CLINGING_TO_CEILING);
|
||||
}
|
||||
|
||||
public boolean isPouncing() {
|
||||
return this.getAttackState() == AttackState.POUNCING;
|
||||
}
|
||||
|
||||
public boolean isLeapingToCeiling() {
|
||||
return this.getAttackState() == AttackState.LEAPING_TO_CEILING;
|
||||
}
|
||||
|
||||
public float getVisualAlpha(float partialTick) {
|
||||
return Mth.lerp(partialTick, this.previousVisualAlpha, this.visualAlpha);
|
||||
}
|
||||
|
||||
public AttackState getAttackState() {
|
||||
return AttackState.BY_ID.apply(this.entityData.get(ATTACK_STATE));
|
||||
}
|
||||
|
||||
private void setCamouflaged(boolean value) {
|
||||
this.entityData.set(CAMOUFLAGED, value);
|
||||
}
|
||||
|
||||
private void setClingingToCeiling(boolean value) {
|
||||
this.entityData.set(CLINGING_TO_CEILING, value);
|
||||
}
|
||||
|
||||
private void setAttackState(AttackState state) {
|
||||
this.entityData.set(ATTACK_STATE, state.id());
|
||||
}
|
||||
|
||||
private float getTargetVisualAlpha() {
|
||||
return this.isCamouflaged() ? (float) AntarchySettings.jumpyBugCamouflageAlpha() : 1.0F;
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<JumpyBugEntity> state) {
|
||||
AttackState attackState = this.getAttackState();
|
||||
if (attackState == AttackState.POUNCING || attackState == AttackState.CLINGING || attackState == AttackState.LEAPING_TO_CEILING) {
|
||||
return state.setAndContinue(JUMP_ANIM);
|
||||
}
|
||||
if (attackState == AttackState.RECOVERING) {
|
||||
return state.setAndContinue(ATTACK_ANIM);
|
||||
}
|
||||
return state.setAndContinue(state.isMoving() ? WALK_ANIM : IDLE_ANIM);
|
||||
}
|
||||
|
||||
private void tickClinging() {
|
||||
BlockPos clingPos = this.findCeilingWithinRange(2);
|
||||
if (clingPos == null) {
|
||||
this.stopClinging();
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAttackState(AttackState.CLINGING);
|
||||
this.setCamouflaged(true);
|
||||
this.setNoGravity(true);
|
||||
this.noPhysics = true;
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.move(MoverType.SELF, Vec3.ZERO);
|
||||
this.setPos(this.getX(), clingPos.getY() - this.getBbHeight() - 0.01D, this.getZ());
|
||||
}
|
||||
|
||||
private void tickPouncing() {
|
||||
this.setAttackState(AttackState.POUNCING);
|
||||
this.setCamouflaged(false);
|
||||
this.pounceTicks++;
|
||||
|
||||
if (this.onGround() && this.pounceTicks > 8) {
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null && target.isAlive() && this.distanceToSqr(target) <= POUNCE_LAND_DAMAGE_RANGE * POUNCE_LAND_DAMAGE_RANGE) {
|
||||
target.hurt(this.damageSources().mobAttack(this), (float) AntarchySettings.jumpyBugPounceDamage());
|
||||
}
|
||||
this.finishPounce(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void tickLeapingToCeiling() {
|
||||
this.leapingTicks++;
|
||||
if (this.leapingTicks > 80) {
|
||||
this.cancelLeap();
|
||||
return;
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
BlockPos ceiling = this.findCeilingWithinRange(2);
|
||||
if (ceiling != null) {
|
||||
this.leapingTicks = 0;
|
||||
this.beginClinging();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCamouflage() {
|
||||
if (this.isPouncing() || this.isLeapingToCeiling()) {
|
||||
this.setCamouflaged(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isClingingToCeiling()) {
|
||||
this.setCamouflaged(true);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean playerNearby = this.level().getNearestPlayer(this, CAMOUFLAGE_PLAYER_RANGE) != null;
|
||||
if (!playerNearby) {
|
||||
this.setCamouflaged(false);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean idle = this.getTarget() == null && this.getDeltaMovement().horizontalDistanceSqr() < 0.003D;
|
||||
boolean movingQuickly = this.getDeltaMovement().lengthSqr() > QUICK_MOVE_SQR;
|
||||
this.setCamouflaged(!movingQuickly && idle);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos findCeilingWithinRange(int maxBlocks) {
|
||||
BlockPos origin = this.blockPosition();
|
||||
for (int dy = 1; dy <= maxBlocks; dy++) {
|
||||
BlockPos candidate = origin.above(dy);
|
||||
if (isCeilingBlock(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos findCeilingForLeap() {
|
||||
return findCeilingWithinRange(CEILING_SEARCH_RANGE);
|
||||
}
|
||||
|
||||
private boolean isCeilingBlock(BlockPos pos) {
|
||||
return this.level().getBlockState(pos).isFaceSturdy(this.level(), pos, net.minecraft.core.Direction.DOWN);
|
||||
}
|
||||
|
||||
private void beginClinging() {
|
||||
this.setClingingToCeiling(true);
|
||||
this.setAttackState(AttackState.CLINGING);
|
||||
this.setNoGravity(true);
|
||||
this.noPhysics = true;
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
|
||||
private void stopClinging() {
|
||||
this.setClingingToCeiling(false);
|
||||
this.setAttackState(AttackState.GROUND);
|
||||
this.setNoGravity(false);
|
||||
this.noPhysics = false;
|
||||
}
|
||||
|
||||
private void beginLeapToCeiling(BlockPos ceilingPos) {
|
||||
this.setAttackState(AttackState.LEAPING_TO_CEILING);
|
||||
this.setCamouflaged(false);
|
||||
this.leapingTicks = 0;
|
||||
this.getNavigation().stop();
|
||||
|
||||
double dy = ceilingPos.getY() - this.getY();
|
||||
double upwardSpeed = Math.min(2.5D, Math.sqrt(0.16D * Math.max(dy, 1.0D)) + 0.4D);
|
||||
this.setDeltaMovement(0.0D, upwardSpeed, 0.0D);
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
|
||||
private void cancelLeap() {
|
||||
this.leapingTicks = 0;
|
||||
this.setAttackState(AttackState.GROUND);
|
||||
}
|
||||
|
||||
private void beginPounce(LivingEntity target) {
|
||||
this.stopClinging();
|
||||
this.setTarget(target);
|
||||
this.setAttackState(AttackState.POUNCING);
|
||||
this.setCamouflaged(false);
|
||||
this.getNavigation().stop();
|
||||
this.pounceCooldownTicks = DEFAULT_POUNCE_COOLDOWN;
|
||||
this.pounceTicks = 0;
|
||||
|
||||
Vec3 from = this.position();
|
||||
Vec3 to = target.getEyePosition().subtract(from);
|
||||
Vec3 horizontal = new Vec3(to.x, 0.0D, to.z);
|
||||
Vec3 direction = horizontal.lengthSqr() > 1.0E-4D ? horizontal.normalize() : target.getLookAngle();
|
||||
double upward = POUNCE_UPWARD_SPEED + Mth.clamp(to.y * 0.08D, -0.04D, 0.22D);
|
||||
this.setDeltaMovement(direction.x * POUNCE_HORIZONTAL_SPEED, upward, direction.z * POUNCE_HORIZONTAL_SPEED);
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
|
||||
private void finishPounce(boolean keepMomentum) {
|
||||
this.pounceTicks = 0;
|
||||
if (!keepMomentum) {
|
||||
Vec3 movement = this.getDeltaMovement();
|
||||
this.setDeltaMovement(movement.x * 0.25D, Math.max(0.0D, movement.y), movement.z * 0.25D);
|
||||
}
|
||||
this.setAttackState(AttackState.RECOVERING);
|
||||
this.recoveryTicks = DEFAULT_RECOVERY_TICKS;
|
||||
}
|
||||
|
||||
private boolean canUseAmbush() {
|
||||
return !this.isPouncing()
|
||||
&& !this.isLeapingToCeiling()
|
||||
&& this.recoveryTicks <= 0;
|
||||
}
|
||||
|
||||
public enum AttackState implements StringRepresentable {
|
||||
GROUND(0, "ground"),
|
||||
CLINGING(1, "clinging"),
|
||||
POUNCING(2, "pouncing"),
|
||||
RECOVERING(3, "recovering"),
|
||||
LEAPING_TO_CEILING(4, "leaping_to_ceiling");
|
||||
|
||||
private static final IntFunction<AttackState> BY_ID = ByIdMap.continuous(AttackState::id, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||
public static final Codec<AttackState> CODEC = StringRepresentable.fromEnum(AttackState::values);
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
|
||||
AttackState(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int id() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
private final class CloseMeleeGoal extends Goal {
|
||||
private static final double MELEE_RANGE_SQR = 5.0D * 5.0D;
|
||||
private static final double CONTINUE_RANGE_SQR = 7.0D * 7.0D;
|
||||
private static final int ATTACK_COOLDOWN = 20;
|
||||
private int attackCooldown;
|
||||
|
||||
private CloseMeleeGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) <= MELEE_RANGE_SQR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) <= CONTINUE_RANGE_SQR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.attackCooldown = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target == null) return;
|
||||
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.1D);
|
||||
|
||||
if (this.attackCooldown > 0) {
|
||||
this.attackCooldown--;
|
||||
}
|
||||
if (this.attackCooldown <= 0 && JumpyBugEntity.this.distanceToSqr(target) <= MELEE_RANGE_SQR) {
|
||||
JumpyBugEntity.this.doHurtTarget(target);
|
||||
this.attackCooldown = ATTACK_COOLDOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PounceAttackGoal extends Goal {
|
||||
private PounceAttackGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& JumpyBugEntity.this.canUseAmbush()
|
||||
&& JumpyBugEntity.this.pounceCooldownTicks <= 0
|
||||
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 9.0D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return JumpyBugEntity.this.isPouncing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target != null) {
|
||||
JumpyBugEntity.this.beginPounce(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class HideOnCeilingGoal extends Goal {
|
||||
@Nullable
|
||||
private BlockPos targetCeiling;
|
||||
|
||||
private HideOnCeilingGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (!JumpyBugEntity.this.canUseAmbush() || JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
return false;
|
||||
}
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
boolean targetFarAway = target != null
|
||||
&& JumpyBugEntity.this.distanceToSqr(target) > 12.0D * 12.0D;
|
||||
boolean noTarget = target == null;
|
||||
if (!noTarget && !targetFarAway) {
|
||||
return false;
|
||||
}
|
||||
if (JumpyBugEntity.this.random.nextInt(25) != 0) {
|
||||
return false;
|
||||
}
|
||||
this.targetCeiling = JumpyBugEntity.this.findCeilingForLeap();
|
||||
return this.targetCeiling != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return JumpyBugEntity.this.isClingingToCeiling() || JumpyBugEntity.this.isLeapingToCeiling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.targetCeiling != null) {
|
||||
BlockPos ceil = this.targetCeiling;
|
||||
// If we're already adjacent to the ceiling, just cling directly
|
||||
if (ceil.getY() - JumpyBugEntity.this.getY() <= 2) {
|
||||
JumpyBugEntity.this.beginClinging();
|
||||
} else {
|
||||
JumpyBugEntity.this.beginLeapToCeiling(ceil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
JumpyBugEntity.this.stopClinging();
|
||||
} else if (JumpyBugEntity.this.isLeapingToCeiling()) {
|
||||
JumpyBugEntity.this.cancelLeap();
|
||||
}
|
||||
this.targetCeiling = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (JumpyBugEntity.this.isClingingToCeiling()) {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target != null) {
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
if (JumpyBugEntity.this.pounceCooldownTicks <= 0
|
||||
&& (JumpyBugEntity.this.hasLineOfSight(target) || JumpyBugEntity.this.distanceToSqr(target) <= 4.0D)) {
|
||||
JumpyBugEntity.this.stopClinging();
|
||||
JumpyBugEntity.this.beginPounce(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChaseTargetGoal extends Goal {
|
||||
private ChaseTargetGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& JumpyBugEntity.this.recoveryTicks <= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return this.canUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
LivingEntity target = JumpyBugEntity.this.getTarget();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JumpyBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
double distSqr = JumpyBugEntity.this.distanceToSqr(target);
|
||||
|
||||
if (distSqr <= MELEE_WALK_RANGE * MELEE_WALK_RANGE) {
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.05D);
|
||||
} else if (distSqr > CHASE_LEAP_RANGE_SQR) {
|
||||
if (JumpyBugEntity.this.onGround() && JumpyBugEntity.this.chaseJumpCooldownTicks <= 0) {
|
||||
Vec3 dir = target.position().subtract(JumpyBugEntity.this.position()).normalize();
|
||||
JumpyBugEntity.this.setDeltaMovement(
|
||||
dir.x * CHASE_LEAP_HORIZONTAL,
|
||||
CHASE_LEAP_VERTICAL,
|
||||
dir.z * CHASE_LEAP_HORIZONTAL
|
||||
);
|
||||
JumpyBugEntity.this.hasImpulse = true;
|
||||
JumpyBugEntity.this.chaseJumpCooldownTicks = CHASE_JUMP_COOLDOWN;
|
||||
}
|
||||
JumpyBugEntity.this.getNavigation().stop();
|
||||
} else {
|
||||
JumpyBugEntity.this.getNavigation().moveTo(target, 1.05D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class WanderGoal extends RandomStrollGoal {
|
||||
private WanderGoal() {
|
||||
super(JumpyBugEntity.this, 0.8D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& JumpyBugEntity.this.getTarget() == null
|
||||
&& super.canUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return !JumpyBugEntity.this.isClingingToCeiling()
|
||||
&& !JumpyBugEntity.this.isPouncing()
|
||||
&& !JumpyBugEntity.this.isLeapingToCeiling()
|
||||
&& super.canContinueToUse();
|
||||
}
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class LurkingTerrorEntity extends Monster implements GeoEntity {
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation FLY_ANIM = RawAnimation.begin().thenLoop("fly");
|
||||
private static final RawAnimation ATTACK_LAND_ANIM = RawAnimation.begin().thenLoop("attackland");
|
||||
private static final RawAnimation ATTACK_AIR_ANIM = RawAnimation.begin().thenLoop("attackair");
|
||||
private static final byte ATTACK_ANIM_EVENT = 5;
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
private int attackAnimTicks = 0;
|
||||
|
||||
public LurkingTerrorEntity(EntityType<? extends LurkingTerrorEntity> type, Level level) {
|
||||
super(type, level);
|
||||
this.moveControl = new FlyingMoveControl(this, 10, true);
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.lurkingTerrorHealth())
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.35D)
|
||||
.add(Attributes.ATTACK_DAMAGE, AntarchySettings.lurkingTerrorAttackDamage())
|
||||
.add(Attributes.FOLLOW_RANGE, 16.0D)
|
||||
.add(Attributes.FLYING_SPEED, 0.55D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<LurkingTerrorEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockPos belowPos = pos.below();
|
||||
BlockState belowState = level.getBlockState(belowPos);
|
||||
boolean validSupport = !belowState.is(Blocks.BEDROCK)
|
||||
&& belowState.blocksMotion()
|
||||
&& belowState.isFaceSturdy(level, belowPos, Direction.UP)
|
||||
&& belowState.isCollisionShapeFullBlock(level, belowPos);
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& validSupport
|
||||
&& level.isEmptyBlock(pos)
|
||||
&& level.isEmptyBlock(pos.above())
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
FlyingPathNavigation nav = new FlyingPathNavigation(this, level);
|
||||
nav.setCanOpenDoors(false);
|
||||
nav.setCanFloat(true);
|
||||
nav.setCanPassDoors(true);
|
||||
return nav;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new MeleeAttackGoal(this, 1.0D, true));
|
||||
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.85D));
|
||||
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
|
||||
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 3, this::mainAnimController));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<LurkingTerrorEntity> state) {
|
||||
if (attackAnimTicks > 0) {
|
||||
if (!this.onGround()) {
|
||||
return state.setAndContinue(ATTACK_AIR_ANIM);
|
||||
}
|
||||
return state.setAndContinue(ATTACK_LAND_ANIM);
|
||||
}
|
||||
if (!this.onGround()) {
|
||||
return state.setAndContinue(FLY_ANIM);
|
||||
}
|
||||
if (state.isMoving()) {
|
||||
return state.setAndContinue(WALK_ANIM);
|
||||
}
|
||||
return state.setAndContinue(IDLE_ANIM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHurtTarget(net.minecraft.world.entity.Entity target) {
|
||||
boolean result = super.doHurtTarget(target);
|
||||
if (result) {
|
||||
this.playSound(AntarchySoundEvents.LURKING_TERROR_BITE.get(), 1.0F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
attackAnimTicks = 20;
|
||||
this.level().broadcastEntityEvent(this, ATTACK_ANIM_EVENT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return AntarchySoundEvents.LURKING_TERROR_SNARL.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(net.minecraft.world.damagesource.DamageSource source) {
|
||||
return AntarchySoundEvents.LURKING_TERROR_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return AntarchySoundEvents.LURKING_TERROR_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEntityEvent(byte id) {
|
||||
if (id == ATTACK_ANIM_EVENT) {
|
||||
attackAnimTicks = 20;
|
||||
return;
|
||||
}
|
||||
super.handleEntityEvent(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (!this.level().isClientSide() && !this.onGround() && this.tickCount % 40 == 0) {
|
||||
this.playSound(AntarchySoundEvents.LURKING_TERROR_FLY_LOOP.get(), 0.45F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
}
|
||||
if (!this.level().isClientSide() && attackAnimTicks > 0) attackAnimTicks--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean causeFallDamage(float fallDistance, float multiplier, net.minecraft.world.damagesource.DamageSource source) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -117,8 +117,9 @@ public class MantisEntity extends Monster implements GeoEntity {
|
||||
|
||||
boolean ignoreLightLevel = AntarchySettings.mantisIgnoreLightLevel();
|
||||
|
||||
// Daytime overworld spawns can't pass the vanilla darkness check, so no light gate here
|
||||
if (level.getLevel().isDay() && level.getBiome(pos).is(AntarchyTags.Biomes.MANTIS_OVERWORLD_SPAWN_BIOMES)) {
|
||||
return atSurface && level.canSeeSky(pos) && (ignoreLightLevel || Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random));
|
||||
return atSurface && level.canSeeSky(pos);
|
||||
}
|
||||
|
||||
// Elythia meadow (and any future mantis_spawn_biomes): nighttime, no sky-access requirement
|
||||
|
||||
@@ -62,6 +62,7 @@ public class MissileSquidEntity extends Monster implements GeoEntity {
|
||||
private static final String LAUNCH_OWNER_KEY = "LaunchOwner";
|
||||
private static final String ARMED_AFTER_LANDING_KEY = "ArmedAfterLanding";
|
||||
private static final String ACTION_ANIM_TICKS_KEY = "ActionAnimTicks";
|
||||
private static final String SPAWNED_BY_KRAKEN_KEY = "SpawnedByKraken";
|
||||
private static final double AGGRO_SPEED = 0.5D;
|
||||
private static final double PATROL_SPEED = 0.28D;
|
||||
private static final double ATTACH_RANGE_SQR = 2.25D;
|
||||
@@ -86,6 +87,7 @@ public class MissileSquidEntity extends Monster implements GeoEntity {
|
||||
private int attachedTargetId = -1;
|
||||
private boolean launchedFromSquidzooka;
|
||||
private boolean armedAfterLanding;
|
||||
private boolean spawnedByKraken;
|
||||
@Nullable
|
||||
private UUID launchOwnerUuid;
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
@@ -298,6 +300,7 @@ public class MissileSquidEntity extends Monster implements GeoEntity {
|
||||
tag.putBoolean(LAUNCHED_FROM_SQUIDZOOKA_KEY, this.launchedFromSquidzooka);
|
||||
tag.putBoolean(ARMED_AFTER_LANDING_KEY, this.armedAfterLanding);
|
||||
tag.putInt(ACTION_ANIM_TICKS_KEY, this.actionAnimationTicks);
|
||||
tag.putBoolean(SPAWNED_BY_KRAKEN_KEY, this.spawnedByKraken);
|
||||
if (this.launchOwnerUuid != null) {
|
||||
tag.putUUID(LAUNCH_OWNER_KEY, this.launchOwnerUuid);
|
||||
}
|
||||
@@ -311,10 +314,19 @@ public class MissileSquidEntity extends Monster implements GeoEntity {
|
||||
this.launchedFromSquidzooka = tag.getBoolean(LAUNCHED_FROM_SQUIDZOOKA_KEY);
|
||||
this.armedAfterLanding = tag.getBoolean(ARMED_AFTER_LANDING_KEY);
|
||||
this.actionAnimationTicks = Math.max(0, tag.getInt(ACTION_ANIM_TICKS_KEY));
|
||||
this.spawnedByKraken = tag.getBoolean(SPAWNED_BY_KRAKEN_KEY);
|
||||
this.launchOwnerUuid = tag.hasUUID(LAUNCH_OWNER_KEY) ? tag.getUUID(LAUNCH_OWNER_KEY) : null;
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
public boolean isSpawnedByKraken() {
|
||||
return this.spawnedByKraken;
|
||||
}
|
||||
|
||||
public void setSpawnedByKraken(boolean spawnedByKraken) {
|
||||
this.spawnedByKraken = spawnedByKraken;
|
||||
}
|
||||
|
||||
private void tickAggroMovement(LivingEntity target) {
|
||||
this.patrolTarget = null;
|
||||
this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
|
||||
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.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.control.FlyingMoveControl;
|
||||
import net.minecraft.world.entity.ai.control.SmoothSwimmingMoveControl;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.ai.navigation.FlyingPathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.WaterBoundPathNavigation;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.*;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.keyframe.event.builtin.AutoPlayingSoundKeyframeHandler;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class OctopusBombEntity extends Monster implements GeoEntity {
|
||||
private static final EntityDataAccessor<Integer> ATTACK_STATE = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> CAN_FLY = SynchedEntityData.defineId(OctopusBombEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private static final int ATTACK_NONE = 0;
|
||||
private static final int ATTACK_GRAB = 1;
|
||||
private static final int ATTACK_SWIPE = 2;
|
||||
|
||||
private static final int ANIM_IDLE = 0;
|
||||
private static final int ANIM_SWIM = 2;
|
||||
private static final int ANIM_SWIPE = 3;
|
||||
private static final int ANIM_GRAB = 4;
|
||||
private static final int ANIM_DEATH = 5;
|
||||
|
||||
private static final int DEATH_ANIM_TICKS = 60;
|
||||
private static final int INK_SPRAY_RANGE = 8;
|
||||
private static final int INK_SPRAY_COOLDOWN = 80;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation SWIPE_ANIM = RawAnimation.begin().thenPlay("Swipe");
|
||||
private static final RawAnimation GRAB_ANIM = RawAnimation.begin().thenPlay("Grab");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlay("Death");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
// Squid-style wander
|
||||
private float wanderDx, wanderDy, wanderDz;
|
||||
private int wanderRetargetTicks;
|
||||
|
||||
// Combat
|
||||
private int attackCooldown;
|
||||
private int actionTicks;
|
||||
private int lastAttackState;
|
||||
private int grabbedTargetId = -1;
|
||||
private int strafeRetargetTicks;
|
||||
private float orbitDirection = 1.0F;
|
||||
private int inkSprayCooldown;
|
||||
|
||||
// Kraken spawn flag
|
||||
private boolean spawnedByKraken;
|
||||
|
||||
public OctopusBombEntity(EntityType<? extends Monster> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.moveControl = new SmoothSwimmingMoveControl(this, 80, 8, 0.06F, 0.12F, true);
|
||||
this.xpReward = 15;
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, 100.0D)
|
||||
.add(Attributes.ATTACK_DAMAGE, 16.0D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.28D)
|
||||
.add(Attributes.FLYING_SPEED, 0.28D)
|
||||
.add(Attributes.FOLLOW_RANGE, 32.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.4D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<OctopusBombEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, net.minecraft.core.BlockPos pos, RandomSource random) {
|
||||
return level.getFluidState(pos).is(net.minecraft.tags.FluidTags.WATER)
|
||||
&& level.getFluidState(pos.above()).is(net.minecraft.tags.FluidTags.WATER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(ATTACK_STATE, ATTACK_NONE);
|
||||
builder.define(ANIMATION_STATE, ANIM_IDLE);
|
||||
builder.define(CAN_FLY, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
|
||||
super.onSyncedDataUpdated(key);
|
||||
if (CAN_FLY.equals(key)) {
|
||||
this.moveControl = this.canFly()
|
||||
? new FlyingMoveControl(this, 20, true)
|
||||
: new SmoothSwimmingMoveControl(this, 80, 8, 0.06F, 0.12F, true);
|
||||
this.navigation = this.canFly()
|
||||
? new FlyingPathNavigation(this, this.level())
|
||||
: new WaterBoundPathNavigation(this, this.level());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 20.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
|
||||
// Only retaliate when hit — neutral otherwise
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
// When spawned by Kraken and flying, target nearest player
|
||||
this.targetSelector.addGoal(2, new KrakenSummonTargetGoal(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PathNavigation createNavigation(Level level) {
|
||||
return new WaterBoundPathNavigation(this, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnReason, @Nullable SpawnGroupData spawnData) {
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.MAX_HEALTH)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
this.setHealth((float) com.craisinlord.antarchy.config.AntarchySettings.octopusBombHealth());
|
||||
Objects.requireNonNull(this.getAttribute(Attributes.ATTACK_DAMAGE)).setBaseValue(com.craisinlord.antarchy.config.AntarchySettings.octopusBombAttackDamage());
|
||||
return super.finalizeSpawn(level, difficulty, spawnReason, spawnData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 0, this::mainAnimController)
|
||||
.setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>()));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<OctopusBombEntity> state) {
|
||||
return switch (this.getAnimationState()) {
|
||||
case ANIM_GRAB -> state.setAndContinue(GRAB_ANIM);
|
||||
case ANIM_SWIPE -> state.setAndContinue(SWIPE_ANIM);
|
||||
case ANIM_SWIM -> state.setAndContinue(SWIM_ANIM);
|
||||
case ANIM_DEATH -> state.setAndContinue(DEATH_ANIM);
|
||||
default -> state.setAndContinue(IDLE_ANIM);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void baseTick() {
|
||||
int airSupply = this.getAirSupply();
|
||||
super.baseTick();
|
||||
if (!this.canFly()) {
|
||||
this.handleAirSupply(airSupply);
|
||||
} else {
|
||||
this.setAirSupply(this.getMaxAirSupply());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
this.setNoGravity(this.isInWater() || this.canFly());
|
||||
|
||||
if (this.level().isClientSide) {
|
||||
this.updateSwimRotation();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.attackCooldown > 0) this.attackCooldown--;
|
||||
if (this.inkSprayCooldown > 0) this.inkSprayCooldown--;
|
||||
|
||||
// Ink spray — always active regardless of aggro state
|
||||
this.tickInkSpray();
|
||||
|
||||
LivingEntity target = this.getTarget();
|
||||
|
||||
if (target == null || !target.isAlive()) {
|
||||
this.resetCombatState();
|
||||
if (this.isInWater()) this.tickWanderMovement();
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
return;
|
||||
}
|
||||
|
||||
this.getLookControl().setLookAt(target, 25.0F, 20.0F);
|
||||
|
||||
if (this.getAttackState() != ATTACK_NONE) {
|
||||
this.tickCurrentAttack(target);
|
||||
} else {
|
||||
this.tickPursuitMovement(target);
|
||||
if (this.attackCooldown <= 0) {
|
||||
int nextAttack = this.chooseNextAttack(target);
|
||||
if (nextAttack != ATTACK_NONE) {
|
||||
this.startAttack(nextAttack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateSwimRotation();
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void travel(Vec3 travelVector) {
|
||||
if (this.isEffectiveAi() && (this.isInWater() || this.canFly())) {
|
||||
this.moveRelative(this.getSpeed(), travelVector);
|
||||
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(this.isInWater() ? 0.92D : 0.91D));
|
||||
return;
|
||||
}
|
||||
super.travel(travelVector);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldDespawnInPeaceful() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return AntarchySoundEvents.KRAKEN_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return AntarchySoundEvents.KRAKEN_DEATH.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getSoundVolume() {
|
||||
return 1.2F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt("AttackCooldown", this.attackCooldown);
|
||||
tag.putInt("ActionTicks", this.actionTicks);
|
||||
tag.putInt("LastAttackState", this.lastAttackState);
|
||||
tag.putBoolean("CanFly", this.canFly());
|
||||
tag.putBoolean("SpawnedByKraken", this.spawnedByKraken);
|
||||
if (this.grabbedTargetId >= 0) tag.putInt("GrabTarget", this.grabbedTargetId);
|
||||
tag.putInt("AttackState", this.getAttackState());
|
||||
tag.putInt("AnimationState", this.getAnimationState());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.attackCooldown = tag.getInt("AttackCooldown");
|
||||
this.actionTicks = tag.getInt("ActionTicks");
|
||||
this.lastAttackState = tag.contains("LastAttackState") ? tag.getInt("LastAttackState") : ATTACK_NONE;
|
||||
this.setCanFly(tag.getBoolean("CanFly"));
|
||||
this.spawnedByKraken = tag.getBoolean("SpawnedByKraken");
|
||||
this.grabbedTargetId = tag.contains("GrabTarget") ? tag.getInt("GrabTarget") : -1;
|
||||
this.entityData.set(ATTACK_STATE, tag.getInt("AttackState"));
|
||||
this.entityData.set(ANIMATION_STATE, tag.contains("AnimationState") ? tag.getInt("AnimationState") : ANIM_IDLE);
|
||||
}
|
||||
|
||||
public boolean canFly() {
|
||||
return this.entityData.get(CAN_FLY);
|
||||
}
|
||||
|
||||
public void setCanFly(boolean canFly) {
|
||||
this.entityData.set(CAN_FLY, canFly);
|
||||
}
|
||||
|
||||
public int getAttackState() {
|
||||
return this.entityData.get(ATTACK_STATE);
|
||||
}
|
||||
|
||||
public int getAnimationState() {
|
||||
return this.entityData.get(ANIMATION_STATE);
|
||||
}
|
||||
|
||||
private void setAttackState(int state) {
|
||||
this.entityData.set(ATTACK_STATE, state);
|
||||
}
|
||||
|
||||
private void setAnimationState(int state) {
|
||||
this.entityData.set(ANIMATION_STATE, state);
|
||||
}
|
||||
|
||||
public void launchAsProjectile(net.minecraft.world.entity.Entity owner, Vec3 velocity) {
|
||||
this.spawnedByKraken = true;
|
||||
this.setCanFly(true);
|
||||
this.setDeltaMovement(velocity);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Squid-style wander
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickWanderMovement() {
|
||||
if (--this.wanderRetargetTicks <= 0) {
|
||||
this.wanderRetargetTicks = 40 + this.random.nextInt(40);
|
||||
float angle = this.random.nextFloat() * (float) (Math.PI * 2);
|
||||
this.wanderDx = Mth.cos(angle) * 0.015f;
|
||||
this.wanderDz = Mth.sin(angle) * 0.015f;
|
||||
this.wanderDy = -0.01f + this.random.nextFloat() * 0.01f; // slight downward bias
|
||||
}
|
||||
|
||||
// Surface avoidance: if the block above is not water, push down
|
||||
float dy = this.wanderDy;
|
||||
if (!this.level().getFluidState(this.blockPosition().above()).is(net.minecraft.tags.FluidTags.WATER)) {
|
||||
dy = -0.03f;
|
||||
}
|
||||
|
||||
this.setDeltaMovement(this.getDeltaMovement().add(this.wanderDx, dy, this.wanderDz));
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Ink spray
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickInkSpray() {
|
||||
if (this.inkSprayCooldown > 0 || !(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
|
||||
Player nearbyPlayer = this.level().getNearestPlayer(this, INK_SPRAY_RANGE);
|
||||
if (nearbyPlayer == null || nearbyPlayer.isCreative() || nearbyPlayer.isSpectator()) return;
|
||||
|
||||
this.inkSprayCooldown = INK_SPRAY_COOLDOWN;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_HURT.get(), 1.0F, 1.49F);
|
||||
|
||||
double cx = this.getX();
|
||||
double cy = this.getY() + this.getBbHeight() * 0.5D;
|
||||
double cz = this.getZ();
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, cx, cy, cz, 120, 2.0D, 1.2D, 2.0D, 0.04D);
|
||||
serverLevel.sendParticles(ParticleTypes.SQUID_INK, cx, cy, cz, 60, 3.5D, 1.8D, 3.5D, 0.02D);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Combat
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void tickPursuitMovement(LivingEntity target) {
|
||||
if (this.strafeRetargetTicks-- <= 0) {
|
||||
this.strafeRetargetTicks = 10 + this.random.nextInt(12);
|
||||
if (this.random.nextFloat() < 0.4F) this.orbitDirection *= -1.0F;
|
||||
}
|
||||
Vec3 attackPoint = this.createCombatTarget(target);
|
||||
this.getMoveControl().setWantedPosition(attackPoint.x, attackPoint.y, attackPoint.z, 1.1D);
|
||||
}
|
||||
|
||||
private Vec3 createCombatTarget(LivingEntity target) {
|
||||
double radius = 9.0D;
|
||||
double angle = this.tickCount * 0.16D * this.orbitDirection + this.getId() * 0.11D;
|
||||
double x = target.getX() + Math.cos(angle) * radius;
|
||||
double z = target.getZ() + Math.sin(angle) * radius;
|
||||
double y = Mth.clamp(
|
||||
target.getY() + 1.0D + Math.sin((this.tickCount + this.getId()) * 0.18D),
|
||||
target.getY() - 3.0D,
|
||||
target.getY() + 4.0D
|
||||
);
|
||||
return new Vec3(x, y, z);
|
||||
}
|
||||
|
||||
private void tickCurrentAttack(LivingEntity target) {
|
||||
this.getNavigation().stop();
|
||||
this.getLookControl().setLookAt(target, 30.0F, 25.0F);
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.8D));
|
||||
|
||||
switch (this.getAttackState()) {
|
||||
case ATTACK_GRAB -> this.tickGrabAttack(target);
|
||||
case ATTACK_SWIPE -> this.tickSwipeAttack();
|
||||
default -> this.finishAttack();
|
||||
}
|
||||
}
|
||||
|
||||
private void startAttack(int attackState) {
|
||||
this.setAttackState(attackState);
|
||||
this.grabbedTargetId = -1;
|
||||
if (attackState == ATTACK_GRAB) {
|
||||
this.actionTicks = 55;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_ATTACK.get(), 1.2F, 1.07F);
|
||||
} else if (attackState == ATTACK_SWIPE) {
|
||||
this.actionTicks = 40;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_SPIN.get(), 1.1F, 1.13F);
|
||||
}
|
||||
}
|
||||
|
||||
private void tickGrabAttack(LivingEntity target) {
|
||||
if (this.actionTicks == 48 && this.distanceToSqr(target) <= 42.25D) {
|
||||
this.grabbedTargetId = target.getId();
|
||||
}
|
||||
|
||||
LivingEntity grabbed = this.getGrabbedTarget();
|
||||
if (grabbed != null && grabbed.isAlive()) {
|
||||
this.holdGrabbedTarget(grabbed);
|
||||
if (this.actionTicks <= 45 && this.actionTicks > 10 && this.actionTicks % 10 == 0) {
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
grabbed.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), 4.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
if (grabbed != null && grabbed.isAlive()) this.pullDown(grabbed, 2.5D);
|
||||
this.grabbedTargetId = -1;
|
||||
this.finishAttack();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSwipeAttack() {
|
||||
if (this.actionTicks == 10) this.performSwipe();
|
||||
if (--this.actionTicks <= 0) this.finishAttack();
|
||||
}
|
||||
|
||||
private void finishAttack() {
|
||||
int finished = this.getAttackState();
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.actionTicks = 0;
|
||||
this.attackCooldown = 18;
|
||||
this.grabbedTargetId = -1;
|
||||
this.lastAttackState = finished;
|
||||
}
|
||||
|
||||
private int chooseNextAttack(LivingEntity target) {
|
||||
double distSqr = this.distanceToSqr(target);
|
||||
boolean canGrab = distSqr <= 42.25D;
|
||||
boolean canSwipe = distSqr <= 81.0D && this.isTargetInFront(target, -0.15D);
|
||||
if (!canGrab && !canSwipe) return ATTACK_NONE;
|
||||
|
||||
int grabWeight = canGrab ? (this.lastAttackState == ATTACK_GRAB ? 1 : 4) : 0;
|
||||
int swipeWeight = canSwipe ? (this.lastAttackState == ATTACK_SWIPE ? 1 : 5) : 0;
|
||||
int total = grabWeight + swipeWeight;
|
||||
if (total == 0) return ATTACK_NONE;
|
||||
|
||||
int roll = this.random.nextInt(total);
|
||||
return roll < grabWeight ? ATTACK_GRAB : ATTACK_SWIPE;
|
||||
}
|
||||
|
||||
private void performSwipe() {
|
||||
AABB hitBox = this.getBoundingBox().inflate(7.0D, 3.0D, 7.0D);
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox,
|
||||
p -> p.isAlive() && this.isTargetInFront(p, -0.15D));
|
||||
float damage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE);
|
||||
for (Player player : players) {
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
player.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), damage);
|
||||
Vec3 dir = player.position().subtract(this.position());
|
||||
Vec3 horizontal = new Vec3(dir.x, 0.0D, dir.z).normalize().scale(1.75D);
|
||||
player.push(horizontal.x, 0.3D, horizontal.z);
|
||||
player.hurtMarked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void holdGrabbedTarget(LivingEntity target) {
|
||||
Vec3 anchor = this.position().add(0.0D, -this.getBbHeight() * 0.4D, 0.0D);
|
||||
target.setDeltaMovement(anchor.subtract(target.position()).scale(0.35D));
|
||||
target.resetFallDistance();
|
||||
if (target instanceof ServerPlayer sp) {
|
||||
sp.connection.teleport(anchor.x, anchor.y, anchor.z, target.getYRot(), target.getXRot());
|
||||
} else {
|
||||
target.moveTo(anchor.x, anchor.y, anchor.z, target.getYRot(), target.getXRot());
|
||||
}
|
||||
target.hurtMarked = true;
|
||||
}
|
||||
|
||||
private void pullDown(LivingEntity target, double strength) {
|
||||
target.push(0.0D, -strength, 0.0D);
|
||||
target.hurtMarked = true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LivingEntity getGrabbedTarget() {
|
||||
if (this.grabbedTargetId < 0) return null;
|
||||
net.minecraft.world.entity.Entity e = this.level().getEntity(this.grabbedTargetId);
|
||||
return e instanceof LivingEntity le ? le : null;
|
||||
}
|
||||
|
||||
private boolean isTargetInFront(LivingEntity target, double minDot) {
|
||||
Vec3 forward = this.getViewVector(1.0F);
|
||||
Vec3 toTarget = target.position().subtract(this.position()).normalize();
|
||||
return forward.dot(toTarget) >= minDot;
|
||||
}
|
||||
|
||||
private void resetCombatState() {
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.setTarget(null);
|
||||
this.actionTicks = 0;
|
||||
this.grabbedTargetId = -1;
|
||||
}
|
||||
|
||||
private void handleAirSupply(int previousAirSupply) {
|
||||
if (this.isInWaterOrBubble()) {
|
||||
this.setAirSupply(this.getMaxAirSupply());
|
||||
return;
|
||||
}
|
||||
this.setAirSupply(previousAirSupply - 1);
|
||||
if (this.getAirSupply() <= -20) {
|
||||
this.setAirSupply(0);
|
||||
this.hurt(this.damageSources().drown(), 2.0F);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSwimRotation() {
|
||||
Vec3 velocity = this.getDeltaMovement();
|
||||
if (velocity.horizontalDistanceSqr() > 1.0E-4D) {
|
||||
float targetYaw = (float) (Mth.atan2(velocity.z, velocity.x) * (180.0D / Math.PI)) - 90.0F;
|
||||
this.setYRot(Mth.approachDegrees(this.getYRot(), targetYaw, 6.0F));
|
||||
this.yBodyRot = this.getYRot();
|
||||
this.yHeadRot = this.getYRot();
|
||||
}
|
||||
if (velocity.lengthSqr() > 1.0E-4D) {
|
||||
float targetPitch = (float) (-(Mth.atan2(velocity.y, velocity.horizontalDistance()) * (180.0D / Math.PI)));
|
||||
this.setXRot(Mth.approachDegrees(this.getXRot(), targetPitch, 4.0F));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimationState() {
|
||||
if (this.isDeadOrDying()) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
return;
|
||||
}
|
||||
switch (this.getAttackState()) {
|
||||
case ATTACK_GRAB -> { this.setAnimationState(ANIM_GRAB); return; }
|
||||
case ATTACK_SWIPE -> { this.setAnimationState(ANIM_SWIPE); return; }
|
||||
default -> {}
|
||||
}
|
||||
if (!this.isInWater()) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
Vec3 vel = this.getDeltaMovement();
|
||||
double hSqr = vel.horizontalDistanceSqr();
|
||||
double vSqr = vel.y * vel.y;
|
||||
if (hSqr > 0.03D && hSqr > vSqr * 2.5D) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
this.setAnimationState(ANIM_IDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damageSource) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
super.die(damageSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tickDeath() {
|
||||
this.deathTime++;
|
||||
if (this.deathTime == 1) this.setAnimationState(ANIM_DEATH);
|
||||
if (this.deathTime >= DEATH_ANIM_TICKS) {
|
||||
this.remove(RemovalReason.KILLED);
|
||||
this.dropExperience(this);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Inner goal: only target players when spawned by Kraken in fly mode
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private static final class KrakenSummonTargetGoal extends NearestAttackableTargetGoal<Player> {
|
||||
private final OctopusBombEntity octopus;
|
||||
|
||||
KrakenSummonTargetGoal(OctopusBombEntity octopus) {
|
||||
super(octopus, Player.class, true);
|
||||
this.octopus = octopus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return this.octopus.spawnedByKraken && this.octopus.canFly() && super.canUse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,13 @@ package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
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.sounds.SoundEvent;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
@@ -13,6 +18,7 @@ import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.SpawnGroupData;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
@@ -39,6 +45,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ScorpionEntity extends Monster implements GeoEntity {
|
||||
private static final byte ATTACK_ANIM_EVENT = 4;
|
||||
private static final EntityDataAccessor<Integer> TEXTURE_VARIANT =
|
||||
SynchedEntityData.defineId(ScorpionEntity.class, EntityDataSerializers.INT);
|
||||
private static final String TEXTURE_VARIANT_KEY = "TextureVariant";
|
||||
public static final int BLUE_VARIANT = 0;
|
||||
public static final int GREEN_VARIANT = 1;
|
||||
|
||||
private static final int ATTACK_ANIM_TICKS = 12;
|
||||
private static final int ATTACK_HIT_TICK = 6;
|
||||
@@ -82,6 +93,12 @@ public class ScorpionEntity extends Monster implements GeoEntity {
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(TEXTURE_VARIANT, BLUE_VARIANT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
@@ -93,6 +110,13 @@ public class ScorpionEntity extends Monster implements GeoEntity {
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SpawnGroupData finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType spawnType, @Nullable SpawnGroupData spawnGroupData) {
|
||||
this.assignRandomTextureVariant();
|
||||
return super.finalizeSpawn(level, difficulty, spawnType, spawnGroupData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
@@ -208,6 +232,22 @@ public class ScorpionEntity extends Monster implements GeoEntity {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt(TEXTURE_VARIANT_KEY, this.getTextureVariant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
if (tag.contains(TEXTURE_VARIANT_KEY)) {
|
||||
this.setTextureVariant(tag.getInt(TEXTURE_VARIANT_KEY));
|
||||
} else {
|
||||
this.assignRandomTextureVariant();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return AntarchySoundEvents.SCORPION_AMBIENT.get();
|
||||
@@ -228,6 +268,18 @@ public class ScorpionEntity extends Monster implements GeoEntity {
|
||||
return 0.6F;
|
||||
}
|
||||
|
||||
public int getTextureVariant() {
|
||||
return this.entityData.get(TEXTURE_VARIANT);
|
||||
}
|
||||
|
||||
public void setTextureVariant(int variant) {
|
||||
this.entityData.set(TEXTURE_VARIANT, variant == GREEN_VARIANT ? GREEN_VARIANT : BLUE_VARIANT);
|
||||
}
|
||||
|
||||
private void assignRandomTextureVariant() {
|
||||
this.setTextureVariant(this.random.nextBoolean() ? BLUE_VARIANT : GREEN_VARIANT);
|
||||
}
|
||||
|
||||
private final class ScorpionMeleeAttackGoal extends MeleeAttackGoal {
|
||||
private ScorpionMeleeAttackGoal() {
|
||||
super(ScorpionEntity.this, 1.15D, true);
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class SpitBugEntity extends Monster implements GeoEntity {
|
||||
private static final EntityDataAccessor<Integer> SPIT_ANIM_TICKS =
|
||||
SynchedEntityData.defineId(SpitBugEntity.class, EntityDataSerializers.INT);
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation SPIT_ANIM = RawAnimation.begin().thenPlay("spit");
|
||||
|
||||
private static final double MAX_HEALTH = 50.0D;
|
||||
private static final double MOVEMENT_SPEED = 0.23D;
|
||||
private static final double ATTACK_DAMAGE = 5.0D;
|
||||
private static final double FOLLOW_RANGE = 24.0D;
|
||||
private static final double SPIT_RANGE_SQR = 14.0D * 14.0D;
|
||||
private static final double MELEE_RANGE_SQR = 3.5D * 3.5D;
|
||||
private static final int SPIT_COOLDOWN_TICKS = 55;
|
||||
private static final int SPIT_ANIMATION_TICKS = 18;
|
||||
private static final int SPIT_RELEASE_TICKS = 8;
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private int spitCooldownTicks;
|
||||
private int spitAnimationTicks;
|
||||
private int spitReleaseTicks;
|
||||
|
||||
public SpitBugEntity(EntityType<? extends SpitBugEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.xpReward = 10;
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, MAX_HEALTH)
|
||||
.add(Attributes.MOVEMENT_SPEED, MOVEMENT_SPEED)
|
||||
.add(Attributes.ATTACK_DAMAGE, ATTACK_DAMAGE)
|
||||
.add(Attributes.FOLLOW_RANGE, FOLLOW_RANGE)
|
||||
.add(Attributes.ARMOR, 4.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.2D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<SpitBugEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& level.isEmptyBlock(pos)
|
||||
&& level.isEmptyBlock(pos.above())
|
||||
&& Monster.checkMonsterSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(SPIT_ANIM_TICKS, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new SpitAttackGoal());
|
||||
this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
|
||||
this.goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 0.9D));
|
||||
this.goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 12.0F));
|
||||
this.goalSelector.addGoal(5, new RandomLookAroundGoal(this));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 2, this::mainAnimController));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (this.level().isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.spitCooldownTicks > 0) {
|
||||
this.spitCooldownTicks--;
|
||||
}
|
||||
|
||||
if (this.spitAnimationTicks > 0) {
|
||||
this.spitAnimationTicks--;
|
||||
this.entityData.set(SPIT_ANIM_TICKS, this.spitAnimationTicks);
|
||||
if (this.spitReleaseTicks > 0 && --this.spitReleaseTicks == 0) {
|
||||
this.fireSpitProjectile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doHurtTarget(net.minecraft.world.entity.Entity target) {
|
||||
boolean hurt = super.doHurtTarget(target);
|
||||
if (hurt) {
|
||||
this.playSound(SoundEvents.SPIDER_HURT, 0.7F, 0.8F + this.random.nextFloat() * 0.2F);
|
||||
}
|
||||
return hurt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getAmbientSound() {
|
||||
return SoundEvents.SPIDER_AMBIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return SoundEvents.SPIDER_HURT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent getDeathSound() {
|
||||
return SoundEvents.SPIDER_DEATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playStepSound(BlockPos pos, BlockState blockState) {
|
||||
this.playSound(SoundEvents.SPIDER_STEP, 0.18F, 0.95F);
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<SpitBugEntity> state) {
|
||||
if (this.entityData.get(SPIT_ANIM_TICKS) > 0) {
|
||||
return state.setAndContinue(SPIT_ANIM);
|
||||
}
|
||||
return state.setAndContinue(state.isMoving() ? WALK_ANIM : IDLE_ANIM);
|
||||
}
|
||||
|
||||
private boolean canSpitAt(LivingEntity target) {
|
||||
return this.spitCooldownTicks <= 0
|
||||
&& this.spitAnimationTicks <= 0
|
||||
&& this.hasLineOfSight(target)
|
||||
&& this.distanceToSqr(target) <= SPIT_RANGE_SQR
|
||||
&& this.distanceToSqr(target) > MELEE_RANGE_SQR;
|
||||
}
|
||||
|
||||
private void startSpitAttack() {
|
||||
this.spitCooldownTicks = SPIT_COOLDOWN_TICKS;
|
||||
this.spitAnimationTicks = SPIT_ANIMATION_TICKS;
|
||||
this.spitReleaseTicks = SPIT_RELEASE_TICKS;
|
||||
this.entityData.set(SPIT_ANIM_TICKS, this.spitAnimationTicks);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void fireSpitProjectile() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityType<SpitBugProjectileEntity> projectileType = (EntityType<SpitBugProjectileEntity>) BuiltInRegistries.ENTITY_TYPE.getOptional(
|
||||
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "spit_bug_projectile")
|
||||
).orElse(null);
|
||||
if (projectileType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpitBugProjectileEntity projectile = new SpitBugProjectileEntity(projectileType, this, serverLevel);
|
||||
Vec3 origin = this.position()
|
||||
.add(0.0D, this.getBbHeight() * 0.55D, 0.0D)
|
||||
.add(this.getViewVector(0.0F).scale(1.35D));
|
||||
Vec3 aimPoint = target.getEyePosition().add(target.getDeltaMovement().scale(0.45D));
|
||||
Vec3 direction = aimPoint.subtract(origin).normalize();
|
||||
|
||||
projectile.setPos(origin.x, origin.y, origin.z);
|
||||
projectile.shoot(direction.x, direction.y, direction.z, 1.05F, 0.0F);
|
||||
serverLevel.addFreshEntity(projectile);
|
||||
this.playSound(SoundEvents.SLIME_ATTACK, 1.0F, 0.85F + this.random.nextFloat() * 0.15F);
|
||||
}
|
||||
|
||||
private final class SpitAttackGoal extends Goal {
|
||||
SpitAttackGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
LivingEntity target = SpitBugEntity.this.getTarget();
|
||||
return target != null && target.isAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return this.canUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
LivingEntity target = SpitBugEntity.this.getTarget();
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SpitBugEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
|
||||
if (SpitBugEntity.this.canSpitAt(target)) {
|
||||
SpitBugEntity.this.getNavigation().stop();
|
||||
SpitBugEntity.this.startSpitAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
if (SpitBugEntity.this.distanceToSqr(target) > 36.0D || !SpitBugEntity.this.hasLineOfSight(target)) {
|
||||
SpitBugEntity.this.getNavigation().moveTo(target, 1.0D);
|
||||
} else {
|
||||
SpitBugEntity.this.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
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.LivingEntity;
|
||||
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class SpitBugProjectileEntity extends ThrowableItemProjectile {
|
||||
private static final EntityDataAccessor<Boolean> LINGERING =
|
||||
SynchedEntityData.defineId(SpitBugProjectileEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final int MAX_FLIGHT_TICKS = 100;
|
||||
private static final int LINGER_DURATION_TICKS = 300;
|
||||
private static final int DIRECT_HIT_DAMAGE = 8;
|
||||
private static final float LINGER_RADIUS = 1.65F;
|
||||
|
||||
private boolean lingering;
|
||||
private int lingeringTicks;
|
||||
|
||||
public SpitBugProjectileEntity(EntityType<? extends SpitBugProjectileEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
}
|
||||
|
||||
public SpitBugProjectileEntity(EntityType<? extends SpitBugProjectileEntity> entityType, LivingEntity owner, Level level) {
|
||||
super(entityType, owner, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(LINGERING, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Item getDefaultItem() {
|
||||
return Items.SLIME_BALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getDefaultGravity() {
|
||||
return 0.03D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCollidedWith() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAttackable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLingering() {
|
||||
return this.entityData.get(LINGERING);
|
||||
}
|
||||
|
||||
public float getRenderScale(float partialTick) {
|
||||
if (!this.isLingering()) {
|
||||
return 0.65F;
|
||||
}
|
||||
float progress = Math.min(1.0F, (this.lingeringTicks + partialTick) / 8.0F);
|
||||
return 0.6F + progress * 0.5F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.isLingering()) {
|
||||
this.tickLingering();
|
||||
return;
|
||||
}
|
||||
|
||||
super.tick();
|
||||
if (this.tickCount > MAX_FLIGHT_TICKS) {
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.level().isClientSide) {
|
||||
this.level().addParticle(ParticleTypes.ITEM_SLIME, this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
private void tickLingering() {
|
||||
this.lingeringTicks++;
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
|
||||
if (this.level().isClientSide) {
|
||||
double radius = 0.4D + this.random.nextDouble() * 0.45D;
|
||||
this.level().addParticle(
|
||||
ParticleTypes.ITEM_SLIME,
|
||||
this.getX() + (this.random.nextDouble() - 0.5D) * radius,
|
||||
this.getY() + 0.04D,
|
||||
this.getZ() + (this.random.nextDouble() - 0.5D) * radius,
|
||||
0.0D,
|
||||
0.0D,
|
||||
0.0D
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.lingeringTicks % 4 == 0) {
|
||||
this.applyLingeringSpit();
|
||||
}
|
||||
|
||||
if (this.lingeringTicks >= LINGER_DURATION_TICKS) {
|
||||
this.discard();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLingeringSpit() {
|
||||
for (LivingEntity target : this.level().getEntitiesOfClass(
|
||||
LivingEntity.class,
|
||||
this.getBoundingBox().inflate(LINGER_RADIUS, 0.6D, LINGER_RADIUS),
|
||||
entity -> entity.isAlive() && entity != this.getOwner()
|
||||
)) {
|
||||
target.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 30, 3, false, true, true));
|
||||
target.makeStuckInBlock(Blocks.HONEY_BLOCK.defaultBlockState(), new Vec3(0.2D, 0.0D, 0.2D));
|
||||
}
|
||||
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
serverLevel.sendParticles(ParticleTypes.ITEM_SLIME, this.getX(), this.getY() + 0.05D, this.getZ(), 3, 0.45D, 0.02D, 0.45D, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHitEntity(EntityHitResult result) {
|
||||
if (!this.level().isClientSide && result.getEntity() instanceof LivingEntity livingTarget) {
|
||||
Entity owner = this.getOwner();
|
||||
if (owner instanceof LivingEntity livingOwner) {
|
||||
livingTarget.hurt(this.damageSources().mobProjectile(this, livingOwner), DIRECT_HIT_DAMAGE);
|
||||
} else {
|
||||
livingTarget.hurt(this.damageSources().magic(), DIRECT_HIT_DAMAGE);
|
||||
}
|
||||
livingTarget.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 80, 3, false, true, true));
|
||||
livingTarget.makeStuckInBlock(Blocks.HONEY_BLOCK.defaultBlockState(), new Vec3(0.25D, 0.0D, 0.25D));
|
||||
}
|
||||
|
||||
this.beginLingering(result.getLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHitBlock(BlockHitResult result) {
|
||||
this.beginLingering(result.getLocation());
|
||||
}
|
||||
|
||||
private void beginLingering(Vec3 location) {
|
||||
if (this.isLingering()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lingering = true;
|
||||
this.entityData.set(LINGERING, true);
|
||||
this.lingeringTicks = 0;
|
||||
this.noPhysics = true;
|
||||
this.setPos(location.x, location.y + 0.05D, location.z);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.StinkyBehavior;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.AgeableMob;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.FloatGoal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.PanicGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.animation.AnimatableManager;
|
||||
import software.bernie.geckolib.animation.AnimationController;
|
||||
import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.animation.PlayState;
|
||||
import software.bernie.geckolib.animation.RawAnimation;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
public class StinkBugEntity extends Animal implements GeoEntity {
|
||||
private static final String MAIN_CONTROLLER = "main_controller";
|
||||
private static final String FART_CONTROLLER = "fart_controller";
|
||||
private static final String FART_TRIGGER = "fart";
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation FART_ANIM = RawAnimation.begin().thenPlay("fart");
|
||||
private static final int FART_ANIMATION_TICKS = 30;
|
||||
private static final int FART_RELEASE_TICKS = 6;
|
||||
private static final int STINK_BURST_PARTICLES = 32;
|
||||
private static final double STINK_BURST_RADIUS = 8.0D;
|
||||
private static final int STINK_BURST_DURATION_TICKS = 1200;
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
private int fartAnimationTicks;
|
||||
private boolean pendingFartBurst;
|
||||
|
||||
public StinkBugEntity(EntityType<? extends StinkBugEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.xpReward = 2;
|
||||
}
|
||||
|
||||
public static AttributeSupplier.Builder createAttributes() {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, 6.0D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.24D)
|
||||
.add(Attributes.FOLLOW_RANGE, 8.0D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<StinkBugEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
|
||||
if (spawnReason == MobSpawnType.SPAWN_EGG || spawnReason == MobSpawnType.SPAWNER || spawnReason == MobSpawnType.COMMAND) {
|
||||
return true;
|
||||
}
|
||||
return level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& Animal.checkAnimalSpawnRules(entityType, level, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new FloatGoal(this));
|
||||
this.goalSelector.addGoal(1, new PanicGoal(this, 1.4D));
|
||||
this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 0.9D));
|
||||
this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 6.0F));
|
||||
this.goalSelector.addGoal(4, new RandomLookAroundGoal(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
if (this.fartAnimationTicks > 0) {
|
||||
this.fartAnimationTicks--;
|
||||
if (this.fartAnimationTicks == FART_RELEASE_TICKS && this.pendingFartBurst && !this.level().isClientSide()) {
|
||||
StinkyBehavior.emitBurst(this, STINK_BURST_PARTICLES);
|
||||
StinkyBehavior.applyBurstStinkyEffect(this, STINK_BURST_RADIUS, STINK_BURST_DURATION_TICKS);
|
||||
this.pendingFartBurst = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
boolean hurt = super.hurt(source, amount);
|
||||
if (hurt && !this.pendingFartBurst) {
|
||||
this.triggerFartAnimation();
|
||||
this.pendingFartBurst = true;
|
||||
}
|
||||
return hurt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFood(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) {
|
||||
return AntarchyObjects.STINK_BUG.get().create(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, MAIN_CONTROLLER, 2, this::mainAnimController));
|
||||
controllers.add(new AnimationController<>(this, FART_CONTROLLER, 0, state -> PlayState.STOP)
|
||||
.triggerableAnim(FART_TRIGGER, FART_ANIM));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatableInstanceCache getAnimatableInstanceCache() {
|
||||
return this.geoCache;
|
||||
}
|
||||
|
||||
private <T extends StinkBugEntity> PlayState mainAnimController(AnimationState<T> animationState) {
|
||||
animationState.setAnimation(animationState.isMoving() ? WALK_ANIM : IDLE_ANIM);
|
||||
return PlayState.CONTINUE;
|
||||
}
|
||||
|
||||
private void triggerFartAnimation() {
|
||||
this.fartAnimationTicks = FART_ANIMATION_TICKS;
|
||||
if (!this.level().isClientSide()) {
|
||||
this.triggerAnim(FART_CONTROLLER, FART_TRIGGER);
|
||||
}
|
||||
}
|
||||
}
|
||||
+473
-56
@@ -2,6 +2,8 @@ package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -15,6 +17,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.BossEvent;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.tags.DamageTypeTags;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
@@ -25,6 +28,7 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
|
||||
import net.minecraft.world.entity.ai.goal.RandomLookAroundGoal;
|
||||
import net.minecraft.world.entity.ai.goal.MoveTowardsTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.WaterAvoidingRandomStrollGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.HurtByTargetGoal;
|
||||
import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
|
||||
@@ -53,38 +57,56 @@ import java.util.UUID;
|
||||
public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
|
||||
private static final EntityDataAccessor<Integer> ANIMATION_STATE = SynchedEntityData.defineId(ToreterrorEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> PHASE_2 = SynchedEntityData.defineId(ToreterrorEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Boolean> JUMP_SHAKING = SynchedEntityData.defineId(ToreterrorEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private static final int ANIM_IDLE = 0;
|
||||
private static final int ANIM_WALK = 1;
|
||||
private static final int ANIM_SHOOT = 2;
|
||||
private static final int ANIM_JUMP_ATTACK = 3;
|
||||
private static final int ANIM_SPIN = 4;
|
||||
private static final int ANIM_DEATH = 5;
|
||||
private static final int ANIM_SPIN_START = 4;
|
||||
private static final int ANIM_SPIN_LOOP = 5;
|
||||
private static final int ANIM_SPIN_END = 6;
|
||||
private static final int ANIM_DEATH = 7;
|
||||
private static final int ANIM_RAPID_SHOOT = 8;
|
||||
|
||||
private static final int ATTACK_RAPID_FIRE = 0;
|
||||
private static final int ATTACK_BOMBERS = 1;
|
||||
|
||||
private static final int DEATH_TICKS = 60;
|
||||
private static final int SHOOT_TOTAL_TICKS = 80;
|
||||
private static final int SHOOT_FIRE_START_TICK = 12;
|
||||
private static final int SHOOT_FIRE_END_TICK = 68;
|
||||
private static final int SHOOT_FIRE_INTERVAL = 8;
|
||||
private static final int SHOOT_TOTAL_TICKS = 75;
|
||||
private static final int RAPID_FIRE_INTERVAL = 4;
|
||||
private static final int RAPID_FIRE_END_TICK = 60;
|
||||
private static final int SHOOT_BOMBER_TICK = 12;
|
||||
private static final int SHOOT_BOMBER_SECOND_TICK = 32;
|
||||
private static final int JUMP_TOTAL_TICKS = 40;
|
||||
private static final int JUMP_LAUNCH_TICK = 10;
|
||||
private static final int SPIN_TOTAL_TICKS = 40;
|
||||
private static final int SPIN_START_TICK = 3;
|
||||
private static final int SPIN_END_TICK = 22;
|
||||
private static final int JUMP_SHAKE_TICKS = 12;
|
||||
private static final int SPIN_START_TOTAL_TICKS = 5;
|
||||
private static final int SPIN_LOOP_MIN_TICKS = 200;
|
||||
private static final int SPIN_LOOP_MAX_TICKS = 300;
|
||||
private static final int SPIN_END_TOTAL_TICKS = 10;
|
||||
private static final int SPIN_HIT_INTERVAL = 5;
|
||||
private static final int SPIN_STUCK_FAIL_LIMIT = 10;
|
||||
|
||||
private static final double RANGED_MIN_RANGE_SQR = 9.0D;
|
||||
private static final double CLOSE_RANGE_SQR = 64.0D;
|
||||
private static final double JUMP_RANGE_SQR = 400.0D;
|
||||
private static final double RANGED_MAX_RANGE_SQR = 625.0D;
|
||||
private static final double MELEE_RANGE_SQR = 9.0D;
|
||||
private static final double SPIN_RADIUS = 4.5D;
|
||||
private static final double SPIN_TRIGGER_RANGE_SQR = 225.0D;
|
||||
private static final double SPIN_LEASH_RANGE_SQR = 225.0D;
|
||||
private static final double SPIN_RADIUS = 2.5D;
|
||||
private static final double SPIN_MOVE_SPEED = 0.65D;
|
||||
private static final double SPIN_ANIMATION_SPEED = 0.49D;
|
||||
private static final double JUMP_SHOCKWAVE_RADIUS = 6.0D;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("Idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation SHOOT_ANIM = RawAnimation.begin().thenPlay("shoot").thenLoop("Idle");
|
||||
private static final RawAnimation RAPID_SHOOT_ANIM = RawAnimation.begin().thenLoop("shoot");
|
||||
private static final RawAnimation JUMP_ANIM = RawAnimation.begin().thenPlay("jump_attack").thenLoop("Idle");
|
||||
private static final RawAnimation SPIN_ANIM = RawAnimation.begin().thenPlay("spin_attack").thenLoop("Idle");
|
||||
private static final RawAnimation SPIN_START_ANIM = RawAnimation.begin().thenPlay("spin_attack_start");
|
||||
private static final RawAnimation SPIN_LOOP_ANIM = RawAnimation.begin().thenLoop("spin_attack");
|
||||
private static final RawAnimation SPIN_END_ANIM = RawAnimation.begin().thenPlay("spin_attack_end");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death");
|
||||
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
@@ -92,15 +114,26 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
|
||||
private int shootAnimTicks;
|
||||
private int jumpAnimTicks;
|
||||
private int spinAnimTicks;
|
||||
private int spinStartTicks;
|
||||
private int spinLoopTicks;
|
||||
private int spinEndTicks;
|
||||
private int spinLoopTotalTicks;
|
||||
private int shootCooldown;
|
||||
private int jumpCooldown;
|
||||
private int spinCooldown;
|
||||
private boolean jumpLaunched;
|
||||
private boolean jumpAirborne;
|
||||
private boolean shockwaveApplied;
|
||||
private boolean shootUseWaterBombs;
|
||||
private int jumpShakeTicks;
|
||||
private int shootAttackType;
|
||||
private boolean shootBomberFired;
|
||||
private int shootNextFireElapsed;
|
||||
private boolean shootBomberFiredSecond;
|
||||
private int rapidFireNextTick;
|
||||
private Vec3 spinDirection = Vec3.ZERO;
|
||||
private int spinStrafeSign = 1;
|
||||
private int spinStuckTicks;
|
||||
private Vec3 spinProgressAnchor = Vec3.ZERO;
|
||||
private int spinProgressTicks;
|
||||
private final Map<UUID, Integer> spinHitCooldowns = new HashMap<>();
|
||||
|
||||
public ToreterrorEntity(EntityType<? extends ToreterrorEntity> entityType, Level level) {
|
||||
@@ -112,10 +145,11 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
return Mob.createMobAttributes()
|
||||
.add(Attributes.MAX_HEALTH, AntarchySettings.toreterrorHealth())
|
||||
.add(Attributes.ATTACK_DAMAGE, 8.0D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.25D)
|
||||
.add(Attributes.MOVEMENT_SPEED, 0.325D)
|
||||
.add(Attributes.FOLLOW_RANGE, 48.0D)
|
||||
.add(Attributes.KNOCKBACK_RESISTANCE, 0.85D)
|
||||
.add(Attributes.ARMOR, 10.0D);
|
||||
.add(Attributes.ARMOR, 16.0D)
|
||||
.add(Attributes.ARMOR_TOUGHNESS, 4.0D);
|
||||
}
|
||||
|
||||
public static boolean canSpawn(EntityType<ToreterrorEntity> entityType, ServerLevelAccessor level, MobSpawnType spawnReason, BlockPos pos, net.minecraft.util.RandomSource random) {
|
||||
@@ -127,16 +161,20 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
super.defineSynchedData(builder);
|
||||
builder.define(ANIMATION_STATE, ANIM_IDLE);
|
||||
builder.define(PHASE_2, false);
|
||||
builder.define(JUMP_SHAKING, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
this.goalSelector.addGoal(0, new ToreterrorFloatGoal());
|
||||
this.goalSelector.addGoal(1, new ToreterrorRangedGoal());
|
||||
this.goalSelector.addGoal(2, new ToreterrorJumpGoal());
|
||||
this.goalSelector.addGoal(3, new ToreterrorSpinGoal());
|
||||
this.goalSelector.addGoal(4, new MoveTowardsTargetGoal(this, 1.0D, 64.0F));
|
||||
this.goalSelector.addGoal(5, new LookAtPlayerGoal(this, Player.class, 16.0F));
|
||||
this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
|
||||
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.6D));
|
||||
this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 0.5D));
|
||||
this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
|
||||
this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::canTargetEntity));
|
||||
}
|
||||
@@ -148,11 +186,18 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
|
||||
private PlayState mainAnimController(AnimationState<ToreterrorEntity> state) {
|
||||
int animState = this.getAnimationState();
|
||||
state.getController().setAnimationSpeed(1.0D);
|
||||
return switch (animState) {
|
||||
case ANIM_WALK -> state.setAndContinue(WALK_ANIM);
|
||||
case ANIM_SHOOT -> state.setAndContinue(SHOOT_ANIM);
|
||||
case ANIM_RAPID_SHOOT -> state.setAndContinue(RAPID_SHOOT_ANIM);
|
||||
case ANIM_JUMP_ATTACK -> state.setAndContinue(JUMP_ANIM);
|
||||
case ANIM_SPIN -> state.setAndContinue(SPIN_ANIM);
|
||||
case ANIM_SPIN_START -> state.setAndContinue(SPIN_START_ANIM);
|
||||
case ANIM_SPIN_LOOP -> {
|
||||
state.getController().setAnimationSpeed(SPIN_ANIMATION_SPEED);
|
||||
yield state.setAndContinue(SPIN_LOOP_ANIM);
|
||||
}
|
||||
case ANIM_SPIN_END -> state.setAndContinue(SPIN_END_ANIM);
|
||||
case ANIM_DEATH -> state.setAndContinue(DEATH_ANIM);
|
||||
default -> state.setAndContinue(IDLE_ANIM);
|
||||
};
|
||||
@@ -166,11 +211,27 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (this.level().isClientSide) return;
|
||||
if (this.level().isClientSide) {
|
||||
if (this.isJumpShaking()) {
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.register(
|
||||
this,
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.TORETERROR_RANGE,
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.TORETERROR_STRENGTH,
|
||||
this::isJumpShaking
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.shootCooldown > 0) this.shootCooldown--;
|
||||
if (this.jumpCooldown > 0) this.jumpCooldown--;
|
||||
if (this.spinCooldown > 0) this.spinCooldown--;
|
||||
if (this.jumpShakeTicks > 0) {
|
||||
this.jumpShakeTicks--;
|
||||
if (this.jumpShakeTicks <= 0) {
|
||||
this.entityData.set(JUMP_SHAKING, false);
|
||||
}
|
||||
}
|
||||
|
||||
this.spinHitCooldowns.replaceAll((uuid, cd) -> cd - 1);
|
||||
this.spinHitCooldowns.entrySet().removeIf(e -> e.getValue() <= 0);
|
||||
@@ -184,8 +245,8 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
this.tickShootAnimation();
|
||||
} else if (this.jumpAnimTicks > 0) {
|
||||
this.tickJumpAnimation();
|
||||
} else if (this.spinAnimTicks > 0) {
|
||||
this.tickSpinAnimation();
|
||||
} else if (this.isSpinSequenceActive()) {
|
||||
this.tickSpinSequence();
|
||||
} else {
|
||||
this.updateIdleAnimation();
|
||||
}
|
||||
@@ -193,6 +254,55 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
this.bossEvent.setProgress(this.getHealth() / this.getMaxHealth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
if (source.is(net.minecraft.world.damagesource.DamageTypes.DROWN)) return false;
|
||||
if (source.is(DamageTypeTags.IS_PROJECTILE)) {
|
||||
amount *= (float) AntarchySettings.toreterrorProjectileDamageMultiplier();
|
||||
}
|
||||
if (!this.isPhase2()) {
|
||||
float halfHealth = this.getMaxHealth() * 0.5F;
|
||||
if (this.getHealth() - amount <= halfHealth) {
|
||||
float capDamage = this.getHealth() - halfHealth;
|
||||
if (capDamage <= 0.0F) {
|
||||
this.enterPhase2();
|
||||
return false;
|
||||
}
|
||||
boolean result = super.hurt(source, capDamage);
|
||||
if (result) this.enterPhase2();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return super.hurt(source, amount);
|
||||
}
|
||||
|
||||
private void enterPhase2() {
|
||||
this.entityData.set(PHASE_2, true);
|
||||
}
|
||||
|
||||
public boolean isPhase2() {
|
||||
return this.entityData.get(PHASE_2);
|
||||
}
|
||||
|
||||
public boolean isJumpShaking() {
|
||||
return this.entityData.get(JUMP_SHAKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected net.minecraft.sounds.SoundEvent getAmbientSound() {
|
||||
return AntarchySoundEvents.TORETERROR_IDLE.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected net.minecraft.sounds.SoundEvent getHurtSound(DamageSource damageSource) {
|
||||
return AntarchySoundEvents.TORETERROR_HURT.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected net.minecraft.sounds.SoundEvent getDeathSound() {
|
||||
return AntarchySoundEvents.TORETERROR_DEATH.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSeenByPlayer(ServerPlayer player) {
|
||||
super.startSeenByPlayer(player);
|
||||
@@ -243,7 +353,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.shootAnimTicks = 0;
|
||||
this.jumpAnimTicks = 0;
|
||||
this.spinAnimTicks = 0;
|
||||
this.stopSpinSequence();
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.getNavigation().stop();
|
||||
}
|
||||
@@ -279,27 +389,44 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean isInAttackAnimation() {
|
||||
return this.shootAnimTicks > 0 || this.jumpAnimTicks > 0 || this.spinAnimTicks > 0;
|
||||
return this.shootAnimTicks > 0 || this.jumpAnimTicks > 0 || this.isSpinSequenceActive();
|
||||
}
|
||||
|
||||
private boolean isSpinSequenceActive() {
|
||||
return this.spinStartTicks > 0 || this.spinLoopTicks > 0 || this.spinEndTicks > 0;
|
||||
}
|
||||
|
||||
private void tickShootAnimation() {
|
||||
this.setAnimationState(ANIM_SHOOT);
|
||||
int elapsed = SHOOT_TOTAL_TICKS - this.shootAnimTicks;
|
||||
if (this.shootUseWaterBombs) {
|
||||
if (elapsed >= this.shootNextFireElapsed && this.shootNextFireElapsed <= SHOOT_FIRE_END_TICK) {
|
||||
this.shootNextFireElapsed += SHOOT_FIRE_INTERVAL;
|
||||
this.fireWaterBomb();
|
||||
switch (this.shootAttackType) {
|
||||
case ATTACK_RAPID_FIRE -> {
|
||||
this.setAnimationState(ANIM_RAPID_SHOOT);
|
||||
if (elapsed >= this.rapidFireNextTick && elapsed < RAPID_FIRE_END_TICK) {
|
||||
this.rapidFireNextTick += RAPID_FIRE_INTERVAL;
|
||||
this.fireRapidBomb();
|
||||
}
|
||||
} else {
|
||||
}
|
||||
default -> {
|
||||
this.setAnimationState(ANIM_SHOOT);
|
||||
if (!this.shootBomberFired && elapsed >= SHOOT_BOMBER_TICK) {
|
||||
this.shootBomberFired = true;
|
||||
this.fireBomberBurst();
|
||||
}
|
||||
if (this.isPhase2() && !this.shootBomberFiredSecond && elapsed >= SHOOT_BOMBER_SECOND_TICK) {
|
||||
this.shootBomberFiredSecond = true;
|
||||
this.fireBomberBurst();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.85D));
|
||||
if (this.getTarget() != null) {
|
||||
this.getLookControl().setLookAt(this.getTarget(), 30.0F, 20.0F);
|
||||
Vec3 toTarget = this.getTarget().position().subtract(this.position());
|
||||
float yaw = (float) (net.minecraft.util.Mth.atan2(toTarget.z, toTarget.x) * (180.0D / Math.PI)) - 90.0F;
|
||||
this.setYRot(yaw);
|
||||
this.yBodyRot = yaw;
|
||||
this.yHeadRot = yaw;
|
||||
this.setYHeadRot(yaw);
|
||||
}
|
||||
this.shootAnimTicks--;
|
||||
if (this.shootAnimTicks <= 0) {
|
||||
@@ -315,13 +442,28 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null) {
|
||||
Vec3 toTarget = target.position().subtract(this.position()).normalize();
|
||||
this.setDeltaMovement(toTarget.x * 0.6D, 0.85D, toTarget.z * 0.6D);
|
||||
this.setDeltaMovement(toTarget.x * 0.6D, 1.15D, toTarget.z * 0.6D);
|
||||
this.hasImpulse = true;
|
||||
}
|
||||
}
|
||||
if (this.jumpLaunched && this.onGround() && elapsed > JUMP_LAUNCH_TICK + 3 && !this.shockwaveApplied) {
|
||||
if (this.jumpLaunched && !this.onGround()) {
|
||||
this.jumpAirborne = true;
|
||||
}
|
||||
if (this.jumpLaunched && this.jumpAirborne && !this.shockwaveApplied) {
|
||||
if (this.onGround()) {
|
||||
this.shockwaveApplied = true;
|
||||
this.playSound(AntarchySoundEvents.TORETERROR_JUMP_LAND.get(), 1.2F, 0.9F + this.random.nextFloat() * 0.15F);
|
||||
this.applyJumpShockwave();
|
||||
this.jumpShakeTicks = JUMP_SHAKE_TICKS;
|
||||
this.entityData.set(JUMP_SHAKING, true);
|
||||
} else if (this.jumpAnimTicks <= 10) {
|
||||
Vec3 vel = this.getDeltaMovement();
|
||||
this.setDeltaMovement(vel.x * 0.5D, Math.min(vel.y, -1.5D), vel.z * 0.5D);
|
||||
this.hasImpulse = true;
|
||||
if (this.jumpAnimTicks <= 2) {
|
||||
this.jumpAnimTicks = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.jumpAnimTicks--;
|
||||
if (this.jumpAnimTicks <= 0) {
|
||||
@@ -329,35 +471,270 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSpinAnimation() {
|
||||
this.setAnimationState(ANIM_SPIN);
|
||||
int elapsed = SPIN_TOTAL_TICKS - this.spinAnimTicks;
|
||||
private void tickSpinSequence() {
|
||||
this.getNavigation().stop();
|
||||
this.setDeltaMovement(this.getDeltaMovement().multiply(0.7D, 1.0D, 0.7D));
|
||||
if (elapsed >= SPIN_START_TICK && elapsed <= SPIN_END_TICK && elapsed % SPIN_HIT_INTERVAL == 0) {
|
||||
if (this.spinStartTicks > 0) {
|
||||
this.tickSpinStart();
|
||||
return;
|
||||
}
|
||||
if (this.spinLoopTicks > 0) {
|
||||
this.tickSpinLoop();
|
||||
return;
|
||||
}
|
||||
this.tickSpinEnd();
|
||||
}
|
||||
|
||||
private void tickSpinStart() {
|
||||
this.setAnimationState(ANIM_SPIN_START);
|
||||
this.setDeltaMovement(this.getDeltaMovement().scale(0.75D));
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target != null) {
|
||||
Vec3 towardTarget = horizontalDirectionTo(target.position());
|
||||
if (towardTarget.lengthSqr() > 1.0E-4D) {
|
||||
this.spinDirection = towardTarget;
|
||||
this.lookInDirection(towardTarget);
|
||||
}
|
||||
}
|
||||
this.spinStartTicks--;
|
||||
}
|
||||
|
||||
private void tickSpinLoop() {
|
||||
this.setAnimationState(ANIM_SPIN_LOOP);
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null || !target.isAlive()) {
|
||||
this.beginSpinEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateSpinDirection(target);
|
||||
if (!this.moveDuringSpin()) {
|
||||
this.spinStuckTicks++;
|
||||
if (this.spinStuckTicks >= SPIN_STUCK_FAIL_LIMIT) {
|
||||
this.beginSpinEnd();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.spinStuckTicks = 0;
|
||||
}
|
||||
|
||||
this.spinProgressTicks++;
|
||||
if (this.spinProgressTicks >= 20) {
|
||||
if (this.position().distanceToSqr(this.spinProgressAnchor) < 4.0D) {
|
||||
this.beginSpinEnd();
|
||||
return;
|
||||
}
|
||||
this.spinProgressAnchor = this.position();
|
||||
this.spinProgressTicks = 0;
|
||||
}
|
||||
|
||||
int elapsed = this.spinLoopTotalTicks - this.spinLoopTicks;
|
||||
if (elapsed % SPIN_HIT_INTERVAL == 0) {
|
||||
this.applySpinDamage();
|
||||
}
|
||||
this.spinAnimTicks--;
|
||||
if (this.spinAnimTicks <= 0) {
|
||||
this.spinCooldown = 160 + this.random.nextInt(80);
|
||||
|
||||
this.lookInDirection(this.spinDirection);
|
||||
this.spinLoopTicks--;
|
||||
if (this.spinLoopTicks <= 0) {
|
||||
this.beginSpinEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSpinEnd() {
|
||||
this.setAnimationState(ANIM_SPIN_END);
|
||||
this.setDeltaMovement(this.getDeltaMovement().multiply(0.7D, 1.0D, 0.7D));
|
||||
this.spinEndTicks--;
|
||||
if (this.spinEndTicks <= 0) {
|
||||
this.stopSpinSequence();
|
||||
this.spinCooldown = 340 + this.random.nextInt(80);
|
||||
this.spinHitCooldowns.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void fireWaterBomb() {
|
||||
private void updateSpinDirection(LivingEntity target) {
|
||||
Vec3 towardTarget = horizontalDirectionTo(target.position());
|
||||
if (towardTarget.lengthSqr() <= 1.0E-4D) {
|
||||
return;
|
||||
}
|
||||
if (this.distanceToSqr(target) > SPIN_LEASH_RANGE_SQR) {
|
||||
this.spinDirection = towardTarget;
|
||||
return;
|
||||
}
|
||||
Vec3 forward = this.spinDirection.lengthSqr() > 1.0E-4D ? this.spinDirection.normalize() : towardTarget;
|
||||
Vec3 combined = forward.scale(0.99D).add(towardTarget.scale(0.01D));
|
||||
this.spinDirection = combined.lengthSqr() > 1.0E-4D ? combined.normalize() : towardTarget;
|
||||
}
|
||||
|
||||
private boolean moveDuringSpin() {
|
||||
Vec3 direction = this.spinDirection.lengthSqr() > 1.0E-4D ? this.spinDirection.normalize() : Vec3.ZERO;
|
||||
if (direction == Vec3.ZERO) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.trySpinMoves(direction, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.spinStrafeSign *= -1;
|
||||
Vec3 bounced = rotateHorizontal(direction, this.spinStrafeSign * 35.0D);
|
||||
if (this.trySpinMoves(bounced, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vec3 reversed = direction.scale(-1.0D);
|
||||
if (this.trySpinMoves(reversed, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Vec3 reversedBounce = rotateHorizontal(reversed, this.spinStrafeSign * 25.0D);
|
||||
return this.trySpinMoves(reversedBounce, true);
|
||||
}
|
||||
|
||||
private boolean trySpinMoves(Vec3 baseDirection, boolean bounced) {
|
||||
Vec3[] attempts = new Vec3[]{
|
||||
baseDirection,
|
||||
rotateHorizontal(baseDirection, 28.0D * this.spinStrafeSign),
|
||||
rotateHorizontal(baseDirection, -28.0D * this.spinStrafeSign),
|
||||
rotateHorizontal(baseDirection, 52.0D * this.spinStrafeSign),
|
||||
rotateHorizontal(baseDirection, -52.0D * this.spinStrafeSign)
|
||||
};
|
||||
for (Vec3 attempt : attempts) {
|
||||
if (this.trySpinMove(attempt, 0.0D, 0.0D) || this.trySpinMove(attempt, 1.0D, 0.42D) || this.trySpinMove(attempt, -1.0D, -0.28D)) {
|
||||
this.spinDirection = attempt.normalize();
|
||||
if (bounced) {
|
||||
this.spinStrafeSign *= -1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean trySpinMove(Vec3 direction, double yOffset, double verticalVelocity) {
|
||||
Vec3 horizontalMove = new Vec3(direction.x * SPIN_MOVE_SPEED, 0.0D, direction.z * SPIN_MOVE_SPEED);
|
||||
AABB movedBox = this.getBoundingBox().move(horizontalMove.x, yOffset, horizontalMove.z);
|
||||
if (!this.level().noCollision(this, movedBox)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.hasSpinSupport(movedBox)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.setDeltaMovement(horizontalMove.x, verticalVelocity, horizontalMove.z);
|
||||
this.hasImpulse = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasSpinSupport(AABB box) {
|
||||
double probeY = box.minY - 0.2D;
|
||||
double[][] probes = new double[][]{
|
||||
{box.getCenter().x, box.getCenter().z},
|
||||
{box.minX + 0.25D, box.minZ + 0.25D},
|
||||
{box.minX + 0.25D, box.maxZ - 0.25D},
|
||||
{box.maxX - 0.25D, box.minZ + 0.25D},
|
||||
{box.maxX - 0.25D, box.maxZ - 0.25D}
|
||||
};
|
||||
|
||||
for (double[] probe : probes) {
|
||||
BlockPos supportPos = BlockPos.containing(probe[0], probeY, probe[1]);
|
||||
if (this.level().loadedAndEntityCanStandOnFace(supportPos, this, Direction.UP)) {
|
||||
return true;
|
||||
}
|
||||
BlockPos lowerSupportPos = supportPos.below();
|
||||
if (this.level().loadedAndEntityCanStandOnFace(lowerSupportPos, this, Direction.UP)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Vec3 horizontalDirectionTo(Vec3 targetPos) {
|
||||
Vec3 diff = targetPos.subtract(this.position());
|
||||
Vec3 horizontal = new Vec3(diff.x, 0.0D, diff.z);
|
||||
return horizontal.lengthSqr() > 1.0E-4D ? horizontal.normalize() : Vec3.ZERO;
|
||||
}
|
||||
|
||||
private void lookInDirection(Vec3 direction) {
|
||||
if (direction.lengthSqr() <= 1.0E-4D) {
|
||||
return;
|
||||
}
|
||||
float yaw = (float) (net.minecraft.util.Mth.atan2(direction.z, direction.x) * (180.0D / Math.PI)) - 90.0F;
|
||||
this.setYRot(yaw);
|
||||
this.yBodyRot = yaw;
|
||||
this.yHeadRot = yaw;
|
||||
this.setYHeadRot(yaw);
|
||||
}
|
||||
|
||||
private static Vec3 rotateHorizontal(Vec3 direction, double degrees) {
|
||||
if (direction.lengthSqr() <= 1.0E-4D) {
|
||||
return Vec3.ZERO;
|
||||
}
|
||||
double radians = Math.toRadians(degrees);
|
||||
double cos = Math.cos(radians);
|
||||
double sin = Math.sin(radians);
|
||||
double x = direction.x * cos - direction.z * sin;
|
||||
double z = direction.x * sin + direction.z * cos;
|
||||
return new Vec3(x, 0.0D, z).normalize();
|
||||
}
|
||||
|
||||
private void beginSpinEnd() {
|
||||
this.spinStartTicks = 0;
|
||||
this.spinLoopTicks = 0;
|
||||
if (this.spinEndTicks <= 0) {
|
||||
this.spinEndTicks = SPIN_END_TOTAL_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopSpinSequence() {
|
||||
this.spinStartTicks = 0;
|
||||
this.spinLoopTicks = 0;
|
||||
this.spinEndTicks = 0;
|
||||
this.spinLoopTotalTicks = 0;
|
||||
this.spinDirection = Vec3.ZERO;
|
||||
this.spinStrafeSign = 1;
|
||||
this.spinStuckTicks = 0;
|
||||
this.spinProgressAnchor = Vec3.ZERO;
|
||||
this.spinProgressTicks = 0;
|
||||
}
|
||||
|
||||
private Vec3 calcLaunchVelocity(Vec3 from, Vec3 to, double minSpeed) {
|
||||
Vec3 diff = to.subtract(from);
|
||||
double dh = Math.sqrt(diff.x * diff.x + diff.z * diff.z);
|
||||
double dy = diff.y;
|
||||
double gravity = AntarchySettings.waterBombGravity();
|
||||
// Choose travel time based on horizontal distance at minSpeed, minimum 8 ticks
|
||||
double t = Math.max(8.0, dh / minSpeed);
|
||||
double vx = diff.x / t;
|
||||
double vz = diff.z / t;
|
||||
// Compensate for gravity accumulating over t ticks
|
||||
double vy = dy / t + 0.5 * gravity * t;
|
||||
return new Vec3(vx, vy, vz);
|
||||
}
|
||||
|
||||
private void fireRapidBomb() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null) return;
|
||||
this.playSound(AntarchySoundEvents.SQUIDZOOKA_FIRE.get(), 0.8F, 0.9F + this.random.nextFloat() * 0.2F);
|
||||
Vec3 from = new Vec3(this.getX(), this.getEyeY(), this.getZ());
|
||||
Vec3 to = new Vec3(target.getX(), target.getEyeY(), target.getZ());
|
||||
Vec3 baseVel = calcLaunchVelocity(from, to, 1.5);
|
||||
int count = this.isPhase2() ? 2 : 1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
double spread = count > 1 ? (i - 0.5) * 0.15 : 0.0;
|
||||
Vec3 vel = new Vec3(baseVel.x + spread, baseVel.y, baseVel.z - spread);
|
||||
WaterBombEntity bomb = new WaterBombEntity(this.level(), this);
|
||||
bomb.moveTo(this.getX(), this.getEyeY(), this.getZ(), this.getYRot(), 0);
|
||||
bomb.shootFromRotation(this, this.getXRot(), this.getYRot(), 0.0F, 1.4F, 1.5F);
|
||||
bomb.setPos(from.x, from.y, from.z);
|
||||
bomb.setDeltaMovement(vel);
|
||||
bomb.hasImpulse = true;
|
||||
serverLevel.addFreshEntity(bomb);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireBomberBurst() {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) return;
|
||||
LivingEntity target = this.getTarget();
|
||||
if (target == null) return;
|
||||
this.playSound(AntarchySoundEvents.TORETERROR_BOMBER_FIRE.get(), 1.1F, 0.9F + this.random.nextFloat() * 0.15F);
|
||||
int count = 3;
|
||||
for (int i = 0; i < count; i++) {
|
||||
BomberEntity bomber = AntarchyObjects.BOMBER.get().create(this.level());
|
||||
@@ -371,6 +748,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
bomber.setDeltaMovement(vx, vy, vz);
|
||||
bomber.setTarget(target);
|
||||
serverLevel.addFreshEntity(bomber);
|
||||
bomber.primeFuse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +758,7 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
List<LivingEntity> victims = this.level().getEntitiesOfClass(LivingEntity.class, shockArea, this::canTargetEntity);
|
||||
float damage = (float) AntarchySettings.toreterrorJumpAttackDamage();
|
||||
for (LivingEntity victim : victims) {
|
||||
victim.hurt(this.damageSources().mobAttack(this), damage);
|
||||
victim.hurt(AntarchyDamageSources.toreterrorJump(serverLevel, this), damage);
|
||||
Vec3 knockDir = victim.position().subtract(this.position()).normalize().add(0, 0.4D, 0);
|
||||
double knockback = AntarchySettings.toreterrorJumpAttackKnockback();
|
||||
victim.push(knockDir.x * knockback, 0.5D, knockDir.z * knockback);
|
||||
@@ -393,14 +771,14 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
|
||||
private void applySpinDamage() {
|
||||
if (!(this.level() instanceof ServerLevel)) return;
|
||||
AABB spinArea = this.getBoundingBox().inflate(SPIN_RADIUS, 2.0D, SPIN_RADIUS);
|
||||
AABB spinArea = this.getBoundingBox().inflate(SPIN_RADIUS, SPIN_RADIUS, SPIN_RADIUS);
|
||||
List<LivingEntity> victims = this.level().getEntitiesOfClass(LivingEntity.class, spinArea, this::canTargetEntity);
|
||||
float damage = (float) AntarchySettings.toreterrorSpinDamage();
|
||||
double knockback = AntarchySettings.toreterrorSpinKnockback();
|
||||
for (LivingEntity victim : victims) {
|
||||
UUID id = victim.getUUID();
|
||||
if (this.spinHitCooldowns.getOrDefault(id, 0) > 0) continue;
|
||||
if (victim.hurt(this.damageSources().mobAttack(this), damage)) {
|
||||
if (victim.hurt(AntarchyDamageSources.toreterrorSpin((ServerLevel) this.level(), this), damage)) {
|
||||
this.spinHitCooldowns.put(id, 20);
|
||||
Vec3 knockDir = victim.position().subtract(this.position()).normalize();
|
||||
victim.push(knockDir.x * knockback, 0.35D, knockDir.z * knockback);
|
||||
@@ -428,16 +806,19 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
if (target == null || ToreterrorEntity.this.isInAttackAnimation()) return false;
|
||||
if (ToreterrorEntity.this.shootCooldown > 0) return false;
|
||||
double distSqr = ToreterrorEntity.this.distanceToSqr(target);
|
||||
return distSqr >= RANGED_MIN_RANGE_SQR && distSqr <= RANGED_MAX_RANGE_SQR
|
||||
return distSqr > CLOSE_RANGE_SQR && distSqr <= RANGED_MAX_RANGE_SQR
|
||||
&& ToreterrorEntity.this.hasLineOfSight(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ToreterrorEntity.this.shootAnimTicks = SHOOT_TOTAL_TICKS;
|
||||
ToreterrorEntity.this.shootUseWaterBombs = ToreterrorEntity.this.random.nextFloat() < (float) AntarchySettings.toreterrorRangedWaterBombChance();
|
||||
ToreterrorEntity.this.shootNextFireElapsed = SHOOT_FIRE_START_TICK;
|
||||
ToreterrorEntity.this.shootBomberFired = false;
|
||||
ToreterrorEntity.this.shootBomberFiredSecond = false;
|
||||
ToreterrorEntity.this.rapidFireNextTick = 0;
|
||||
float roll = ToreterrorEntity.this.random.nextFloat();
|
||||
if (roll < 0.5F) ToreterrorEntity.this.shootAttackType = ATTACK_RAPID_FIRE;
|
||||
else ToreterrorEntity.this.shootAttackType = ATTACK_BOMBERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -462,14 +843,16 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
if (target == null || ToreterrorEntity.this.isInAttackAnimation()) return false;
|
||||
if (ToreterrorEntity.this.jumpCooldown > 0) return false;
|
||||
double distSqr = ToreterrorEntity.this.distanceToSqr(target);
|
||||
return distSqr <= RANGED_MAX_RANGE_SQR && ToreterrorEntity.this.onGround();
|
||||
return distSqr <= JUMP_RANGE_SQR && ToreterrorEntity.this.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ToreterrorEntity.this.jumpAnimTicks = JUMP_TOTAL_TICKS;
|
||||
ToreterrorEntity.this.jumpLaunched = false;
|
||||
ToreterrorEntity.this.jumpAirborne = false;
|
||||
ToreterrorEntity.this.shockwaveApplied = false;
|
||||
ToreterrorEntity.this.playSound(AntarchySoundEvents.TORETERROR_JUMP_UP.get(), 1.2F, 0.9F + ToreterrorEntity.this.random.nextFloat() * 0.15F);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -483,6 +866,28 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private final class ToreterrorFloatGoal extends Goal {
|
||||
ToreterrorFloatGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.JUMP));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return ToreterrorEntity.this.isInWater() && !ToreterrorEntity.this.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return canUse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
Vec3 vel = ToreterrorEntity.this.getDeltaMovement();
|
||||
ToreterrorEntity.this.setDeltaMovement(vel.x, Math.min(vel.y + 0.08D, 0.2D), vel.z);
|
||||
}
|
||||
}
|
||||
|
||||
private final class ToreterrorSpinGoal extends Goal {
|
||||
ToreterrorSpinGoal() {
|
||||
this.setFlags(EnumSet.of(Flag.MOVE));
|
||||
@@ -493,22 +898,34 @@ public class ToreterrorEntity extends Monster implements GeoEntity {
|
||||
LivingEntity target = ToreterrorEntity.this.getTarget();
|
||||
if (target == null || ToreterrorEntity.this.isInAttackAnimation()) return false;
|
||||
if (ToreterrorEntity.this.spinCooldown > 0) return false;
|
||||
return ToreterrorEntity.this.distanceToSqr(target) <= MELEE_RANGE_SQR && ToreterrorEntity.this.onGround();
|
||||
return ToreterrorEntity.this.distanceToSqr(target) <= SPIN_TRIGGER_RANGE_SQR && ToreterrorEntity.this.onGround();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
ToreterrorEntity.this.spinAnimTicks = SPIN_TOTAL_TICKS;
|
||||
LivingEntity target = ToreterrorEntity.this.getTarget();
|
||||
ToreterrorEntity.this.spinStartTicks = SPIN_START_TOTAL_TICKS;
|
||||
ToreterrorEntity.this.spinLoopTotalTicks = SPIN_LOOP_MIN_TICKS + ToreterrorEntity.this.random.nextInt(SPIN_LOOP_MAX_TICKS - SPIN_LOOP_MIN_TICKS + 1);
|
||||
ToreterrorEntity.this.spinLoopTicks = ToreterrorEntity.this.spinLoopTotalTicks;
|
||||
ToreterrorEntity.this.spinEndTicks = 0;
|
||||
ToreterrorEntity.this.spinStuckTicks = 0;
|
||||
ToreterrorEntity.this.spinProgressAnchor = ToreterrorEntity.this.position();
|
||||
ToreterrorEntity.this.spinProgressTicks = 0;
|
||||
ToreterrorEntity.this.spinStrafeSign = ToreterrorEntity.this.random.nextBoolean() ? 1 : -1;
|
||||
ToreterrorEntity.this.spinHitCooldowns.clear();
|
||||
ToreterrorEntity.this.spinDirection = target == null
|
||||
? Vec3.ZERO
|
||||
: ToreterrorEntity.this.horizontalDirectionTo(target.position());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return ToreterrorEntity.this.spinAnimTicks > 0;
|
||||
return ToreterrorEntity.this.isSpinSequenceActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ToreterrorEntity.this.spinAnimTicks = 0;
|
||||
ToreterrorEntity.this.stopSpinSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.craisinlord.antarchy.content.entity;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -11,6 +12,8 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.projectile.ThrowableProjectile;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BaseFireBlock;
|
||||
import net.minecraft.world.level.block.CampfireBlock;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LiquidBlock;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
@@ -18,6 +21,8 @@ import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -26,6 +31,8 @@ import java.util.UUID;
|
||||
|
||||
public class WaterBombEntity extends ThrowableProjectile {
|
||||
|
||||
private static final EntityDataAccessor<Boolean> HUGE = SynchedEntityData.defineId(WaterBombEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
|
||||
private int lifetimeTicks;
|
||||
private final Map<UUID, Integer> hitCooldowns = new HashMap<>();
|
||||
|
||||
@@ -39,8 +46,18 @@ public class WaterBombEntity extends ThrowableProjectile {
|
||||
this.lifetimeTicks = AntarchySettings.waterBombLifetimeTicks();
|
||||
}
|
||||
|
||||
public WaterBombEntity(Level level, LivingEntity owner, boolean huge) {
|
||||
this(level, owner);
|
||||
this.entityData.set(HUGE, huge);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void defineSynchedData(SynchedEntityData.Builder builder) {
|
||||
builder.define(HUGE, false);
|
||||
}
|
||||
|
||||
public boolean isHuge() {
|
||||
return this.entityData.get(HUGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,24 +92,41 @@ public class WaterBombEntity extends ThrowableProjectile {
|
||||
UUID id = entity.getUUID();
|
||||
if (this.hitCooldowns.getOrDefault(id, 0) > 0) return;
|
||||
|
||||
float damage = (float) AntarchySettings.waterBombDamage();
|
||||
if (living.hurt(this.damageSources().thrown(this, this.getOwner()), damage)) {
|
||||
float damage = (float) AntarchySettings.waterBombDamage() * (this.isHuge() ? 3.0F : 1.0F);
|
||||
if (living.hurt(AntarchyDamageSources.waterSoaked(this.level(), this, this.getOwner()), damage)) {
|
||||
this.hitCooldowns.put(id, 15);
|
||||
Vec3 knockDir = living.position().subtract(this.position()).normalize();
|
||||
double knockback = AntarchySettings.waterBombKnockback();
|
||||
living.push(knockDir.x * knockback, 0.3D, knockDir.z * knockback);
|
||||
living.hurtMarked = true;
|
||||
}
|
||||
living.clearFire();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHitBlock(BlockHitResult result) {
|
||||
BlockPos hitPos = result.getBlockPos().relative(result.getDirection());
|
||||
BlockPos blockPos = result.getBlockPos();
|
||||
BlockPos hitPos = blockPos.relative(result.getDirection());
|
||||
Level level = this.level();
|
||||
if (level.isEmptyBlock(hitPos) && level.getBlockState(hitPos.below()).isSolid()) {
|
||||
level.setBlock(hitPos, Blocks.WATER.defaultBlockState().setValue(LiquidBlock.LEVEL, 2), 3);
|
||||
if (level.getBlockState(blockPos).getBlock() instanceof CampfireBlock campfire && level.getBlockState(blockPos).getValue(CampfireBlock.LIT)) {
|
||||
level.setBlock(blockPos, level.getBlockState(blockPos).setValue(CampfireBlock.LIT, false), 3);
|
||||
this.discard();
|
||||
return;
|
||||
}
|
||||
if (this.isHuge()) {
|
||||
for (BlockPos pos : BlockPos.betweenClosed(hitPos.offset(-1, 0, -1), hitPos.offset(1, 0, 1))) {
|
||||
if (level.getBlockState(pos).getBlock() instanceof BaseFireBlock) {
|
||||
level.removeBlock(pos, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (level.getBlockState(hitPos).getBlock() instanceof BaseFireBlock) {
|
||||
level.removeBlock(hitPos, false);
|
||||
} else if (level.isEmptyBlock(hitPos) && level.getBlockState(hitPos.below()).isSolid()) {
|
||||
level.setBlock(hitPos, Blocks.WATER.defaultBlockState().setValue(LiquidBlock.LEVEL, 1), 3);
|
||||
level.scheduleTick(hitPos, Fluids.WATER, 2);
|
||||
}
|
||||
}
|
||||
this.discard();
|
||||
}
|
||||
|
||||
@@ -100,11 +134,13 @@ public class WaterBombEntity extends ThrowableProjectile {
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putInt("LifetimeTicks", this.lifetimeTicks);
|
||||
tag.putBoolean("Huge", this.isHuge());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readAdditionalSaveData(CompoundTag tag) {
|
||||
super.readAdditionalSaveData(tag);
|
||||
this.lifetimeTicks = tag.contains("LifetimeTicks") ? tag.getInt("LifetimeTicks") : AntarchySettings.waterBombLifetimeTicks();
|
||||
if (tag.contains("Huge")) this.entityData.set(HUGE, tag.getBoolean("Huge"));
|
||||
}
|
||||
}
|
||||
+127
-5
@@ -1,29 +1,103 @@
|
||||
package com.craisinlord.antarchy.content.entity.ant;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.PlayerRespawnLogic;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.OwnableEntity;
|
||||
import net.minecraft.world.entity.TamableAnimal;
|
||||
import net.minecraft.world.entity.animal.horse.AbstractHorse;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.vehicle.DismountHelper;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import net.minecraft.world.level.portal.DimensionTransition;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class AntTeleportHelper {
|
||||
private static final double COMPANION_FOLLOW_RADIUS = 16.0D;
|
||||
|
||||
private AntTeleportHelper() {
|
||||
}
|
||||
|
||||
public static void teleportPlayerWithCompanions(ServerPlayer player, ServerLevel destination, Vec3 destinationPos) {
|
||||
Mob vehicle = player.getVehicle() instanceof Mob mob ? mob : null;
|
||||
List<Mob> companions = collectCompanions(player);
|
||||
player.teleportTo(destination, destinationPos.x, destinationPos.y, destinationPos.z, player.getYRot(), player.getXRot());
|
||||
Mob movedVehicle = null;
|
||||
for (Mob companion : companions) {
|
||||
Mob moved = moveCompanion(companion, destination, destinationPos);
|
||||
if (companion == vehicle) {
|
||||
movedVehicle = moved;
|
||||
}
|
||||
}
|
||||
|
||||
if (movedVehicle != null) {
|
||||
player.startRiding(movedVehicle, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Mob> collectCompanions(ServerPlayer player) {
|
||||
List<Mob> companions = new ArrayList<>();
|
||||
if (player.getVehicle() instanceof Mob vehicle) {
|
||||
companions.add(vehicle);
|
||||
}
|
||||
|
||||
for (Mob mob : player.serverLevel().getEntitiesOfClass(Mob.class, player.getBoundingBox().inflate(COMPANION_FOLLOW_RADIUS), mob -> isFollowingPet(mob, player))) {
|
||||
if (!companions.contains(mob)) {
|
||||
companions.add(mob);
|
||||
}
|
||||
}
|
||||
|
||||
return companions;
|
||||
}
|
||||
|
||||
private static boolean isFollowingPet(Mob mob, ServerPlayer player) {
|
||||
if (!mob.isAlive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mob instanceof TamableAnimal tamable) {
|
||||
return tamable.isTame() && player.getUUID().equals(tamable.getOwnerUUID()) && !tamable.isOrderedToSit();
|
||||
}
|
||||
|
||||
if (mob instanceof AbstractHorse horse) {
|
||||
return horse.isTamed() && player.getUUID().equals(horse.getOwnerUUID());
|
||||
}
|
||||
|
||||
return mob instanceof OwnableEntity ownable && player.getUUID().equals(ownable.getOwnerUUID());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Mob moveCompanion(Mob companion, ServerLevel destination, Vec3 destinationPos) {
|
||||
companion.stopRiding();
|
||||
companion.ejectPassengers();
|
||||
companion.getNavigation().stop();
|
||||
if (companion.level() == destination) {
|
||||
companion.teleportTo(destinationPos.x, destinationPos.y, destinationPos.z);
|
||||
return companion;
|
||||
}
|
||||
|
||||
Entity moved = companion.changeDimension(new DimensionTransition(destination, destinationPos, Vec3.ZERO, companion.getYRot(), companion.getXRot(), DimensionTransition.DO_NOTHING));
|
||||
return moved instanceof Mob mob ? mob : null;
|
||||
}
|
||||
|
||||
static InteractionResult handleInteraction(BaseAntEntity ant, Player player, ItemStack itemStack) {
|
||||
if (ant.requiresActivationReagent() && !ant.isTeleportActivatedState()) {
|
||||
if (!(player instanceof ServerPlayer serverPlayer)) {
|
||||
@@ -59,7 +133,7 @@ public final class AntTeleportHelper {
|
||||
}
|
||||
|
||||
Vec3 destinationPos = getDestinationPosition(serverPlayer, destination);
|
||||
serverPlayer.teleportTo(destination, destinationPos.x, destinationPos.y, destinationPos.z, serverPlayer.getYRot(), serverPlayer.getXRot());
|
||||
teleportPlayerWithCompanions(serverPlayer, destination, destinationPos);
|
||||
serverPlayer.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
|
||||
return InteractionResult.CONSUME;
|
||||
}
|
||||
@@ -87,7 +161,7 @@ public final class AntTeleportHelper {
|
||||
return destination;
|
||||
}
|
||||
|
||||
// If the player's respawn is not in the overworld, return them to overworld spawn instead.
|
||||
// If the player's respawn is not in the overworld, return them to overworld spawn instead
|
||||
return player.server.overworld();
|
||||
}
|
||||
|
||||
@@ -111,6 +185,11 @@ public final class AntTeleportHelper {
|
||||
|
||||
@Nullable
|
||||
private static Vec3 findSafeArrivalPosition(ServerPlayer player, ServerLevel destination, BlockPos preferredPos) {
|
||||
int[] yRange = getDimensionYRange(destination);
|
||||
if (yRange != null) {
|
||||
return findSafeArrivalPositionInYRange(player, destination, preferredPos, yRange[0], yRange[1]);
|
||||
}
|
||||
|
||||
Set<BlockPos> candidates = new LinkedHashSet<>();
|
||||
BlockPos adjustedPreferredPos = player.adjustSpawnLocation(destination, preferredPos);
|
||||
addArrivalCandidate(candidates, preferredPos);
|
||||
@@ -146,6 +225,40 @@ public final class AntTeleportHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static int[] getDimensionYRange(ServerLevel destination) {
|
||||
ResourceKey<Level> dim = destination.dimension();
|
||||
if (dim == AntarchySettings.termiteDestinationDimension()) {
|
||||
return new int[]{100, 200};
|
||||
}
|
||||
if (dim == AntarchySettings.brownAntDestinationDimension()) {
|
||||
return new int[]{75, 120};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Vec3 findSafeArrivalPositionInYRange(ServerPlayer player, ServerLevel destination, BlockPos preferredPos, int minY, int maxY) {
|
||||
for (int radius = 0; radius <= 8; radius++) {
|
||||
for (int xOff = -radius; xOff <= radius; xOff++) {
|
||||
for (int zOff = -radius; zOff <= radius; zOff++) {
|
||||
if (radius > 0 && Math.abs(xOff) != radius && Math.abs(zOff) != radius) {
|
||||
continue;
|
||||
}
|
||||
int x = preferredPos.getX() + xOff;
|
||||
int z = preferredPos.getZ() + zOff;
|
||||
for (int y = maxY; y >= minY + 1; y--) {
|
||||
Vec3 safePos = tryFindSafeDismount(player, destination, new BlockPos(x, y, z));
|
||||
if (safePos != null && safePos.y >= minY && safePos.y <= maxY) {
|
||||
return safePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addArrivalCandidate(Set<BlockPos> candidates, @Nullable BlockPos pos) {
|
||||
if (pos == null) {
|
||||
return;
|
||||
@@ -161,17 +274,26 @@ public final class AntTeleportHelper {
|
||||
@Nullable
|
||||
private static Vec3 tryFindSafeDismount(ServerPlayer player, ServerLevel destination, BlockPos candidate) {
|
||||
Vec3 safePos = DismountHelper.findSafeDismountLocation(player.getType(), destination, candidate, true);
|
||||
if (safePos != null) {
|
||||
if (safePos != null && isValidArrivalPosition(destination, safePos)) {
|
||||
return safePos;
|
||||
}
|
||||
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
safePos = DismountHelper.findSafeDismountLocation(player.getType(), destination, candidate.relative(direction), true);
|
||||
if (safePos != null) {
|
||||
if (safePos != null && isValidArrivalPosition(destination, safePos)) {
|
||||
return safePos;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isValidArrivalPosition(ServerLevel destination, Vec3 safePos) {
|
||||
if (destination.dimension() != AntarchySettings.termiteDestinationDimension()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockPos standPos = BlockPos.containing(safePos);
|
||||
return !destination.getBlockState(standPos.below()).is(Blocks.BEDROCK);
|
||||
}
|
||||
}
|
||||
@@ -1230,11 +1230,11 @@ public abstract class BaseAntEntity extends Animal implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean shouldPlayAttackAnimation() {
|
||||
if (!(this instanceof RedAntEntity)) {
|
||||
return false;
|
||||
return this.shouldUseBiteAnimation() && this.getBiteAnimationTicks() > 0;
|
||||
}
|
||||
|
||||
return this.getBiteAnimationTicks() > 0;
|
||||
protected boolean shouldUseBiteAnimation() {
|
||||
return this instanceof RedAntEntity;
|
||||
}
|
||||
|
||||
private int getBiteAnimationTicks() {
|
||||
@@ -1245,6 +1245,10 @@ public abstract class BaseAntEntity extends Animal implements GeoEntity {
|
||||
this.entityData.set(BITE_ANIM_TICKS, Math.max(0, ticks));
|
||||
}
|
||||
|
||||
protected final void triggerBiteAnimation(int ticks) {
|
||||
this.setBiteAnimationTicks(ticks);
|
||||
}
|
||||
|
||||
private void syncAttribute(@Nullable AttributeInstance attributeInstance, double desiredValue, boolean rescaleCurrentHealth) {
|
||||
if (attributeInstance == null || Math.abs(attributeInstance.getBaseValue() - desiredValue) < ATTRIBUTE_EPSILON) {
|
||||
return;
|
||||
|
||||
+67
-37
@@ -1,9 +1,11 @@
|
||||
package com.craisinlord.antarchy.content.entity.ant;
|
||||
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.AntarchyTags;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
@@ -17,10 +19,22 @@ import org.jetbrains.annotations.Nullable;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
|
||||
public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
private static final int WOOD_SEARCH_INTERVAL_TICKS = 10;
|
||||
private static final int WOOD_BITE_ANIMATION_TICKS = 8;
|
||||
private static final int WOOD_SEARCH_INTERVAL_TICKS = 20;
|
||||
private static final int WOOD_REPATH_INTERVAL_TICKS = 6;
|
||||
private static final int WOOD_SEARCH_RADIUS_HORIZONTAL = 12;
|
||||
private static final int WOOD_SEARCH_RADIUS_VERTICAL = 4;
|
||||
private static final Vec3i[] IMMEDIATE_WOOD_OFFSETS = {
|
||||
new Vec3i(0, 1, 0),
|
||||
new Vec3i(0, 0, -1),
|
||||
new Vec3i(0, 0, 1),
|
||||
new Vec3i(1, 0, 0),
|
||||
new Vec3i(-1, 0, 0),
|
||||
new Vec3i(0, 1, -1),
|
||||
new Vec3i(0, 1, 1),
|
||||
new Vec3i(1, 1, 0),
|
||||
new Vec3i(-1, 1, 0),
|
||||
};
|
||||
@Nullable
|
||||
private BlockPos targetWoodPos;
|
||||
private int nextWoodSearchTick;
|
||||
@@ -79,13 +93,8 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canGroupWithNestmates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canMarch() {
|
||||
return false;
|
||||
protected boolean shouldUseBiteAnimation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,37 +128,25 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean tryEatNearbyWood() {
|
||||
for (BlockPos targetPos : this.getImmediateWoodChecks()) {
|
||||
BlockPos basePos = this.blockPosition();
|
||||
for (Vec3i offset : IMMEDIATE_WOOD_OFFSETS) {
|
||||
BlockPos targetPos = basePos.offset(offset);
|
||||
BlockState targetState = this.level().getBlockState(targetPos);
|
||||
if (!targetState.is(AntarchyTags.Blocks.TERMITE_FOODS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SoundType soundType = targetState.getSoundType();
|
||||
this.triggerBiteAnimation(WOOD_BITE_ANIMATION_TICKS);
|
||||
this.level().destroyBlock(targetPos, false, this);
|
||||
this.level().playSound(null, targetPos, soundType.getBreakSound(), this.getSoundSource(), 0.7F, 1.1F);
|
||||
this.playSound(AntarchySoundEvents.ANT_BITE.get(), 0.35F, 0.95F + this.random.nextFloat() * 0.1F);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private BlockPos[] getImmediateWoodChecks() {
|
||||
BlockPos basePos = this.blockPosition();
|
||||
BlockPos abovePos = basePos.above();
|
||||
return new BlockPos[] {
|
||||
basePos.above(),
|
||||
basePos.north(),
|
||||
basePos.south(),
|
||||
basePos.east(),
|
||||
basePos.west(),
|
||||
abovePos.north(),
|
||||
abovePos.south(),
|
||||
abovePos.east(),
|
||||
abovePos.west()
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos getCurrentWoodTarget() {
|
||||
if (this.targetWoodPos != null && this.isValidWoodTarget(this.targetWoodPos)) {
|
||||
@@ -168,20 +165,52 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
@Nullable
|
||||
private BlockPos findClosestWoodTarget() {
|
||||
BlockPos origin = this.blockPosition();
|
||||
int ox = origin.getX();
|
||||
int oy = origin.getY();
|
||||
int oz = origin.getZ();
|
||||
BlockPos bestPos = null;
|
||||
double bestDistance = Double.MAX_VALUE;
|
||||
double bestDistSq = Double.MAX_VALUE;
|
||||
BlockPos.MutableBlockPos candidate = new BlockPos.MutableBlockPos();
|
||||
|
||||
for (BlockPos pos : BlockPos.betweenClosed(
|
||||
origin.offset(-WOOD_SEARCH_RADIUS_HORIZONTAL, -WOOD_SEARCH_RADIUS_VERTICAL, -WOOD_SEARCH_RADIUS_HORIZONTAL),
|
||||
origin.offset(WOOD_SEARCH_RADIUS_HORIZONTAL, WOOD_SEARCH_RADIUS_VERTICAL + 2, WOOD_SEARCH_RADIUS_HORIZONTAL))) {
|
||||
if (!this.isValidWoodTarget(pos)) {
|
||||
for (int r = 0; r <= WOOD_SEARCH_RADIUS_HORIZONTAL; r++) {
|
||||
if (bestPos != null && bestDistSq <= (double)(r * r)) {
|
||||
break;
|
||||
}
|
||||
for (int dy = -WOOD_SEARCH_RADIUS_VERTICAL; dy <= WOOD_SEARCH_RADIUS_VERTICAL + 2; dy++) {
|
||||
if (r == 0) {
|
||||
candidate.set(ox, oy + dy, oz);
|
||||
if (this.isValidWoodTarget(candidate)) {
|
||||
double d = candidate.distSqr(origin);
|
||||
if (d < bestDistSq) { bestDistSq = d; bestPos = candidate.immutable(); }
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
double distance = pos.distSqr(origin);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestPos = pos.immutable();
|
||||
// North/South faces (dz = ±r)
|
||||
for (int dx = -r; dx <= r; dx++) {
|
||||
candidate.set(ox + dx, oy + dy, oz - r);
|
||||
if (this.isValidWoodTarget(candidate)) {
|
||||
double d = candidate.distSqr(origin);
|
||||
if (d < bestDistSq) { bestDistSq = d; bestPos = candidate.immutable(); }
|
||||
}
|
||||
candidate.set(ox + dx, oy + dy, oz + r);
|
||||
if (this.isValidWoodTarget(candidate)) {
|
||||
double d = candidate.distSqr(origin);
|
||||
if (d < bestDistSq) { bestDistSq = d; bestPos = candidate.immutable(); }
|
||||
}
|
||||
}
|
||||
// East/West faces (dx = ±r, corners already covered above)
|
||||
for (int dz = -r + 1; dz <= r - 1; dz++) {
|
||||
candidate.set(ox - r, oy + dy, oz + dz);
|
||||
if (this.isValidWoodTarget(candidate)) {
|
||||
double d = candidate.distSqr(origin);
|
||||
if (d < bestDistSq) { bestDistSq = d; bestPos = candidate.immutable(); }
|
||||
}
|
||||
candidate.set(ox + r, oy + dy, oz + dz);
|
||||
if (this.isValidWoodTarget(candidate)) {
|
||||
double d = candidate.distSqr(origin);
|
||||
if (d < bestDistSq) { bestDistSq = d; bestPos = candidate.immutable(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +244,10 @@ public class TermiteEntity extends BaseAntEntity implements GeoEntity {
|
||||
}
|
||||
|
||||
private boolean isWalkableStandPos(BlockPos pos) {
|
||||
BlockState floorState = this.level().getBlockState(pos.below());
|
||||
BlockPos floor = pos.below();
|
||||
BlockState floorState = this.level().getBlockState(floor);
|
||||
return this.level().getBlockState(pos).isAir()
|
||||
&& this.level().getBlockState(pos.above()).isAir()
|
||||
&& floorState.isFaceSturdy(this.level(), pos.below(), Direction.UP);
|
||||
&& floorState.isFaceSturdy(this.level(), floor, Direction.UP);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -604,7 +604,7 @@ public class BasiliskEntity extends Monster implements GeoEntity {
|
||||
BasiliskEntity.this.getLookControl().setLookAt(target, 30.0F, 30.0F);
|
||||
}
|
||||
|
||||
// super handles path recalc and cooldown timer — always run it
|
||||
// super handles path recalc and cooldown timer
|
||||
super.tick();
|
||||
|
||||
if (BasiliskEntity.this.hissChargeTimer > 0 || BasiliskEntity.this.attackAnimTicks > 0) {
|
||||
|
||||
+16
-1
@@ -83,6 +83,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
private static final int SWIPE_TICKS = 18;
|
||||
private static final float SWIPE_KNOCKBACK = 2.8F;
|
||||
private static final int COCOON_HIT_THRESHOLD = 3;
|
||||
private static final int COCOON_HIT_COOLDOWN_TICKS = 60;
|
||||
private static final int COCOON_HIT_ANIM_TICKS = 30;
|
||||
private static final int COCOON_HATCH_ANIM_TICKS = 40;
|
||||
private static final int COCOON_IDLE_ANIM_TICKS = 70;
|
||||
@@ -111,6 +112,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
@Nullable
|
||||
BlockPos anchorPos;
|
||||
private int cocoonHits;
|
||||
private int cocoonHitCooldownTicks;
|
||||
private boolean isHatching;
|
||||
private int cocoonAnimTicks;
|
||||
private boolean pendingHatchAnim;
|
||||
@@ -261,6 +263,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private void tickCocoon() {
|
||||
if (this.cocoonHitCooldownTicks > 0) {
|
||||
this.cocoonHitCooldownTicks--;
|
||||
}
|
||||
|
||||
// Pin position to hang from anchor
|
||||
if (this.anchorPos != null) {
|
||||
this.setPos(this.anchorPos.getX() + 0.5, this.anchorPos.getY() - 1.5, this.anchorPos.getZ() + 0.5);
|
||||
@@ -319,6 +325,8 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
this.entityData.set(IS_COCOONED, false);
|
||||
this.isHatching = false;
|
||||
this.anchorPos = null;
|
||||
this.cocoonHits = 0;
|
||||
this.cocoonHitCooldownTicks = 0;
|
||||
this.refreshDimensions();
|
||||
this.setAnimationState(ANIM_FLY);
|
||||
Player nearbyPlayer = this.level().getNearestPlayer(this, 48.0D);
|
||||
@@ -344,6 +352,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
this.anchorPos = anchor;
|
||||
this.refreshDimensions();
|
||||
if (cocooned) {
|
||||
this.cocoonHits = 0;
|
||||
this.cocoonHitCooldownTicks = 0;
|
||||
this.isHatching = false;
|
||||
this.pendingHatchAnim = false;
|
||||
this.setAnimationState(ANIM_COCOON);
|
||||
}
|
||||
}
|
||||
@@ -355,9 +367,10 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
if (this.isCocooned() && !this.isHatching) {
|
||||
if (this.level().isClientSide || this.hurtTime > 0) return false;
|
||||
if (this.level().isClientSide || this.cocoonHitCooldownTicks > 0) return false;
|
||||
this.hurtTime = this.hurtDuration;
|
||||
this.cocoonHits++;
|
||||
this.cocoonHitCooldownTicks = COCOON_HIT_COOLDOWN_TICKS;
|
||||
if (this.cocoonHits >= COCOON_HIT_THRESHOLD) {
|
||||
this.startHatching();
|
||||
} else {
|
||||
@@ -706,6 +719,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
super.addAdditionalSaveData(tag);
|
||||
tag.putBoolean("IsCocooned", this.isCocooned());
|
||||
tag.putInt("CocoonHits", this.cocoonHits);
|
||||
tag.putInt("CocoonHitCooldown", this.cocoonHitCooldownTicks);
|
||||
if (this.anchorPos != null) {
|
||||
tag.putInt("AnchorX", this.anchorPos.getX());
|
||||
tag.putInt("AnchorY", this.anchorPos.getY());
|
||||
@@ -723,6 +737,7 @@ public class BrutalflyEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
this.setCocooned(true, anchor);
|
||||
this.cocoonHits = tag.getInt("CocoonHits");
|
||||
this.cocoonHitCooldownTicks = tag.getInt("CocoonHitCooldown");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-24
@@ -55,6 +55,7 @@ import software.bernie.geckolib.animation.AnimationState;
|
||||
import software.bernie.geckolib.util.GeckoLibUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
@@ -71,6 +72,8 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Integer> TEXTURE_VARIANT =
|
||||
SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Optional<UUID>> SHOULDER_PLAYER_DATA =
|
||||
SynchedEntityData.defineId(FlyingSquirrelEntity.class, EntityDataSerializers.OPTIONAL_UUID);
|
||||
|
||||
private static final String PICKUP_ANIM_TICKS_KEY = "PickupAnimTicks";
|
||||
private static final String TEXTURE_VARIANT_KEY = "TextureVariant";
|
||||
@@ -227,6 +230,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
builder.define(CLIMBING, false);
|
||||
builder.define(PICKING_UP_NUT, false);
|
||||
builder.define(TEXTURE_VARIANT, TEXTURE_VARIANT_REGULAR);
|
||||
builder.define(SHOULDER_PLAYER_DATA, Optional.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -376,13 +380,19 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
if (this.isOnShoulder()) {
|
||||
if (!(this.getVehicle() instanceof Player shoulderPlayer)
|
||||
|| !shoulderPlayer.isAlive()) {
|
||||
Player shoulderPlayer = this.getShoulderPlayer();
|
||||
if (shoulderPlayer == null || !shoulderPlayer.isAlive()) {
|
||||
this.dismountShoulder();
|
||||
} else if (this.shoulderSneakGraceTicks <= 0 && shoulderPlayer.isShiftKeyDown()) {
|
||||
this.glideOffShoulder();
|
||||
} else {
|
||||
Vec3 shoulderPos = this.getShoulderRidePos(shoulderPlayer);
|
||||
this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.setYRot(shoulderPlayer.getYHeadRot());
|
||||
this.setXRot(shoulderPlayer.getXRot());
|
||||
this.yBodyRot = shoulderPlayer.getYHeadRot();
|
||||
this.yHeadRot = shoulderPlayer.getYHeadRot();
|
||||
this.fallDistance = 0.0F;
|
||||
}
|
||||
this.setNoGravity(true);
|
||||
@@ -434,22 +444,25 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
|
||||
this.tickIdleStallLogging(horizontalSpeed);
|
||||
}
|
||||
if (this.isOnShoulder() && this.level().isClientSide) {
|
||||
Player shoulderPlayer = this.getShoulderPlayer();
|
||||
if (shoulderPlayer != null) {
|
||||
Vec3 shoulderPos = this.getShoulderRidePos(shoulderPlayer);
|
||||
this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.setYRot(shoulderPlayer.getYHeadRot());
|
||||
this.setXRot(shoulderPlayer.getXRot());
|
||||
this.yBodyRot = shoulderPlayer.getYHeadRot();
|
||||
this.yHeadRot = shoulderPlayer.getYHeadRot();
|
||||
this.fallDistance = 0.0F;
|
||||
}
|
||||
}
|
||||
this.setNoGravity(this.isGliding() || this.isClimbingTrunk() || this.isClimbingToShoulder() || this.isOnShoulder());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rideTick() {
|
||||
super.rideTick();
|
||||
if (this.getVehicle() instanceof Player player) {
|
||||
Vec3 shoulderPos = this.getShoulderRidePos(player);
|
||||
this.setPos(shoulderPos.x, shoulderPos.y, shoulderPos.z);
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.setYRot(player.getYHeadRot());
|
||||
this.setXRot(player.getXRot());
|
||||
this.yBodyRot = player.getYHeadRot();
|
||||
this.yHeadRot = player.getYHeadRot();
|
||||
this.fallDistance = 0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -703,7 +716,14 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
public boolean isOnShoulder() {
|
||||
return this.getVehicle() instanceof Player;
|
||||
return this.entityData.get(SHOULDER_PLAYER_DATA).isPresent();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Player getShoulderPlayer() {
|
||||
return this.entityData.get(SHOULDER_PLAYER_DATA)
|
||||
.map(uuid -> this.level().getPlayerByUUID(uuid))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public boolean isClimbingToShoulder() {
|
||||
@@ -725,10 +745,9 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
private void dismountShoulder() {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (vehicle != null) {
|
||||
this.stopRiding();
|
||||
Vec3 dismountPos = vehicle.position().add(0.0D, 0.1D, 0.0D);
|
||||
Player shoulderPlayer = this.getShoulderPlayer();
|
||||
if (shoulderPlayer != null) {
|
||||
Vec3 dismountPos = shoulderPlayer.position().add(0.0D, 0.1D, 0.0D);
|
||||
this.setPos(dismountPos.x, dismountPos.y, dismountPos.z);
|
||||
}
|
||||
this.clearShoulderMountState();
|
||||
@@ -737,6 +756,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
private void clearShoulderMountState() {
|
||||
this.entityData.set(SHOULDER_PLAYER_DATA, Optional.empty());
|
||||
this.shoulderPlayerId = null;
|
||||
this.shoulderClimbStartPos = null;
|
||||
this.climbingToShoulderTicks = 0;
|
||||
@@ -746,16 +766,15 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
private void glideOffShoulder() {
|
||||
Entity vehicle = this.getVehicle();
|
||||
if (!(vehicle instanceof Player player)) {
|
||||
Player player = this.getShoulderPlayer();
|
||||
if (player == null) {
|
||||
this.dismountShoulder();
|
||||
return;
|
||||
}
|
||||
|
||||
Vec3 launchPos = this.getShoulderRidePos(player);
|
||||
this.stopRiding();
|
||||
this.setPos(launchPos.x, launchPos.y, launchPos.z);
|
||||
this.clearShoulderMountState();
|
||||
this.setPos(launchPos.x, launchPos.y, launchPos.z);
|
||||
this.fallDistance = 0.0F;
|
||||
|
||||
BlockPos glideTarget = this.findFallGlideTarget();
|
||||
@@ -815,7 +834,7 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
this.shoulderPlayerId = null;
|
||||
this.shoulderSneakGraceTicks = SHOULDER_SNEAK_GRACE_TICKS;
|
||||
this.setClimbingTrunk(false);
|
||||
this.startRiding(player, true);
|
||||
this.entityData.set(SHOULDER_PLAYER_DATA, Optional.of(player.getUUID()));
|
||||
this.setDeltaMovement(Vec3.ZERO);
|
||||
this.fallDistance = 0.0F;
|
||||
}
|
||||
@@ -1118,11 +1137,16 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
}
|
||||
|
||||
public void collectNut(ItemEntity itemEntity) {
|
||||
ItemStack itemStack = itemEntity.getItem();
|
||||
if (itemStack.isEmpty()) {
|
||||
itemEntity.discard();
|
||||
return;
|
||||
}
|
||||
|
||||
this.startPickupAnimation();
|
||||
|
||||
ItemStack particleStack = itemEntity.getItem().copy();
|
||||
ItemStack particleStack = itemStack.copy();
|
||||
particleStack.setCount(1);
|
||||
ItemStack itemStack = itemEntity.getItem();
|
||||
itemStack.shrink(1);
|
||||
if (itemStack.isEmpty()) {
|
||||
itemEntity.discard();
|
||||
@@ -1648,6 +1672,11 @@ public class FlyingSquirrelEntity extends TamableAnimal implements GeoEntity {
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.targetNut != null) {
|
||||
if (!this.targetNut.isAlive() || this.targetNut.getItem().isEmpty()) {
|
||||
this.targetNut = null;
|
||||
return;
|
||||
}
|
||||
|
||||
FlyingSquirrelEntity.this.getLookControl().setLookAt(this.targetNut, 30.0F, 30.0F);
|
||||
|
||||
if (--this.repathDelay <= 0 && !FlyingSquirrelEntity.this.isGliding()) {
|
||||
|
||||
+146
-180
@@ -5,8 +5,9 @@ import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import com.craisinlord.antarchy.content.AntarchySoundEvents;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageSources;
|
||||
import com.craisinlord.antarchy.content.entity.MissileSquidEntity;
|
||||
|
||||
import com.craisinlord.antarchy.content.entity.OctopusBombEntity;
|
||||
import com.craisinlord.antarchy.content.damage.AntarchyDamageTypes;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -68,24 +69,28 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
private static final String ATTACK_COOLDOWN_KEY = "AttackCooldown";
|
||||
private static final String ACTION_TICKS_KEY = "ActionTicks";
|
||||
private static final String PHASE_TRANSITION_TICKS_KEY = "PhaseTransitionTicks";
|
||||
private static final String NEXT_ATTACK_INDEX_KEY = "NextAttackIndex";
|
||||
private static final String SLAM_COOLDOWN_KEY = "SlamCooldown";
|
||||
private static final String PHASE_SUMMON_COOLDOWN_KEY = "PhaseSummonCooldown";
|
||||
private static final String LAST_ATTACK_STATE_KEY = "LastAttackState";
|
||||
private static final String AGGRO_TRIGGERED_KEY = "AggroTriggered";
|
||||
private static final String GRAB_TARGET_KEY = "GrabTarget";
|
||||
|
||||
private static final double BOSS_BAR_RANGE = 40.0D;
|
||||
|
||||
private static final int ATTACK_NONE = 0;
|
||||
private static final int ATTACK_GRAB = 1;
|
||||
private static final int ATTACK_SWIPE = 2;
|
||||
private static final int ATTACK_SLAM = 3;
|
||||
private static final int ANIM_IDLE = 0;
|
||||
private static final int ANIM_IDLE_TWO = 1;
|
||||
private static final int ANIM_SWIM = 2;
|
||||
private static final int ANIM_SWIPE = 3;
|
||||
private static final int ANIM_GRAB = 4;
|
||||
private static final int ATTACK_SWING_LEFT = 2;
|
||||
private static final int ATTACK_SWING_RIGHT = 3;
|
||||
|
||||
// Synced animation state values
|
||||
private static final int ANIM_NONE = 0;
|
||||
private static final int ANIM_CRY = 1;
|
||||
private static final int ANIM_GRAB = 2;
|
||||
private static final int ANIM_SWING_LEFT = 3;
|
||||
private static final int ANIM_SWING_RIGHT = 4;
|
||||
private static final int ANIM_DEATH = 5;
|
||||
|
||||
private static final int DEATH_ANIM_TICKS = 60;
|
||||
|
||||
private static final ResourceLocation PHASE_TWO_SPEED_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "kraken_phase_two_speed");
|
||||
private static final ResourceLocation PHASE_TWO_FLYING_SPEED_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "kraken_phase_two_flying_speed");
|
||||
private static final ResourceLocation PHASE_TWO_DAMAGE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "kraken_phase_two_damage");
|
||||
@@ -96,21 +101,21 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
private static final AttributeModifier PHASE_TWO_DAMAGE = new AttributeModifier(PHASE_TWO_DAMAGE_ID, PHASE_TWO_DAMAGE_BONUS, AttributeModifier.Operation.ADD_VALUE);
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation SWIM_ANIM = RawAnimation.begin().thenLoop("swim");
|
||||
private static final RawAnimation SWIPE_ANIM = RawAnimation.begin().thenPlay("Swipe");
|
||||
private static final RawAnimation GRAB_ANIM = RawAnimation.begin().thenPlay("Grab");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlay("Death");
|
||||
private static final RawAnimation CRY_ANIM = RawAnimation.begin().thenPlay("cry");
|
||||
private static final RawAnimation GRAB_ANIM = RawAnimation.begin().thenPlay("grab");
|
||||
private static final RawAnimation SWING_LEFT_ANIM = RawAnimation.begin().thenPlay("swing_left");
|
||||
private static final RawAnimation SWING_RIGHT_ANIM = RawAnimation.begin().thenPlay("swing_right");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlay("death");
|
||||
|
||||
private final ServerBossEvent bossEvent = new ServerBossEvent(Component.literal("The Kraken"), BossEvent.BossBarColor.BLUE, BossEvent.BossBarOverlay.PROGRESS);
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
@Nullable
|
||||
private Vec3 patrolTarget;
|
||||
private int patrolRetargetTicks;
|
||||
private int attackCooldown;
|
||||
private int actionTicks;
|
||||
private int phaseTransitionTicks;
|
||||
private int nextAttackIndex;
|
||||
private int slamCooldown;
|
||||
private int phaseSummonCooldown;
|
||||
private int lastAttackState;
|
||||
private boolean aggroTriggered;
|
||||
@@ -121,6 +126,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
private int strafeRetargetTicks;
|
||||
private float orbitDirection = 1.0F;
|
||||
private boolean stormActive;
|
||||
|
||||
public KrakenEntity(EntityType<? extends Monster> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.moveControl = new FlyingMoveControl(this, 20, true);
|
||||
@@ -149,7 +155,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
builder.define(ATTACK_STATE, ATTACK_NONE);
|
||||
builder.define(ROARING, false);
|
||||
builder.define(PHASE_TWO, false);
|
||||
builder.define(ANIMATION_STATE, ANIM_IDLE);
|
||||
builder.define(ANIMATION_STATE, ANIM_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -165,17 +171,26 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, "main_controller", 0, this::mainAnimController)
|
||||
// idle_controller always loops idle; action_controller layers on top when active
|
||||
controllers.add(new AnimationController<>(this, "idle_controller", 0, state -> {
|
||||
if (this.getAnimationState() == ANIM_DEATH) {
|
||||
return PlayState.STOP;
|
||||
}
|
||||
state.setAndContinue(IDLE_ANIM);
|
||||
return PlayState.CONTINUE;
|
||||
}).setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>()));
|
||||
controllers.add(new AnimationController<>(this, "action_controller", 0, this::actionAnimController)
|
||||
.setSoundKeyframeHandler(new AutoPlayingSoundKeyframeHandler<>()));
|
||||
}
|
||||
|
||||
private PlayState mainAnimController(AnimationState<KrakenEntity> state) {
|
||||
private PlayState actionAnimController(AnimationState<KrakenEntity> state) {
|
||||
return switch (this.getAnimationState()) {
|
||||
case ANIM_CRY -> state.setAndContinue(CRY_ANIM);
|
||||
case ANIM_GRAB -> state.setAndContinue(GRAB_ANIM);
|
||||
case ANIM_SWIPE -> state.setAndContinue(SWIPE_ANIM);
|
||||
case ANIM_SWIM -> state.setAndContinue(SWIM_ANIM);
|
||||
case ANIM_SWING_LEFT -> state.setAndContinue(SWING_LEFT_ANIM);
|
||||
case ANIM_SWING_RIGHT -> state.setAndContinue(SWING_RIGHT_ANIM);
|
||||
case ANIM_DEATH -> state.setAndContinue(DEATH_ANIM);
|
||||
default -> state.setAndContinue(IDLE_ANIM);
|
||||
default -> PlayState.STOP;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -221,9 +236,6 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
if (this.attackCooldown > 0) {
|
||||
this.attackCooldown--;
|
||||
}
|
||||
if (this.slamCooldown > 0) {
|
||||
this.slamCooldown--;
|
||||
}
|
||||
if (this.phaseSummonCooldown > 0) {
|
||||
this.phaseSummonCooldown--;
|
||||
}
|
||||
@@ -307,7 +319,8 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
if (source.is(DamageTypeTags.IS_FIRE)
|
||||
|| source.is(DamageTypes.LIGHTNING_BOLT)
|
||||
|| source.is(AntarchyDamageTypes.KRAKEN_LIGHTNING)
|
||||
|| source.getEntity() instanceof MissileSquidEntity) {
|
||||
|| source.getEntity() instanceof MissileSquidEntity
|
||||
|| source.getEntity() instanceof OctopusBombEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -351,8 +364,6 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
tag.putInt(ATTACK_COOLDOWN_KEY, this.attackCooldown);
|
||||
tag.putInt(ACTION_TICKS_KEY, this.actionTicks);
|
||||
tag.putInt(PHASE_TRANSITION_TICKS_KEY, this.phaseTransitionTicks);
|
||||
tag.putInt(NEXT_ATTACK_INDEX_KEY, this.nextAttackIndex);
|
||||
tag.putInt(SLAM_COOLDOWN_KEY, this.slamCooldown);
|
||||
tag.putInt(PHASE_SUMMON_COOLDOWN_KEY, this.phaseSummonCooldown);
|
||||
tag.putInt(LAST_ATTACK_STATE_KEY, this.lastAttackState);
|
||||
tag.putBoolean(AGGRO_TRIGGERED_KEY, this.aggroTriggered);
|
||||
@@ -374,8 +385,6 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.attackCooldown = tag.getInt(ATTACK_COOLDOWN_KEY);
|
||||
this.actionTicks = tag.getInt(ACTION_TICKS_KEY);
|
||||
this.phaseTransitionTicks = tag.getInt(PHASE_TRANSITION_TICKS_KEY);
|
||||
this.nextAttackIndex = tag.getInt(NEXT_ATTACK_INDEX_KEY);
|
||||
this.slamCooldown = tag.getInt(SLAM_COOLDOWN_KEY);
|
||||
this.phaseSummonCooldown = tag.getInt(PHASE_SUMMON_COOLDOWN_KEY);
|
||||
this.lastAttackState = tag.contains(LAST_ATTACK_STATE_KEY) ? tag.getInt(LAST_ATTACK_STATE_KEY) : ATTACK_NONE;
|
||||
this.aggroTriggered = tag.getBoolean(AGGRO_TRIGGERED_KEY);
|
||||
@@ -384,7 +393,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.entityData.set(ROARING, tag.getBoolean("Roaring"));
|
||||
this.entityData.set(ATTACK_STATE, tag.getInt("AttackState"));
|
||||
this.entityData.set(PHASE_TWO, tag.getBoolean("PhaseTwo"));
|
||||
this.entityData.set(ANIMATION_STATE, tag.contains("AnimationState") ? tag.getInt("AnimationState") : ANIM_IDLE);
|
||||
this.entityData.set(ANIMATION_STATE, tag.contains("AnimationState") ? tag.getInt("AnimationState") : ANIM_NONE);
|
||||
this.lightningStrikeCooldown = tag.getInt("LightningStrikeCooldown");
|
||||
this.lightningAmbientCooldown = tag.getInt("LightningAmbientCooldown");
|
||||
this.stormActive = false;
|
||||
@@ -394,6 +403,17 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startSeenByPlayer(net.minecraft.server.level.ServerPlayer player) {
|
||||
super.startSeenByPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopSeenByPlayer(net.minecraft.server.level.ServerPlayer player) {
|
||||
super.stopSeenByPlayer(player);
|
||||
this.bossEvent.removePlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Entity.RemovalReason reason) {
|
||||
this.clearKrakenStorm();
|
||||
@@ -475,7 +495,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.getLookControl().setLookAt(target, 25.0F, 20.0F);
|
||||
|
||||
if (!this.spawnedPhaseMinions && this.phaseTransitionTicks <= 40) {
|
||||
this.spawnPhaseMinions(1 + this.random.nextInt(2), true, target);
|
||||
this.spawnMinions(1 + this.random.nextInt(2), target);
|
||||
this.spawnedPhaseMinions = true;
|
||||
}
|
||||
|
||||
@@ -499,8 +519,8 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
|
||||
switch (this.getAttackState()) {
|
||||
case ATTACK_GRAB -> this.tickGrabAttack(target);
|
||||
case ATTACK_SWIPE -> this.tickSwipeAttack();
|
||||
case ATTACK_SLAM -> this.tickSlamAttack();
|
||||
case ATTACK_SWING_LEFT -> this.tickSwingAttack(false);
|
||||
case ATTACK_SWING_RIGHT -> this.tickSwingAttack(true);
|
||||
default -> this.finishAttack();
|
||||
}
|
||||
}
|
||||
@@ -512,12 +532,9 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
if (attackState == ATTACK_GRAB) {
|
||||
this.actionTicks = 55;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_ATTACK.get(), 1.45F, 0.9F);
|
||||
} else if (attackState == ATTACK_SWIPE) {
|
||||
} else if (attackState == ATTACK_SWING_LEFT || attackState == ATTACK_SWING_RIGHT) {
|
||||
this.actionTicks = 40;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_SPIN.get(), 1.3F, 0.95F);
|
||||
} else if (attackState == ATTACK_SLAM) {
|
||||
this.actionTicks = 34;
|
||||
this.playSound(AntarchySoundEvents.KRAKEN_ATTACK.get(), 1.5F, 0.82F);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,19 +562,10 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSwipeAttack() {
|
||||
// swingRight=true means ATTACK_SWING_RIGHT, which knocks players to the Kraken's LEFT
|
||||
private void tickSwingAttack(boolean swingRight) {
|
||||
if (this.actionTicks == 10) {
|
||||
this.performSwipe();
|
||||
}
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
this.finishAttack();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickSlamAttack() {
|
||||
if (this.actionTicks == 8) {
|
||||
this.performTentacleSlam();
|
||||
this.performSwing(swingRight);
|
||||
}
|
||||
|
||||
if (--this.actionTicks <= 0) {
|
||||
@@ -566,15 +574,12 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private void finishAttack() {
|
||||
int finishedAttack = this.getAttackState();
|
||||
int finished = this.getAttackState();
|
||||
this.setAttackState(ATTACK_NONE);
|
||||
this.actionTicks = 0;
|
||||
this.attackCooldown = this.isPhaseTwo() ? 12 : 18;
|
||||
this.grabbedTargetId = -1;
|
||||
this.lastAttackState = finishedAttack;
|
||||
if (finishedAttack == ATTACK_SLAM) {
|
||||
this.slamCooldown = this.isPhaseTwo() ? 110 : 150;
|
||||
}
|
||||
this.lastAttackState = finished;
|
||||
this.updateAnimationState();
|
||||
}
|
||||
|
||||
@@ -614,96 +619,57 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
private int chooseNextAttack(LivingEntity target) {
|
||||
int[] attackRotation = new int[] {ATTACK_GRAB, ATTACK_SWIPE, ATTACK_SLAM};
|
||||
int[] weightedAttacks = new int[attackRotation.length];
|
||||
int[] weights = new int[attackRotation.length];
|
||||
int eligibleCount = 0;
|
||||
int totalWeight = 0;
|
||||
double distSqr = this.distanceToSqr(target);
|
||||
boolean canGrab = distSqr <= 64.0D;
|
||||
boolean canSwing = distSqr <= 81.0D;
|
||||
|
||||
for (int attack : attackRotation) {
|
||||
if (!this.canUseAttack(attack, target)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int weight = this.getAttackWeight(attack);
|
||||
if (attack == this.lastAttackState) {
|
||||
weight = Math.max(1, weight / 3);
|
||||
}
|
||||
if (weight <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
weightedAttacks[eligibleCount] = attack;
|
||||
weights[eligibleCount] = weight;
|
||||
totalWeight += weight;
|
||||
eligibleCount++;
|
||||
}
|
||||
|
||||
if (eligibleCount == 0) {
|
||||
if (!canGrab && !canSwing) {
|
||||
return ATTACK_NONE;
|
||||
}
|
||||
|
||||
int roll = this.random.nextInt(totalWeight);
|
||||
for (int i = 0; i < eligibleCount; i++) {
|
||||
roll -= weights[i];
|
||||
if (roll < 0) {
|
||||
return weightedAttacks[i];
|
||||
}
|
||||
int grabWeight = canGrab ? (this.lastAttackState == ATTACK_GRAB ? 1 : 4) : 0;
|
||||
int swingWeight = canSwing ? (this.isSwingAttack(this.lastAttackState) ? 1 : 5) : 0;
|
||||
int total = grabWeight + swingWeight;
|
||||
if (total == 0) {
|
||||
return ATTACK_NONE;
|
||||
}
|
||||
|
||||
return weightedAttacks[eligibleCount - 1];
|
||||
int roll = this.random.nextInt(total);
|
||||
if (roll < grabWeight) {
|
||||
return ATTACK_GRAB;
|
||||
}
|
||||
return this.resolveSwingDirection(target);
|
||||
}
|
||||
|
||||
private int getAttackWeight(int attackState) {
|
||||
return switch (attackState) {
|
||||
case ATTACK_GRAB -> 4;
|
||||
case ATTACK_SWIPE -> 5;
|
||||
case ATTACK_SLAM -> this.isPhaseTwo() ? 1 : 0;
|
||||
default -> 0;
|
||||
};
|
||||
private boolean isSwingAttack(int attackState) {
|
||||
return attackState == ATTACK_SWING_LEFT || attackState == ATTACK_SWING_RIGHT;
|
||||
}
|
||||
|
||||
private void tickPhaseTwoPressure(LivingEntity target) {
|
||||
if (!this.isPhaseTwo() || this.phaseSummonCooldown > 0) {
|
||||
return;
|
||||
}
|
||||
if (this.countNearbyMissileSquids() >= 12) {
|
||||
this.phaseSummonCooldown = 40;
|
||||
return;
|
||||
}
|
||||
this.spawnPhaseMinions(1 + this.random.nextInt(2), true, target);
|
||||
this.phaseSummonCooldown = 90 + this.random.nextInt(50);
|
||||
}
|
||||
|
||||
private int countNearbyMissileSquids() {
|
||||
return this.level().getEntitiesOfClass(
|
||||
MissileSquidEntity.class,
|
||||
this.getBoundingBox().inflate(28.0D),
|
||||
squid -> squid.isAlive()
|
||||
).size();
|
||||
}
|
||||
|
||||
private boolean canUseAttack(int attackState, LivingEntity target) {
|
||||
double distanceToTarget = this.distanceToSqr(target);
|
||||
return switch (attackState) {
|
||||
case ATTACK_GRAB -> distanceToTarget <= 64.0D;
|
||||
case ATTACK_SWIPE -> distanceToTarget <= 81.0D && this.isTargetInFront(target, 0.1D);
|
||||
case ATTACK_SLAM -> this.slamCooldown <= 0 && distanceToTarget <= 144.0D;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isTargetInFront(LivingEntity target, double minimumDot) {
|
||||
// Determines left vs right based on which side the target is on relative to the Kraken.
|
||||
// Kraken's right vector is forward x world-up. If target is on the Kraken's right side,
|
||||
// use swing_right (which knocks them left). If on the left, use swing_left (knocks them right).
|
||||
private int resolveSwingDirection(LivingEntity target) {
|
||||
Vec3 forward = this.getViewVector(1.0F);
|
||||
Vec3 toTarget = target.position().subtract(this.position()).normalize();
|
||||
return forward.dot(toTarget) >= minimumDot;
|
||||
Vec3 right = forward.cross(new Vec3(0, 1, 0)).normalize();
|
||||
Vec3 toTarget = target.position().subtract(this.position());
|
||||
return right.dot(toTarget) > 0 ? ATTACK_SWING_RIGHT : ATTACK_SWING_LEFT;
|
||||
}
|
||||
|
||||
private void performSwipe() {
|
||||
private void performSwing(boolean swingRight) {
|
||||
AABB hitBox = this.getBoundingBox().inflate(7.0D, 3.0D, 7.0D);
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox, player -> player.isAlive() && this.isTargetInFront(player, -0.15D));
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox, LivingEntity::isAlive);
|
||||
float damage = (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE);
|
||||
|
||||
Vec3 forward = this.getViewVector(1.0F);
|
||||
// swingRight knocks to Kraken's left (-right), swingLeft knocks to Kraken's right (+right)
|
||||
Vec3 right = forward.cross(new Vec3(0, 1, 0)).normalize();
|
||||
Vec3 knockDir = swingRight ? right.scale(-1) : right;
|
||||
|
||||
for (Player player : players) {
|
||||
this.hurtAndKnockback(player, (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE), 1.75D, 0.45D);
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) continue;
|
||||
if (!player.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), damage)) continue;
|
||||
player.push(knockDir.x * 1.75D, 0.4D, knockDir.z * 1.75D);
|
||||
player.hurtMarked = true;
|
||||
}
|
||||
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
@@ -711,19 +677,6 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private void performTentacleSlam() {
|
||||
AABB hitBox = this.getBoundingBox().inflate(8.5D, 2.5D, 8.5D);
|
||||
List<Player> players = this.level().getEntitiesOfClass(Player.class, hitBox, LivingEntity::isAlive);
|
||||
float damage = (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE) * 0.75F;
|
||||
for (Player player : players) {
|
||||
this.hurtAndKnockback(player, damage, 1.35D, 0.62D);
|
||||
}
|
||||
|
||||
if (this.level() instanceof ServerLevel serverLevel) {
|
||||
serverLevel.sendParticles(ParticleTypes.SPLASH, this.getX(), this.getY() + 0.5D, this.getZ(), 36, 1.8D, 0.5D, 1.8D, 0.12D);
|
||||
}
|
||||
}
|
||||
|
||||
private void holdGrabbedTarget(LivingEntity target) {
|
||||
Vec3 anchor = this.position()
|
||||
.add(this.getViewVector(1.0F).normalize().scale(this.getBbWidth() * 0.65D))
|
||||
@@ -738,14 +691,6 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
target.hurtMarked = true;
|
||||
}
|
||||
|
||||
private void hurtAndKnockback(LivingEntity target, float damage, double horizontalStrength, double verticalStrength) {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel) || !target.hurt(AntarchyDamageSources.krakenMauling(serverLevel, this), damage)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.knockAway(target, horizontalStrength, verticalStrength);
|
||||
}
|
||||
|
||||
private void knockAway(LivingEntity target, double horizontalStrength, double verticalStrength) {
|
||||
Vec3 direction = target.position().subtract(this.position());
|
||||
Vec3 horizontal = new Vec3(direction.x, 0.0D, direction.z);
|
||||
@@ -771,7 +716,25 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
return entity instanceof LivingEntity livingEntity ? livingEntity : null;
|
||||
}
|
||||
|
||||
private void spawnPhaseMinions(int desiredCount, boolean launched, @Nullable LivingEntity currentTarget) {
|
||||
private void tickPhaseTwoPressure(LivingEntity target) {
|
||||
if (!this.isPhaseTwo() || this.phaseSummonCooldown > 0) {
|
||||
return;
|
||||
}
|
||||
if (this.countNearbyMinions() >= 12) {
|
||||
this.phaseSummonCooldown = 40;
|
||||
return;
|
||||
}
|
||||
this.spawnMinions(1 + this.random.nextInt(2), target);
|
||||
this.phaseSummonCooldown = 90 + this.random.nextInt(50);
|
||||
}
|
||||
|
||||
private int countNearbyMinions() {
|
||||
net.minecraft.world.phys.AABB range = this.getBoundingBox().inflate(28.0D);
|
||||
return this.level().getEntitiesOfClass(OctopusBombEntity.class, range, OctopusBombEntity::isAlive).size()
|
||||
+ this.level().getEntitiesOfClass(MissileSquidEntity.class, range, MissileSquidEntity::isAlive).size();
|
||||
}
|
||||
|
||||
private void spawnMinions(int desiredCount, @Nullable LivingEntity currentTarget) {
|
||||
if (!(this.level() instanceof ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
@@ -779,21 +742,23 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
int spawned = 0;
|
||||
|
||||
for (int attempt = 0; attempt < desiredCount * 4 && spawned < desiredCount; attempt++) {
|
||||
Vec3 spawnPos = this.findPhaseMinionSpawnPos(currentTarget);
|
||||
MissileSquidEntity missileSquid = AntarchyObjects.MISSILE_SQUID.get().create(serverLevel);
|
||||
if (missileSquid == null) {
|
||||
continue;
|
||||
}
|
||||
Vec3 spawnPos = this.findBombSpawnPos(currentTarget);
|
||||
boolean spawnBomb = this.random.nextBoolean();
|
||||
|
||||
missileSquid.setCanFly(true);
|
||||
missileSquid.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, this.getYRot(), 0.0F);
|
||||
serverLevel.addFreshEntity(missileSquid);
|
||||
if (launched) {
|
||||
missileSquid.launchAsProjectile(this, this.createPhaseMinionLaunchVelocity(spawnPos, currentTarget));
|
||||
} else if (currentTarget != null) {
|
||||
missileSquid.setTarget(currentTarget);
|
||||
if (spawnBomb) {
|
||||
OctopusBombEntity bomb = AntarchyObjects.OCTOPUS_BOMB.get().create(serverLevel);
|
||||
if (bomb == null) continue;
|
||||
bomb.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, this.getYRot(), 0.0F);
|
||||
serverLevel.addFreshEntity(bomb);
|
||||
bomb.launchAsProjectile(this, this.createBombLaunchVelocity(spawnPos, currentTarget));
|
||||
serverLevel.sendParticles(ParticleTypes.BUBBLE, bomb.getX(), bomb.getY() + 0.4D, bomb.getZ(), 14, 0.3D, 0.25D, 0.3D, 0.03D);
|
||||
} else {
|
||||
MissileSquidEntity squid = AntarchyObjects.MISSILE_SQUID.get().create(serverLevel);
|
||||
if (squid == null) continue;
|
||||
squid.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, this.getYRot(), 0.0F);
|
||||
serverLevel.addFreshEntity(squid);
|
||||
serverLevel.sendParticles(ParticleTypes.BUBBLE, squid.getX(), squid.getY() + 0.4D, squid.getZ(), 14, 0.3D, 0.25D, 0.3D, 0.03D);
|
||||
}
|
||||
serverLevel.sendParticles(ParticleTypes.BUBBLE, missileSquid.getX(), missileSquid.getY() + 0.4D, missileSquid.getZ(), 14, 0.3D, 0.25D, 0.3D, 0.03D);
|
||||
spawned++;
|
||||
}
|
||||
|
||||
@@ -802,7 +767,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
private Vec3 findPhaseMinionSpawnPos(@Nullable LivingEntity currentTarget) {
|
||||
private Vec3 findBombSpawnPos(@Nullable LivingEntity currentTarget) {
|
||||
Vec3 anchor = currentTarget != null ? currentTarget.position() : this.position();
|
||||
for (int attempt = 0; attempt < 18; attempt++) {
|
||||
double angle = this.random.nextDouble() * Mth.TWO_PI;
|
||||
@@ -821,7 +786,7 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
return this.position().add(0.0D, this.getBbHeight() * 0.6D, 0.0D);
|
||||
}
|
||||
|
||||
private Vec3 createPhaseMinionLaunchVelocity(Vec3 spawnPos, @Nullable LivingEntity currentTarget) {
|
||||
private Vec3 createBombLaunchVelocity(Vec3 spawnPos, @Nullable LivingEntity currentTarget) {
|
||||
Vec3 aimPoint = currentTarget != null
|
||||
? currentTarget.getEyePosition()
|
||||
: this.position().add(this.getViewVector(1.0F).scale(10.0D));
|
||||
@@ -1089,8 +1054,13 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.phaseTransitionTicks > 0 || this.isRoaring()) {
|
||||
this.setAnimationState(ANIM_IDLE);
|
||||
if (this.isRoaring()) {
|
||||
this.setAnimationState(ANIM_CRY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.phaseTransitionTicks > 0) {
|
||||
this.setAnimationState(ANIM_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1099,22 +1069,18 @@ public class KrakenEntity extends Monster implements GeoEntity {
|
||||
this.setAnimationState(ANIM_GRAB);
|
||||
return;
|
||||
}
|
||||
case ATTACK_SWIPE, ATTACK_SLAM -> {
|
||||
this.setAnimationState(ANIM_SWIPE);
|
||||
case ATTACK_SWING_LEFT -> {
|
||||
this.setAnimationState(ANIM_SWING_LEFT);
|
||||
return;
|
||||
}
|
||||
case ATTACK_SWING_RIGHT -> {
|
||||
this.setAnimationState(ANIM_SWING_RIGHT);
|
||||
return;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
|
||||
Vec3 velocity = this.getDeltaMovement();
|
||||
double horizontalSpeedSqr = velocity.horizontalDistanceSqr();
|
||||
double verticalSpeedSqr = velocity.y * velocity.y;
|
||||
if (horizontalSpeedSqr > 0.03D && horizontalSpeedSqr > verticalSpeedSqr * 2.5D) {
|
||||
this.setAnimationState(ANIM_SWIM);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAnimationState(this.isPhaseTwo() ? ANIM_IDLE_TWO : ANIM_IDLE);
|
||||
this.setAnimationState(ANIM_NONE);
|
||||
}
|
||||
}
|
||||
+20
-33
@@ -76,16 +76,13 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
private static final int ANIM_ATTACK = 3;
|
||||
private static final int ANIM_FLY_ATTACK = 4;
|
||||
private static final int ANIM_ROAR = 5;
|
||||
private static final int ANIM_DEATH = 6;
|
||||
private static final int ANIM_TAKEOFF = 7;
|
||||
private static final int ANIM_FLY_DAMAGED = 8;
|
||||
|
||||
private static final int ATTACK_TOTAL_TICKS = 18;
|
||||
private static final int ATTACK_DAMAGE_TICK = 9;
|
||||
private static final int INTRO_ROAR_TICKS = 32;
|
||||
private static final int COMBAT_ROAR_TICKS = 24;
|
||||
private static final int DEATH_TICKS = 36;
|
||||
private static final int ATTACK_TOTAL_TICKS = 20;
|
||||
private static final int ATTACK_DAMAGE_TICK = 10;
|
||||
private static final int INTRO_ROAR_TICKS = 30;
|
||||
private static final int COMBAT_ROAR_TICKS = 30;
|
||||
private static final int DEATH_TICKS = 30;
|
||||
private static final int TARGET_RESET_TICKS = 60;
|
||||
private static final int MIN_AIRBORNE_TICKS_FOR_FLY_ANIM = 4;
|
||||
private static final int DREAD_TICKS = 160;
|
||||
private static final int WEAKNESS_TICKS = 100;
|
||||
private static final int BLOCK_BREAK_TICKS = 10;
|
||||
@@ -101,15 +98,12 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
private static final double FLIGHT_DISENGAGE_RANGE_SQR = 36.0D;
|
||||
private static final double ROAR_RETRY_DISTANCE_SQR = 400.0D;
|
||||
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("Idle");
|
||||
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
|
||||
private static final RawAnimation WALK_ANIM = RawAnimation.begin().thenLoop("walk");
|
||||
private static final RawAnimation TAKEOFF_ANIM = RawAnimation.begin().thenPlay("animation").thenLoop("fly");
|
||||
private static final RawAnimation FLY_ANIM = RawAnimation.begin().thenLoop("fly");
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack").thenLoop("Idle");
|
||||
private static final RawAnimation FLY_ATTACK_ANIM = RawAnimation.begin().thenPlay("fly_attack").thenLoop("fly");
|
||||
private static final RawAnimation ROAR_ANIM = RawAnimation.begin().thenPlay("roar").thenLoop("Idle");
|
||||
private static final RawAnimation DEATH_ANIM = RawAnimation.begin().thenPlayAndHold("death");
|
||||
|
||||
private static final RawAnimation ATTACK_ANIM = RawAnimation.begin().thenPlay("attack").thenLoop("idle");
|
||||
private static final RawAnimation FLY_ATTACK_ANIM = RawAnimation.begin().thenPlay("attack2").thenLoop("fly");
|
||||
private static final RawAnimation ROAR_ANIM = RawAnimation.begin().thenPlay("roar").thenLoop("idle");
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private int attackCooldown;
|
||||
@@ -179,18 +173,14 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
int animState = this.getAnimationState();
|
||||
state.getController().setAnimationSpeed(switch (animState) {
|
||||
case ANIM_FLY -> 0.45D;
|
||||
case ANIM_DEATH -> 0.6D;
|
||||
default -> 1.0D;
|
||||
});
|
||||
return switch (animState) {
|
||||
case ANIM_WALK -> state.setAndContinue(WALK_ANIM);
|
||||
case ANIM_TAKEOFF -> state.setAndContinue(TAKEOFF_ANIM);
|
||||
case ANIM_FLY -> state.setAndContinue(FLY_ANIM);
|
||||
// case ANIM_FLY_DAMAGED -> state.setAndContinue(FLY_ANIM);
|
||||
case ANIM_ATTACK -> state.setAndContinue(ATTACK_ANIM);
|
||||
case ANIM_FLY_ATTACK -> state.setAndContinue(FLY_ATTACK_ANIM);
|
||||
case ANIM_ROAR -> state.setAndContinue(ROAR_ANIM);
|
||||
case ANIM_DEATH -> state.setAndContinue(DEATH_ANIM);
|
||||
default -> state.setAndContinue(IDLE_ANIM);
|
||||
};
|
||||
}
|
||||
@@ -216,6 +206,14 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
if (this.level().isClientSide) {
|
||||
this.tickClientParticles();
|
||||
this.updateFlightRotation();
|
||||
if (this.isRoaring()) {
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.register(
|
||||
this,
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.NIGHTMARE_RANGE,
|
||||
com.craisinlord.antarchy.content.client.CameraShakeClientState.NIGHTMARE_STRENGTH,
|
||||
this::isRoaring
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,7 +247,6 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
}
|
||||
|
||||
if (this.isDeadOrDying()) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.updateFlightRotation();
|
||||
return;
|
||||
}
|
||||
@@ -367,7 +364,6 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
public void die(DamageSource damageSource) {
|
||||
if (!this.level().isClientSide) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.attackAnimationTicks = 0;
|
||||
this.attackCooldown = 0;
|
||||
this.roarTicks = 0;
|
||||
@@ -381,9 +377,6 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
@Override
|
||||
protected void tickDeath() {
|
||||
this.deathTime++;
|
||||
if (this.deathTime == 1) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
}
|
||||
if (this.deathTime >= DEATH_TICKS) {
|
||||
this.remove(RemovalReason.KILLED);
|
||||
this.dropExperience(this);
|
||||
@@ -641,7 +634,6 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
|
||||
private void updateAnimationState() {
|
||||
if (this.isDeadOrDying()) {
|
||||
this.setAnimationState(ANIM_DEATH);
|
||||
this.groundMoveTicks = 0;
|
||||
this.wingFlapCooldown = 0;
|
||||
return;
|
||||
@@ -653,23 +645,18 @@ public class NightmareEntity extends Monster implements GeoEntity {
|
||||
return;
|
||||
}
|
||||
if (this.attackAnimationTicks > 0) {
|
||||
this.setAnimationState(this.onGround() ? ANIM_ATTACK : ANIM_FLY_ATTACK);
|
||||
this.setAnimationState(this.isNoGravity() || this.airborneTicks >= MIN_AIRBORNE_TICKS_FOR_FLY_ANIM ? ANIM_FLY_ATTACK : ANIM_ATTACK);
|
||||
this.groundMoveTicks = 0;
|
||||
this.tickWingFlapSound();
|
||||
return;
|
||||
}
|
||||
if (this.flyingToTarget) {
|
||||
this.groundMoveTicks = 0;
|
||||
if (this.onGround() || this.airborneTicks <= 8) {
|
||||
this.setAnimationState(ANIM_TAKEOFF);
|
||||
this.wingFlapCooldown = 0;
|
||||
} else {
|
||||
this.setAnimationState(ANIM_FLY);
|
||||
this.tickWingFlapSound();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this.onGround()) {
|
||||
if (this.isNoGravity() || this.airborneTicks >= MIN_AIRBORNE_TICKS_FOR_FLY_ANIM) {
|
||||
this.groundMoveTicks = 0;
|
||||
this.setAnimationState(ANIM_FLY);
|
||||
this.tickWingFlapSound();
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.craisinlord.antarchy.content.fluid;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import com.craisinlord.antarchy.content.AntarchyObjects;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.tags.EntityTypeTags;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.LiquidBlock;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.FlowingFluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
public class BileLiquidBlock extends LiquidBlock {
|
||||
private static final ResourceLocation BILE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "bile");
|
||||
private static final ResourceLocation FLOWING_BILE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "flowing_bile");
|
||||
private static final ResourceLocation BROODSTONE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "broodstone");
|
||||
private static final ResourceLocation MYRMITE_ID = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "myrmite");
|
||||
private static final int EFFECT_DURATION_TICKS = 100;
|
||||
|
||||
public BileLiquidBlock(FlowingFluid fluid, BlockBehaviour.Properties properties) {
|
||||
super(fluid, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) {
|
||||
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston);
|
||||
if (!level.isClientSide) {
|
||||
reactWithWater(state, level, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
|
||||
super.onPlace(state, level, pos, oldState, movedByPiston);
|
||||
if (!level.isClientSide) {
|
||||
reactWithWater(state, level, pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void reactWithWater(BlockState state, Level level, BlockPos pos) {
|
||||
for (Direction dir : Direction.values()) {
|
||||
FluidState neighbor = level.getFluidState(pos.relative(dir));
|
||||
if (neighbor.is(Fluids.WATER) || neighbor.is(Fluids.FLOWING_WATER)) {
|
||||
boolean isSource = state.getFluidState().isSource();
|
||||
ResourceLocation resultId = isSource ? BROODSTONE_ID : MYRMITE_ID;
|
||||
Block resultBlock = BuiltInRegistries.BLOCK.getOptional(resultId).orElse(null);
|
||||
if (resultBlock != null) {
|
||||
level.setBlock(pos, resultBlock.defaultBlockState(), 3);
|
||||
level.levelEvent(1501, pos, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) {
|
||||
super.entityInside(state, level, pos, entity);
|
||||
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
if (!level.isClientSide) {
|
||||
if (livingEntity.getType().is(EntityTypeTags.SENSITIVE_TO_BANE_OF_ARTHROPODS)) {
|
||||
return;
|
||||
}
|
||||
livingEntity.addEffect(new MobEffectInstance(AntarchyObjects.STINKY_EFFECT.get(), EFFECT_DURATION_TICKS, 0, false, true, true));
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.WEAKNESS, EFFECT_DURATION_TICKS, 0, false, true, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isBile(FluidState fluidState) {
|
||||
ResourceLocation fluidId = BuiltInRegistries.FLUID.getKey(fluidState.getType());
|
||||
return BILE_ID.equals(fluidId) || FLOWING_BILE_ID.equals(fluidId);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,14 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
private static final String SPIN_TICKS_TAG = "antarchy.big_bertha_spin_ticks";
|
||||
private static final String SPIN_DIRECTION_X_TAG = "antarchy.big_bertha_spin_direction_x";
|
||||
private static final String SPIN_DIRECTION_Z_TAG = "antarchy.big_bertha_spin_direction_z";
|
||||
private static final String TORETERROR_JUMPING_TAG = "antarchy.toreterror_jumping";
|
||||
private static final String TORETERROR_LAUNCHED_TAG = "antarchy.toreterror_launched";
|
||||
private static final int TORETERROR_LANDING_RADIUS = 6;
|
||||
private static final int TORETERROR_COOLDOWN_TICKS = 200;
|
||||
private static final int TORETERROR_SHAKE_TICKS = 25;
|
||||
|
||||
/** Client-side only: how many ticks of camera shake remain from a Toreterror landing. */
|
||||
public static int clientShakeTicks = 0;
|
||||
private static final ResourceLocation ATTACK_RANGE_MODIFIER_ID = ResourceLocation.fromNamespaceAndPath("antarchy", "big_bertha_attack_range");
|
||||
private final Tier tier;
|
||||
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
|
||||
@@ -116,6 +124,14 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
|
||||
}
|
||||
|
||||
if (mode == BossMode.TORETERROR) {
|
||||
if (player.getCooldowns().isOnCooldown(this)) {
|
||||
return InteractionResultHolder.fail(stack);
|
||||
}
|
||||
startToreterrorJump(level, player, stack);
|
||||
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
|
||||
}
|
||||
|
||||
if (mode != BossMode.MOLEVORE) {
|
||||
return InteractionResultHolder.pass(stack);
|
||||
}
|
||||
@@ -144,7 +160,7 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
case LUCID -> applyLucidHit(target, player, serverLevel);
|
||||
case NIGHTMARE -> applyNightmareHit(target, serverLevel);
|
||||
case KRAKEN -> applyKrakenHit(player, target, serverLevel);
|
||||
case MOLEVORE -> {
|
||||
case MOLEVORE, TORETERROR -> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +174,32 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
return;
|
||||
}
|
||||
|
||||
// Toreterror jump tracking
|
||||
if (isToreterrorJumping(stack) && player.getMainHandItem() == stack) {
|
||||
player.fallDistance = 0.0F; // prevent fall damage during the crash-down
|
||||
boolean launched = readInt(stack, TORETERROR_LAUNCHED_TAG) == 1;
|
||||
if (!launched && !player.onGround()) {
|
||||
CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putInt(TORETERROR_LAUNCHED_TAG, 1));
|
||||
}
|
||||
if (launched && player.onGround()) {
|
||||
CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
|
||||
tag.remove(TORETERROR_JUMPING_TAG);
|
||||
tag.remove(TORETERROR_LAUNCHED_TAG);
|
||||
});
|
||||
if (!level.isClientSide && player instanceof ServerPlayer serverPlayer) {
|
||||
applyToreterrorLanding((ServerLevel) level, serverPlayer);
|
||||
}
|
||||
if (level.isClientSide) {
|
||||
clientShakeTicks = TORETERROR_SHAKE_TICKS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tick client shake counter
|
||||
if (level.isClientSide && clientShakeTicks > 0) {
|
||||
clientShakeTicks--;
|
||||
}
|
||||
|
||||
int spinTicks = getSpinTicks(stack);
|
||||
if (spinTicks <= 0) {
|
||||
return;
|
||||
@@ -293,6 +335,15 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryCycleModeWhileCoolingDown(Level level, Player player, ItemStack stack) {
|
||||
if (player.getMainHandItem() != stack || !player.isShiftKeyDown() || !player.getCooldowns().isOnCooldown(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cycleMode(level, player, stack);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startMolevoreSpin(Level level, Player player, ItemStack stack) {
|
||||
Vec3 direction = player.getLookAngle().multiply(1.0D, 0.0D, 1.0D);
|
||||
if (direction.lengthSqr() < 1.0E-4D) {
|
||||
@@ -317,6 +368,43 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
}
|
||||
}
|
||||
|
||||
private void startToreterrorJump(Level level, Player player, ItemStack stack) {
|
||||
CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> {
|
||||
tag.putInt(TORETERROR_JUMPING_TAG, 1);
|
||||
tag.remove(TORETERROR_LAUNCHED_TAG);
|
||||
});
|
||||
player.setDeltaMovement(player.getDeltaMovement().x, 1.5D, player.getDeltaMovement().z);
|
||||
player.hasImpulse = true;
|
||||
player.fallDistance = 0.0F;
|
||||
player.getCooldowns().addCooldown(this, TORETERROR_COOLDOWN_TICKS);
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
level.playSound(null, player.blockPosition(), AntarchySoundEvents.TORETERROR_JUMP_UP.get(), SoundSource.PLAYERS, 1.2F, 0.9F);
|
||||
}
|
||||
|
||||
private void applyToreterrorLanding(ServerLevel level, ServerPlayer player) {
|
||||
ItemStack heldStack = player.getMainHandItem();
|
||||
float damage = (float) (player.getAttributeValue(Attributes.ATTACK_DAMAGE) * 2.0D);
|
||||
AABB aoe = player.getBoundingBox().inflate(TORETERROR_LANDING_RADIUS, 3.0D, TORETERROR_LANDING_RADIUS);
|
||||
for (LivingEntity victim : level.getEntitiesOfClass(LivingEntity.class, aoe,
|
||||
e -> e.isAlive() && e != player && !e.isSpectator())) {
|
||||
victim.hurt(player.damageSources().playerAttack(player), damage);
|
||||
Vec3 knockDir = victim.position().subtract(player.position()).multiply(1.0D, 0.0D, 1.0D);
|
||||
if (knockDir.lengthSqr() < 1.0E-4D) knockDir = player.getLookAngle().multiply(1.0D, 0.0D, 1.0D);
|
||||
Vec3 normalized = knockDir.normalize();
|
||||
victim.push(normalized.x * 2.5D, 0.6D, normalized.z * 2.5D);
|
||||
victim.hurtMarked = true;
|
||||
}
|
||||
level.playSound(null, player.blockPosition(), AntarchySoundEvents.TORETERROR_JUMP_LAND.get(), SoundSource.PLAYERS, 1.2F, 0.9F);
|
||||
level.sendParticles(ParticleTypes.EXPLOSION, player.getX(), player.getY() + 0.5D, player.getZ(), 4, 1.5D, 0.3D, 1.5D, 0.0D);
|
||||
level.sendParticles(ParticleTypes.SPLASH, player.getX(), player.getY() + 0.5D, player.getZ(), 60, 3.0D, 0.5D, 3.0D, 0.2D);
|
||||
level.sendParticles(ParticleTypes.CRIT, player.getX(), player.getY() + 0.5D, player.getZ(), 20, 2.0D, 0.5D, 2.0D, 0.1D);
|
||||
heldStack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
|
||||
}
|
||||
|
||||
private static boolean isToreterrorJumping(ItemStack stack) {
|
||||
return readInt(stack, TORETERROR_JUMPING_TAG) == 1;
|
||||
}
|
||||
|
||||
private void tickMolevoreSpin(ServerLevel level, ServerPlayer player, ItemStack stack, int spinTicks) {
|
||||
Vec3 direction = getStoredSpinDirection(stack);
|
||||
double verticalBoost = player.onGround() ? 0.08D : Math.max(-0.08D, player.getDeltaMovement().y);
|
||||
@@ -505,7 +593,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
NIGHTMARE(1, "tooltip.antarchy.big_bertha.mode.nightmare", "message.antarchy.big_bertha.mode.nightmare"),
|
||||
KRAKEN(2, "tooltip.antarchy.big_bertha.mode.kraken", "message.antarchy.big_bertha.mode.kraken"),
|
||||
MOLEVORE(3, "tooltip.antarchy.big_bertha.mode.molevore", "message.antarchy.big_bertha.mode.molevore"),
|
||||
LUCID(4, "tooltip.antarchy.big_bertha.mode.lucid", "message.antarchy.big_bertha.mode.lucid");
|
||||
LUCID(4, "tooltip.antarchy.big_bertha.mode.lucid", "message.antarchy.big_bertha.mode.lucid"),
|
||||
TORETERROR(5, "tooltip.antarchy.big_bertha.mode.toreterror", "message.antarchy.big_bertha.mode.toreterror");
|
||||
|
||||
private final int id;
|
||||
private final String tooltipKey;
|
||||
@@ -523,7 +612,8 @@ public class BigBerthaItem extends SwordItem implements GeoItem {
|
||||
case LUCID -> NIGHTMARE;
|
||||
case NIGHTMARE -> KRAKEN;
|
||||
case KRAKEN -> MOLEVORE;
|
||||
case MOLEVORE -> BASILISK;
|
||||
case MOLEVORE -> TORETERROR;
|
||||
case TORETERROR -> BASILISK;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
public final class JumpyBootsHelper {
|
||||
public static final int CHARGE_TICKS_MAX = 30;
|
||||
public static final int PRIME_WINDOW_TICKS = 15;
|
||||
public static final int COOLDOWN_TICKS = 40;
|
||||
|
||||
public static final float MAX_VERTICAL_BOOST = 2.0F;
|
||||
public static final float SPRINT_FORWARD_BOOST = 0.3F;
|
||||
public static final long FALL_PROTECTION_TICKS = 120L;
|
||||
public static final String FALL_PROTECTION_NBT_KEY = "antarchy:jumpy_boots_protection_until";
|
||||
|
||||
private JumpyBootsHelper() {
|
||||
}
|
||||
|
||||
public static float verticalBoostFor(int chargeTicks) {
|
||||
float ratio = Math.min(chargeTicks / (float) CHARGE_TICKS_MAX, 1.0F);
|
||||
return ratio * MAX_VERTICAL_BOOST;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.ArmorMaterial;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class JumpyBootsItem extends ArmorItem {
|
||||
public JumpyBootsItem(Holder<ArmorMaterial> material, Properties properties) {
|
||||
super(material, Type.BOOTS, properties);
|
||||
}
|
||||
|
||||
public static boolean isWearingJumpyBoots(net.minecraft.world.entity.LivingEntity entity) {
|
||||
return entity != null && entity.getItemBySlot(net.minecraft.world.entity.EquipmentSlot.FEET).getItem() instanceof JumpyBootsItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack) {
|
||||
return stack.getCount() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.jumpy_boots").withStyle(ChatFormatting.DARK_RED));
|
||||
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import com.craisinlord.antarchy.content.client.NightmareSwordTooltipHelper;
|
||||
import java.util.List;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.SwordItem;
|
||||
@@ -21,6 +23,18 @@ public class NightmareSwordItem extends SwordItem {
|
||||
this.attackSpeed = attackSpeed;
|
||||
}
|
||||
|
||||
public static float calculateDamage(LivingEntity attacker) {
|
||||
float baseDamage = (float) AntarchySettings.nightmareSwordBaseDamage();
|
||||
float maxHealth = attacker.getMaxHealth();
|
||||
if (maxHealth <= 0.0F) {
|
||||
return baseDamage;
|
||||
}
|
||||
|
||||
float missingFraction = (maxHealth - attacker.getHealth()) / maxHealth;
|
||||
float scalingFactor = (float) AntarchySettings.nightmareSwordScalingFactor();
|
||||
return baseDamage + missingFraction * scalingFactor * baseDamage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemAttributeModifiers getDefaultAttributeModifiers() {
|
||||
return SwordItem.createAttributes(
|
||||
@@ -38,6 +52,7 @@ public class NightmareSwordItem extends SwordItem {
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.nightmare_sword").withStyle(ChatFormatting.DARK_RED));
|
||||
tooltipComponents.add(NightmareSwordTooltipHelper.damageLine());
|
||||
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,144 @@
|
||||
package com.craisinlord.antarchy.content.item;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import com.craisinlord.antarchy.config.AntarchySettings;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.effect.MobEffectInstance;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.EquipmentSlotGroup;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.ArmorMaterial;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.component.ItemAttributeModifiers;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
public class PrimordialArmorItem extends ArmorItem {
|
||||
private final Type armorType;
|
||||
|
||||
public PrimordialArmorItem(Holder<ArmorMaterial> material, Type type, Properties properties) {
|
||||
super(material, type, properties);
|
||||
this.armorType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemAttributeModifiers getDefaultAttributeModifiers() {
|
||||
String prefix = "primordial_" + this.armorType.getName();
|
||||
ItemAttributeModifiers.Builder builder = ItemAttributeModifiers.builder()
|
||||
.add(
|
||||
Attributes.ARMOR,
|
||||
new AttributeModifier(
|
||||
ResourceLocation.withDefaultNamespace(prefix + "_armor"),
|
||||
getDefense(),
|
||||
AttributeModifier.Operation.ADD_VALUE
|
||||
),
|
||||
EquipmentSlotGroup.bySlot(this.armorType.getSlot())
|
||||
)
|
||||
.add(
|
||||
Attributes.ARMOR_TOUGHNESS,
|
||||
new AttributeModifier(
|
||||
ResourceLocation.withDefaultNamespace(prefix + "_armor_toughness"),
|
||||
getToughness(),
|
||||
AttributeModifier.Operation.ADD_VALUE
|
||||
),
|
||||
EquipmentSlotGroup.bySlot(this.armorType.getSlot())
|
||||
);
|
||||
|
||||
double knockbackResistance = getKnockbackResistance(this.armorType);
|
||||
if (knockbackResistance > 0.0D) {
|
||||
builder.add(
|
||||
Attributes.KNOCKBACK_RESISTANCE,
|
||||
new AttributeModifier(
|
||||
ResourceLocation.withDefaultNamespace(prefix + "_knockback_resistance"),
|
||||
knockbackResistance,
|
||||
AttributeModifier.Operation.ADD_VALUE
|
||||
),
|
||||
EquipmentSlotGroup.bySlot(this.armorType.getSlot())
|
||||
);
|
||||
}
|
||||
|
||||
builder.add(
|
||||
Attributes.ATTACK_KNOCKBACK,
|
||||
new AttributeModifier(
|
||||
ResourceLocation.withDefaultNamespace(prefix + "_attack_knockback"),
|
||||
AntarchySettings.primordialArmorKnockbackPerPiece(),
|
||||
AttributeModifier.Operation.ADD_VALUE
|
||||
),
|
||||
EquipmentSlotGroup.bySlot(this.armorType.getSlot())
|
||||
);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefense() {
|
||||
return super.getDefense();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getToughness() {
|
||||
return super.getToughness();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack) {
|
||||
return stack.getCount() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) {
|
||||
if (!level.isClientSide && this.armorType == Type.HELMET && entity instanceof LivingEntity living) {
|
||||
if (living.getItemBySlot(EquipmentSlot.HEAD) == stack) {
|
||||
living.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 40, 0, false, false, true));
|
||||
}
|
||||
}
|
||||
super.inventoryTick(stack, level, entity, slotId, isSelected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(ItemStack stack, Item.TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
|
||||
if (this.armorType == Type.HELMET) {
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.primordial_helmet_water_breathing").withStyle(ChatFormatting.AQUA));
|
||||
}
|
||||
String knockbackDealt = String.format(Locale.ROOT, "%.1f", AntarchySettings.primordialArmorKnockbackPerPiece());
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.primordial_armor_knockback_dealt", knockbackDealt).withStyle(ChatFormatting.YELLOW));
|
||||
tooltipComponents.add(Component.translatable("tooltip.antarchy.primordial_armor_set_bonus").withStyle(ChatFormatting.DARK_GREEN));
|
||||
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
|
||||
}
|
||||
|
||||
public static double getKnockbackDealtBonus(LivingEntity entity) {
|
||||
int pieces = 0;
|
||||
for (ItemStack armorStack : entity.getArmorSlots()) {
|
||||
if (armorStack.getItem() instanceof PrimordialArmorItem) {
|
||||
pieces++;
|
||||
}
|
||||
}
|
||||
return pieces * AntarchySettings.primordialArmorKnockbackPerPiece();
|
||||
}
|
||||
|
||||
public static boolean hasFullSet(LivingEntity entity) {
|
||||
return entity.getItemBySlot(EquipmentSlot.HEAD).getItem() instanceof PrimordialArmorItem
|
||||
&& entity.getItemBySlot(EquipmentSlot.CHEST).getItem() instanceof PrimordialArmorItem
|
||||
&& entity.getItemBySlot(EquipmentSlot.LEGS).getItem() instanceof PrimordialArmorItem
|
||||
&& entity.getItemBySlot(EquipmentSlot.FEET).getItem() instanceof PrimordialArmorItem;
|
||||
}
|
||||
|
||||
private static double getKnockbackResistance(Type armorType) {
|
||||
return switch (armorType) {
|
||||
case HELMET -> 0.20D;
|
||||
case CHESTPLATE, BODY -> 0.30D;
|
||||
case LEGGINGS -> 0.30D;
|
||||
case BOOTS -> 0.20D;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
|
||||
import com.craisinlord.antarchy.content.entity.MissileSquidEntity;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -21,7 +22,11 @@ import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ProjectileWeaponItem;
|
||||
import net.minecraft.world.item.UseAnim;
|
||||
import net.minecraft.world.item.enchantment.Enchantments;
|
||||
import net.minecraft.world.item.enchantment.ItemEnchantments;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import software.bernie.geckolib.animatable.GeoItem;
|
||||
import software.bernie.geckolib.animatable.client.GeoRenderProvider;
|
||||
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
|
||||
@@ -55,17 +60,10 @@ public class SquidzookaItem extends ProjectileWeaponItem implements GeoItem {
|
||||
}
|
||||
|
||||
if (!level.isClientSide()) {
|
||||
MissileSquidEntity projectileSquid = AntarchyObjects.MISSILE_SQUID.get().create(level);
|
||||
if (projectileSquid != null) {
|
||||
projectileSquid.moveTo(
|
||||
player.getX(),
|
||||
player.getEyeY() - 0.2D,
|
||||
player.getZ(),
|
||||
player.getYRot(),
|
||||
player.getXRot()
|
||||
);
|
||||
projectileSquid.launchFromSquidzooka(player, (float) AntarchySettings.squidzookaLaunchVelocity());
|
||||
level.addFreshEntity(projectileSquid);
|
||||
fireSquid(level, player, 0.0F);
|
||||
if (hasMultishot(itemStack)) {
|
||||
fireSquid(level, player, -10.0F);
|
||||
fireSquid(level, player, 10.0F);
|
||||
}
|
||||
spawnFireParticles((ServerLevel) level, player);
|
||||
itemStack.hurtAndBreak(1, player, usedHand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND);
|
||||
@@ -115,6 +113,16 @@ public class SquidzookaItem extends ProjectileWeaponItem implements GeoItem {
|
||||
projectile.shootFromRotation(livingEntity, livingEntity.getXRot(), livingEntity.getYRot(), angle, velocity, inaccuracy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack) {
|
||||
return stack.getCount() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEnchantmentValue() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
|
||||
controllers.add(new AnimationController<>(this, FIRE_CONTROLLER, state -> PlayState.STOP)
|
||||
@@ -147,6 +155,32 @@ public class SquidzookaItem extends ProjectileWeaponItem implements GeoItem {
|
||||
triggerAnim(livingEntity, animatableId, FIRE_CONTROLLER, FIRE_ANIMATION);
|
||||
}
|
||||
|
||||
private static void fireSquid(Level level, Player player, float yawOffset) {
|
||||
MissileSquidEntity projectileSquid = AntarchyObjects.MISSILE_SQUID.get().create(level);
|
||||
if (projectileSquid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float yaw = player.getYRot() + yawOffset;
|
||||
Vec3 direction = Vec3.directionFromRotation(new Vec2(player.getXRot(), yaw));
|
||||
projectileSquid.moveTo(
|
||||
player.getX(),
|
||||
player.getEyeY() - 0.2D,
|
||||
player.getZ(),
|
||||
yaw,
|
||||
player.getXRot()
|
||||
);
|
||||
projectileSquid.launchAsProjectile(player, direction.scale(AntarchySettings.squidzookaLaunchVelocity()).add(player.getDeltaMovement()));
|
||||
level.addFreshEntity(projectileSquid);
|
||||
}
|
||||
|
||||
private static boolean hasMultishot(ItemStack stack) {
|
||||
return stack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)
|
||||
.keySet()
|
||||
.stream()
|
||||
.anyMatch(holder -> holder.is(Enchantments.MULTISHOT));
|
||||
}
|
||||
|
||||
private static void spawnFireParticles(ServerLevel level, Player player) {
|
||||
double lookX = player.getLookAngle().x;
|
||||
double lookY = player.getLookAngle().y;
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public record BigBerthaModeCyclePayload() implements CustomPacketPayload {
|
||||
public static final Type<BigBerthaModeCyclePayload> TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "big_bertha_mode_cycle"));
|
||||
public static final StreamCodec<ByteBuf, BigBerthaModeCyclePayload> STREAM_CODEC = StreamCodec.unit(new BigBerthaModeCyclePayload());
|
||||
|
||||
@Override
|
||||
public Type<BigBerthaModeCyclePayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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 DorrieChargeJumpPayload(boolean pressing) implements CustomPacketPayload {
|
||||
public static final Type<DorrieChargeJumpPayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dorrie_charge_jump_input"));
|
||||
public static final StreamCodec<ByteBuf, DorrieChargeJumpPayload> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, DorrieChargeJumpPayload::pressing,
|
||||
DorrieChargeJumpPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<DorrieChargeJumpPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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 DorrieJumpInputPayload(boolean pressing) implements CustomPacketPayload {
|
||||
public static final Type<DorrieJumpInputPayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "dorrie_jump_input"));
|
||||
public static final StreamCodec<ByteBuf, DorrieJumpInputPayload> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, DorrieJumpInputPayload::pressing,
|
||||
DorrieJumpInputPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<DorrieJumpInputPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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 HerculesBeetleImpactShakePayload(int durationTicks) implements CustomPacketPayload {
|
||||
public static final Type<HerculesBeetleImpactShakePayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_impact_shake"));
|
||||
public static final StreamCodec<ByteBuf, HerculesBeetleImpactShakePayload> STREAM_CODEC =
|
||||
ByteBufCodecs.VAR_INT.map(HerculesBeetleImpactShakePayload::new, HerculesBeetleImpactShakePayload::durationTicks);
|
||||
|
||||
@Override
|
||||
public Type<HerculesBeetleImpactShakePayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
public final class HerculesBeetleImpactShakeSync {
|
||||
private static final BiConsumer<ServerPlayer, Integer> NOOP = (player, ticks) -> {
|
||||
};
|
||||
|
||||
private static volatile BiConsumer<ServerPlayer, Integer> sink = NOOP;
|
||||
|
||||
private HerculesBeetleImpactShakeSync() {
|
||||
}
|
||||
|
||||
public static void setSink(BiConsumer<ServerPlayer, Integer> sink) {
|
||||
HerculesBeetleImpactShakeSync.sink = sink == null ? NOOP : sink;
|
||||
}
|
||||
|
||||
public static void send(ServerPlayer player, int ticks) {
|
||||
sink.accept(player, ticks);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.craisinlord.antarchy.content.network;
|
||||
|
||||
import com.craisinlord.antarchy.Antarchy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
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 HerculesBeetleJumpInputPayload(boolean pressing) implements CustomPacketPayload {
|
||||
public static final Type<HerculesBeetleJumpInputPayload> TYPE =
|
||||
new Type<>(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "hercules_beetle_jump_input"));
|
||||
public static final StreamCodec<ByteBuf, HerculesBeetleJumpInputPayload> STREAM_CODEC =
|
||||
StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, HerculesBeetleJumpInputPayload::pressing,
|
||||
HerculesBeetleJumpInputPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<HerculesBeetleJumpInputPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
Loaded 100 of 597 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user