|
|
|
@ -16,6 +16,7 @@ import net.minecraft.world.damagesource.DamageSource
|
|
|
|
|
import net.minecraft.world.entity.AnimationState
|
|
|
|
|
import net.minecraft.world.entity.EntityType
|
|
|
|
|
import net.minecraft.world.entity.LivingEntity
|
|
|
|
|
import net.minecraft.world.entity.MoverType
|
|
|
|
|
import net.minecraft.world.entity.ai.attributes.AttributeSupplier
|
|
|
|
|
import net.minecraft.world.entity.ai.attributes.Attributes
|
|
|
|
|
import net.minecraft.world.entity.ai.goal.Goal
|
|
|
|
@ -34,6 +35,7 @@ import net.minecraft.world.entity.projectile.SmallFireball
|
|
|
|
|
import net.minecraft.world.level.Level
|
|
|
|
|
import net.minecraft.world.phys.Vec3
|
|
|
|
|
import ru.dbotthepony.mc.otm.core.TranslatableComponent
|
|
|
|
|
import ru.dbotthepony.mc.otm.core.util.TickList
|
|
|
|
|
import ru.dbotthepony.mc.otm.registry.MNames
|
|
|
|
|
import ru.dbotthepony.mc.otm.registry.game.MSoundEvents
|
|
|
|
|
import java.util.*
|
|
|
|
@ -73,8 +75,9 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun registerGoals() {
|
|
|
|
|
goalSelector.addGoal(7, LookAtPlayerGoal(this, Player::class.java, 8f))
|
|
|
|
|
goalSelector.addGoal(3, NearestAttackableTargetGoal(this, LivingEntity::class.java, 10, true, true) { entity ->
|
|
|
|
|
targetSelector.addGoal(1, HurtByTargetGoal(this))
|
|
|
|
|
|
|
|
|
|
targetSelector.addGoal(2, NearestAttackableTargetGoal(this, LivingEntity::class.java, 10, true, true) { entity ->
|
|
|
|
|
entity is Player ||
|
|
|
|
|
entity is Villager ||
|
|
|
|
|
entity is AbstractIllager ||
|
|
|
|
@ -86,7 +89,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
goalSelector.addGoal(2, StayNearGoal(this))
|
|
|
|
|
goalSelector.addGoal(2, BlazeFireballGoal(this))
|
|
|
|
|
|
|
|
|
|
targetSelector.addGoal(1, HurtByTargetGoal(this))
|
|
|
|
|
goalSelector.addGoal(7, LookAtPlayerGoal(this, Player::class.java, 8f))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun createNavigation(level: Level): PathNavigation = GroundPathNavigation(this, level)
|
|
|
|
@ -103,15 +106,9 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
return SoundEvents.VAULT_BREAK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//fire inv
|
|
|
|
|
//there should be a better way
|
|
|
|
|
override fun fireImmune(): Boolean {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
///boss healthbar
|
|
|
|
|
private val bossEvent: ServerBossEvent =
|
|
|
|
|
ServerBossEvent(TranslatableComponent(MNames.ENFORCER), BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.PROGRESS)
|
|
|
|
|
ServerBossEvent(name, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.PROGRESS)
|
|
|
|
|
|
|
|
|
|
override fun startSeenByPlayer(player: ServerPlayer) {
|
|
|
|
|
super.startSeenByPlayer(player)
|
|
|
|
@ -125,11 +122,8 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
|
|
|
|
|
override fun aiStep() {
|
|
|
|
|
super.aiStep()
|
|
|
|
|
bossEvent.name = name
|
|
|
|
|
bossEvent.progress = this.health / this.maxHealth
|
|
|
|
|
|
|
|
|
|
if (!this.isOnFire) {
|
|
|
|
|
this.clearFire()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun die(cause: DamageSource) {
|
|
|
|
@ -144,7 +138,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
return 180
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun shootFireball(math: Float) {
|
|
|
|
|
private fun shootFireball(math: Float) {
|
|
|
|
|
if (level().isClientSide) return
|
|
|
|
|
|
|
|
|
|
val offset_dist = 1.4
|
|
|
|
@ -169,7 +163,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
level().addFreshEntity(fireball)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun shootMissile(math: Float) {
|
|
|
|
|
private fun shootMissile(math: Float) {
|
|
|
|
|
if (level().isClientSide) return
|
|
|
|
|
|
|
|
|
|
val offset_dist = 1.4
|
|
|
|
@ -203,8 +197,6 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var chargeTime = 0
|
|
|
|
|
private val windupTime = 20
|
|
|
|
|
private val maxChargeTime = 40
|
|
|
|
|
private var chargeDir: Vec3? = null
|
|
|
|
|
private var cooldown = 0
|
|
|
|
|
private var isCharging = false
|
|
|
|
@ -252,41 +244,39 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
val leftPartic_z = mob.z - mob.lookAngle.z * 2 + 0.8f * mob.lookAngle.x
|
|
|
|
|
val rightPartic_z = mob.z - mob.lookAngle.z * 2 - 0.8f * mob.lookAngle.x
|
|
|
|
|
|
|
|
|
|
if (mob.level() is ServerLevel) {
|
|
|
|
|
(mob.level() as ServerLevel).sendParticles(
|
|
|
|
|
ParticleTypes.LARGE_SMOKE,
|
|
|
|
|
leftPartic_x, smoke_y, leftPartic_z,
|
|
|
|
|
2,
|
|
|
|
|
0.0, 0.07, 0.0,
|
|
|
|
|
0.01
|
|
|
|
|
)
|
|
|
|
|
val level = mob.level() as ServerLevel
|
|
|
|
|
|
|
|
|
|
(mob.level() as ServerLevel).sendParticles(
|
|
|
|
|
ParticleTypes.LARGE_SMOKE,
|
|
|
|
|
rightPartic_x, smoke_y, rightPartic_z,
|
|
|
|
|
2,
|
|
|
|
|
0.0, 0.07, 0.0,
|
|
|
|
|
0.01
|
|
|
|
|
)
|
|
|
|
|
level.sendParticles(
|
|
|
|
|
ParticleTypes.LARGE_SMOKE,
|
|
|
|
|
leftPartic_x, smoke_y, leftPartic_z,
|
|
|
|
|
2,
|
|
|
|
|
0.0, 0.07, 0.0,
|
|
|
|
|
0.01
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
level.sendParticles(
|
|
|
|
|
ParticleTypes.LARGE_SMOKE,
|
|
|
|
|
rightPartic_x, smoke_y, rightPartic_z,
|
|
|
|
|
2,
|
|
|
|
|
0.0, 0.07, 0.0,
|
|
|
|
|
0.01
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (chargeDir == null || chargeDir!!.length() < 0.1) {
|
|
|
|
|
stop()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (chargeTime == windupTime) {
|
|
|
|
|
if (chargeDir == null || chargeDir!!.length() < 0.1) {
|
|
|
|
|
stop()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
mob.playSound(MSoundEvents.ENFORCER_CHARGE, 1.0f, 1.0f)
|
|
|
|
|
}
|
|
|
|
|
mob.playSound(MSoundEvents.ENFORCER_CHARGE, 1.0f, 1.0f)
|
|
|
|
|
|
|
|
|
|
val dir = chargeDir ?: return
|
|
|
|
|
mob.yRot = (-Mth.atan2(dir.x, dir.z) * (180f / Math.PI)).toFloat()
|
|
|
|
|
mob.yRot = Math.toDegrees(-Mth.atan2(dir.x, dir.z)).toFloat()
|
|
|
|
|
mob.yHeadRot = mob.yRot
|
|
|
|
|
|
|
|
|
|
mob.move(net.minecraft.world.entity.MoverType.SELF, dir.scale(1.8))
|
|
|
|
|
|
|
|
|
|
mob.move(MoverType.SELF, dir.scale(1.8))
|
|
|
|
|
|
|
|
|
|
if (mob.horizontalCollision) {
|
|
|
|
|
mob.level().playSound(
|
|
|
|
|
level.playSound(
|
|
|
|
|
null,
|
|
|
|
|
mob.x, mob.y, mob.z,
|
|
|
|
|
SoundEvents.GENERIC_EXPLODE,
|
|
|
|
@ -294,6 +284,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
1.0f,
|
|
|
|
|
1.0f
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
stop()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
@ -323,6 +314,8 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
companion object {
|
|
|
|
|
private const val minCooldown = 35
|
|
|
|
|
private const val maxCooldown = 60
|
|
|
|
|
private const val windupTime = 20
|
|
|
|
|
private const val maxChargeTime = 40
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -345,8 +338,12 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun tick() {
|
|
|
|
|
target?.let { mob.lookControl.setLookAt(it, 30.0f, 30.0f) }
|
|
|
|
|
val target = mob.target ?: return
|
|
|
|
|
var target = target
|
|
|
|
|
|
|
|
|
|
if (target != null)
|
|
|
|
|
mob.lookControl.setLookAt(target, 30.0f, 30.0f)
|
|
|
|
|
|
|
|
|
|
target = mob.target ?: return
|
|
|
|
|
|
|
|
|
|
if (moveCD > 0) {
|
|
|
|
|
moveCD--
|
|
|
|
@ -372,7 +369,7 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
|
|
|
|
|
class BlazeFireballGoal(private val mob: Enforcer) : Goal() {
|
|
|
|
|
private var cooldown = 0
|
|
|
|
|
private var fireTick = 0
|
|
|
|
|
private val tickList = TickList()
|
|
|
|
|
|
|
|
|
|
init {
|
|
|
|
|
setFlags(EnumSet.of(Flag.LOOK))
|
|
|
|
@ -382,47 +379,35 @@ class Enforcer(type: EntityType<Enforcer>, level: Level) : Monster(type,level) {
|
|
|
|
|
return mob.target != null && !mob.isCharging
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun canContinueToUse(): Boolean {
|
|
|
|
|
return canUse()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun tick() {
|
|
|
|
|
val target = mob.target ?: return
|
|
|
|
|
mob.lookControl.setLookAt(target, 30.0f, 30.0f)
|
|
|
|
|
|
|
|
|
|
if (cooldown > 0) {
|
|
|
|
|
fireTick = 20
|
|
|
|
|
cooldown--
|
|
|
|
|
return
|
|
|
|
|
if (--cooldown <= 0) {
|
|
|
|
|
cooldown = Mth.nextInt(mob.random, MIN_COOLDOWN, MAX_COOLDOWN) + CYCLE_LENGTH
|
|
|
|
|
|
|
|
|
|
for (ticks in SHOOT_AT)
|
|
|
|
|
tickList.timer(ticks) {
|
|
|
|
|
mob.shootFireball(0.5f)
|
|
|
|
|
//could have a better firing sound
|
|
|
|
|
mob.playSound(SoundEvents.BLAZE_SHOOT, 1.0f, Mth.nextFloat(mob.random, 0.9f, 1.1f))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fireTick > 0){
|
|
|
|
|
fireTick --
|
|
|
|
|
tickList.tick()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun stop() {
|
|
|
|
|
super.stop()
|
|
|
|
|
cooldown = 0
|
|
|
|
|
tickList.clear()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fireTick >= 19){
|
|
|
|
|
mob.playSound(MSoundEvents.ENFORCER_BEEP, 1.0f, 1.0f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (fireTick == 15){
|
|
|
|
|
mob.shootFireball(0.5f)
|
|
|
|
|
//could have a better firing sound
|
|
|
|
|
mob.playSound(SoundEvents.BLAZE_SHOOT, 1.0f, 1.0f)
|
|
|
|
|
}
|
|
|
|
|
if (fireTick == 10){
|
|
|
|
|
mob.shootFireball(0.5f)
|
|
|
|
|
mob.playSound(SoundEvents.BLAZE_SHOOT, 1.0f, 1.0f)
|
|
|
|
|
}
|
|
|
|
|
if (fireTick == 5){
|
|
|
|
|
mob.shootFireball(0.5f)
|
|
|
|
|
mob.playSound(SoundEvents.BLAZE_SHOOT, 1.0f, 1.0f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
} else {
|
|
|
|
|
cooldown = 5 + mob.random.nextInt(10)
|
|
|
|
|
}
|
|
|
|
|
companion object {
|
|
|
|
|
private val SHOOT_AT = intArrayOf(5, 10, 15)
|
|
|
|
|
private const val MIN_COOLDOWN = 5
|
|
|
|
|
private const val MAX_COOLDOWN = 15
|
|
|
|
|
private const val CYCLE_LENGTH = 20
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|