package mhfc.net.common.ai.entity.boss.greatjaggi; import mhfc.net.common.ai.ActionAdapter; import mhfc.net.common.ai.general.AIUtils; import mhfc.net.common.ai.general.AIUtils.IDamageCalculator; import mhfc.net.common.entity.monster.EntityGreatJaggi; import mhfc.net.common.util.world.WorldHelper; import net.minecraft.entity.Entity; import net.minecraft.util.Vec3; public class Run extends ActionAdapter<EntityGreatJaggi> { private static final int MOVEMENT_START_LOOP = 10; private static final int MOVEMENT_FINISH_LOOP = 33; private static final int AI_END = 85; private static final float TURN_RATE_INITIAL = 8.5f; private static final float TURN_RATE_DURING_RUN = 2.05f; private static final float MAX_RUN_DISTANCE = 30f; private static final int MAX_RUN_FRAMES = 85; private static final double RUN_SPEED = 0.25D; private static final double STOP_SPEED = 0.3D; private static final double REQUIRED_TARGET_MAX_DIST = 3f; private static final IDamageCalculator damageCalc = AIUtils.defaultDamageCalc(35f, 50f, 9999999f); private static enum PastEntityEnum { NOT_PASSED, PASSED, LOOP_FINISHED, TURNING; } private static enum AttackPhase { START(false) { @Override public void onPhaseStart(Run attk) { EntityGreatJaggi monster = attk.getEntity(); monster.motionX = monster.motionY = monster.motionZ = 0f; monster.getTurnHelper().updateTurnSpeed(TURN_RATE_INITIAL); attk.getEntity().getTurnHelper().updateTargetPoint(attk.target); } @Override public void update(Run attk) { attk.getEntity().getTurnHelper().forceUpdate(); } @Override public AttackPhase next(Run attk) { if (attk.target == null) { return STOPPED; } if (attk.getCurrentFrame() < MOVEMENT_START_LOOP) { return START; } return RUNNING; } }, RUNNING(true) { @Override public void onPhaseStart(Run attk) { attk.getEntity().getTurnHelper().updateTurnSpeed(TURN_RATE_DURING_RUN); attk.framesRunning = 0; } @Override public void update(Run attk) { EntityGreatJaggi monster = attk.getEntity(); Vec3 entityPos = Vec3.createVectorHelper(monster.posX, monster.posY, monster.posZ); Vec3 vecToTarget = entityPos.subtract(WorldHelper.getEntityPositionVector(attk.target)); monster.getTurnHelper().updateTargetPoint(attk.target); monster.moveForward(RUN_SPEED, true); Vec3 look = monster.getLookVec(); boolean tarBeh = vecToTarget.normalize().dotProduct(look) < 0; boolean ranLongEnough = attk.runStartPoint.subtract(entityPos).lengthVector() > MAX_RUN_DISTANCE || attk.framesRunning > MAX_RUN_FRAMES; if ((tarBeh || ranLongEnough) && attk.hasPassed == PastEntityEnum.NOT_PASSED) { attk.hasPassed = PastEntityEnum.PASSED; } } @Override public AttackPhase next(Run attk) { if (attk.hasPassed == PastEntityEnum.LOOP_FINISHED) { return STOPPING; } return RUNNING; } @Override public int nextFrame(Run attk, int curr) { attk.framesRunning++; int looping = MOVEMENT_FINISH_LOOP - MOVEMENT_START_LOOP; if (attk.hasPassed == PastEntityEnum.PASSED && (curr + 1 >= MOVEMENT_FINISH_LOOP)) { attk.hasPassed = PastEntityEnum.LOOP_FINISHED; } return MOVEMENT_START_LOOP + (curr + 1 - MOVEMENT_START_LOOP) % looping; } }, STOPPING(true) { @Override public void update(Run attk) { EntityGreatJaggi e = attk.getEntity(); e.moveForward(STOP_SPEED, false); } @Override public AttackPhase next(Run attk) { if (AI_END < attk.getCurrentFrame()) { return STOPPED; } return STOPPING; } }, STOPPED(false) { @Override public void onPhaseStart(Run attk) { Entity entity = attk.getEntity(); entity.motionX = entity.motionY = entity.motionZ = 0f; } }; public final boolean isDamaging; private AttackPhase(boolean isDamaging) { this.isDamaging = isDamaging; } public void onPhaseStart(Run attk) {} public void update(Run attk) {} public AttackPhase next(Run attk) { return this; } public int nextFrame(Run attk, int curr) { return ++curr; } } private AttackPhase currentPhase; private PastEntityEnum hasPassed; private Vec3 runStartPoint; private int framesRunning; @SuppressWarnings("unused") private int runCycles; public Run() { setAnimation("mhfc:models/GreatJaggi/GreatJaggiRun.mcanm"); } @Override public float getWeight() { EntityGreatJaggi monster = this.getEntity(); target = monster.getAttackTarget(); if (target == null) { return DONT_SELECT; } Vec3 toTarget = WorldHelper.getVectorToTarget(monster, target); double dist = toTarget.lengthVector(); if (dist < REQUIRED_TARGET_MAX_DIST) { return DONT_SELECT; } return (float) Math.log(dist / 5f + 1); } @Override public void beginExecution() { EntityGreatJaggi entity = getEntity(); target = entity.getAttackTarget(); currentPhase = AttackPhase.START; hasPassed = PastEntityEnum.NOT_PASSED; runCycles = 0; entity.playLivingSound(); framesRunning = 0; currentPhase.onPhaseStart(this); runStartPoint = Vec3.createVectorHelper(entity.posX, entity.posY, entity.posZ); } @Override public void update() { currentPhase.update(this); if (currentPhase.isDamaging) { AIUtils.damageCollidingEntities(getEntity(), damageCalc); } AttackPhase nextPhase = currentPhase.next(this); if (currentPhase != nextPhase) { nextPhase.onPhaseStart(this); currentPhase = nextPhase; } } @Override public boolean shouldContinue() { return this.currentPhase != AttackPhase.STOPPED; } @Override public void finishExecution() { } @Override public byte mutexBits() { return 1; } @Override public int setToNextFrame(int frame) { return super.setToNextFrame(currentPhase.nextFrame(this, frame)); } }