package mhfc.net.common.ai.entity.boss.tigrex; 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.EntityTigrex; import mhfc.net.common.util.world.WorldHelper; import net.minecraft.entity.Entity; import net.minecraft.util.Vec3; public class Run extends ActionAdapter<EntityTigrex> { private static final int runningStarts = 21; private static final int runningEnds = 60; private static final int attackEnd = 75; private static final float TURN_RATE_INITIAL = 10.5f; private static final float TURN_RATE_DURING_RUN = 0.17f; private static final float MAX_RUN_DISTANCE = 40f; private static final int MAX_RUN_FRAMES = 200; private static final double RUN_SPEED = 1.7; private static final double STOP_SPEED = 0.7; private static final IDamageCalculator damageCalc = AIUtils.defaultDamageCalc(120f, 50F, 99999F); private static enum PastEntityEnum { NOT_PASSED, PASSED, LOOP_FINISHED, TURNING; } private static enum AttackPhase { START(false) { @Override public void onPhaseStart(Run attk) { EntityTigrex tigrex = attk.getEntity(); tigrex.motionX = tigrex.motionY = tigrex.motionZ = 0f; tigrex.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() < runningStarts) { 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) { EntityTigrex tigrex = attk.getEntity(); Vec3 tigPos = WorldHelper.getEntityPositionVector(tigrex); Vec3 vecToTarget = tigPos.subtract(WorldHelper.getEntityPositionVector(attk.target)); tigrex.getTurnHelper().updateTargetPoint(attk.target); tigrex.moveForward(RUN_SPEED, true); Vec3 look = tigrex.getLookVec(); boolean tarBeh = vecToTarget.normalize().dotProduct(look) < 0; boolean ranLongEnough = attk.runStartPoint.subtract(tigPos).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 = runningEnds - runningStarts; if (attk.hasPassed == PastEntityEnum.PASSED && (curr + 1 >= runningEnds)) { attk.hasPassed = PastEntityEnum.LOOP_FINISHED; } return runningStarts + (curr + 1 - runningStarts) % looping; } }, STOPPING(true) { @Override public void update(Run attk) { EntityTigrex e = attk.getEntity(); e.moveForward(STOP_SPEED, false); } @Override public AttackPhase next(Run attk) { if (attackEnd < 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/Tigrex/run.mcanm"); } @Override public float getWeight() { EntityTigrex tigrex = this.getEntity(); target = tigrex.getAttackTarget(); if (target == null) { return DONT_SELECT; } Vec3 toTarget = WorldHelper.getVectorToTarget(tigrex, target); double dist = toTarget.lengthVector(); return (float) Math.log(dist / 5f + 1); // More likely the // further away } @Override public void beginExecution() { EntityTigrex tig = getEntity(); target = tig.getAttackTarget(); tig.playSound("mhfc:tigrex.charge", 2.0F, 1.0F); currentPhase = AttackPhase.START; hasPassed = PastEntityEnum.NOT_PASSED; runCycles = 0; framesRunning = 0; currentPhase.onPhaseStart(this); runStartPoint = Vec3.createVectorHelper(tig.posX, tig.posY, tig.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() { // To determine if to cancel return this.currentPhase != AttackPhase.STOPPED; } @Override public void finishExecution() { // When finished } @Override public byte mutexBits() { // Well known mutex bits return 1; } @Override public int setToNextFrame(int frame) { // For the animation return super.setToNextFrame(currentPhase.nextFrame(this, frame)); // Notify // the // adapter } }