package mods.eln.transparentnode.turret; import mods.eln.fsm.CompositeState; import mods.eln.fsm.State; import mods.eln.fsm.StateMachine; import mods.eln.generic.GenericItemUsingDamageDescriptor; import mods.eln.item.EntitySensorFilterDescriptor; import mods.eln.misc.Coordonate; import mods.eln.misc.Utils; import mods.eln.sim.process.destruct.WorldExplosion; import mods.eln.sound.SoundCommand; import net.minecraft.block.Block; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import java.util.List; import java.util.Random; public class TurretSlowProcess extends StateMachine { private static final Random rand = new Random(); private double actualPower; public TurretSlowProcess(TurretElement element) { actualPower = 0; setInitialState(new IdleState()); reset(); this.element = element; } private final TurretElement element; private class IdleState implements State { @Override public void enter() { } @Override public State state(double time) { if (element.load.getU() >= element.getDescriptor().getProperties().minimalVoltage * (1.0 + element.getDescriptor().getProperties().minimalVoltageHysteresisFactor)) { return new ActiveState(); } else { return this; } } @Override public void leave() { } } private class ActiveState extends CompositeState { public ActiveState() { setInitialState(new SeekingState()); } @Override public void enter() { element.setEnabled(true); actualPower = element.getDescriptor().getProperties().basePower; element.play(new SoundCommand("eln:TurretActivated").mulVolume(0.5)); super.enter(); } @Override public State state(double time) { super.state(time); if (element.load.getU() < element.getDescriptor().getProperties().minimalVoltage * (1.0 - element.getDescriptor().getProperties().minimalVoltageHysteresisFactor)) return new WaitState(); else if (element.load.getU() > element.getDescriptor().getProperties().maximalVoltage) return new DamageState(); else return this; } @Override public void leave() { element.setEnabled(false); actualPower = 0; element.play(new SoundCommand("eln:TurretDeactivated").mulVolume(0.5)); super.leave(); } } private class DamageState implements State { @Override public void enter() { WorldExplosion explosion = new WorldExplosion(element).machineExplosion(); explosion.destructImpl(); } @Override public State state(double time) { return new IdleState(); } @Override public void leave() { } } private class WaitState implements State { private double delay = 5.0; @Override public void enter() { } @Override public State state(double time) { delay -= time; if (delay <= 0.0) { return new IdleState(); } else { return this; } } @Override public void leave() { } } private class SeekingState implements State { private double lastScanWasBefore = 0.; @Override public void enter() { actualPower = element.getDescriptor().getProperties().basePower; element.setGunPosition(0); element.setGunElevation(0); element.setSeekMode(true); element.setTurretAngle(element.getDescriptor().getProperties().actionAngle); } @Override public State state(double time) { if (element.getTurretAngle() >= element.getDescriptor().getProperties().actionAngle) element.setTurretAngle(-element.getDescriptor().getProperties().actionAngle); else if (element.getTurretAngle() <= -element.getDescriptor().getProperties().actionAngle) element.setTurretAngle(element.getDescriptor().getProperties().actionAngle); lastScanWasBefore += time; if (lastScanWasBefore < element.getDescriptor().getProperties().entityDetectionInterval) return null; lastScanWasBefore = 0; Class filterClass = null; ItemStack filterStack = element.getInventory().getStackInSlot(TurretContainer.filterId); if (filterStack != null) { GenericItemUsingDamageDescriptor gen = EntitySensorFilterDescriptor.getDescriptor(filterStack); if (gen != null && gen instanceof EntitySensorFilterDescriptor) { EntitySensorFilterDescriptor filter = (EntitySensorFilterDescriptor) gen; filterClass = filter.entityClass; } } Coordonate coord = element.coordonate(); AxisAlignedBB bb = coord.getAxisAlignedBB((int) element.getDescriptor().getProperties().detectionDistance); @SuppressWarnings("unchecked") List<EntityLivingBase> list = coord.world().getEntitiesWithinAABB(EntityLivingBase.class, bb); for (EntityLivingBase entity : list) { double dx = (entity.posX - coord.x - 0.5); double dz = (entity.posZ - coord.z - 0.5); double entityAngle = -Math.toDegrees(Math.atan2(dz, dx)); switch (element.front) { case XN: if (entityAngle > 0) entityAngle -= 180; else entityAngle += 180; break; case ZP: entityAngle += 90; break; case ZN: entityAngle -= 90; break; default: break; } if (Math.abs(entityAngle - element.getTurretAngle()) < 15 && Math.abs(entityAngle) < element.getDescriptor().getProperties().actionAngle) { if (element.filterIsSpare) { if (filterClass != null && filterClass.isAssignableFrom(entity.getClass())) return null; } else { if (filterClass == null || !filterClass.isAssignableFrom(entity.getClass())) return null; } List<Block> blockList = Utils.traceRay(coord.world(), coord.x + 0.5, coord.y + 0.5, coord.z + 0.5, entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ); boolean visible = true; for (Block b : blockList) if (b.isOpaqueCube()) { visible = false; break; } if (visible) { element.play(new SoundCommand("eln:TurretFire").mulVolume(0.4)); return new AimingState(entity); } } } return null; } @Override public void leave() { element.setSeekMode(false); } } private class AimingState implements State { public AimingState(EntityLivingBase target) { this.target = target; } private final EntityLivingBase target; @Override public void enter() { actualPower = element.getDescriptor().getProperties().basePower + element.chargePower; element.setGunPosition(1); } @Override public State state(double time) { if (target.isDead) return new SeekingState(); Class filterClass = null; ItemStack filterStack = element.getInventory().getStackInSlot(TurretContainer.filterId); if (filterStack != null) { GenericItemUsingDamageDescriptor gen = EntitySensorFilterDescriptor.getDescriptor(filterStack); if (gen != null && gen instanceof EntitySensorFilterDescriptor) { EntitySensorFilterDescriptor filter = (EntitySensorFilterDescriptor) gen; filterClass = filter.entityClass; } } if (element.filterIsSpare) { if (filterClass != null && filterClass.isAssignableFrom(target.getClass())) return new SeekingState(); } else { if (filterClass == null || !filterClass.isAssignableFrom(target.getClass())) return new SeekingState(); } Coordonate coord = element.coordonate(); double dx = (float) (target.posX - coord.x - 0.5); double dy = (float) (target.posY + target.getEyeHeight() - coord.y - 0.75); double dz = (float) (target.posZ - coord.z - 0.5); double entityAngle = -Math.toDegrees(Math.atan2(dz, dx)); switch (element.front) { case XN: if (entityAngle > 0) entityAngle -= 180; else entityAngle += 180; break; case ZP: entityAngle += 90; break; case ZN: entityAngle -= 90; break; default: break; } double entityAngle2 = -Math.toDegrees(Math.asin(dy / Math.sqrt(dx * dx + dz * dz))); if (Math.abs(entityAngle) > element.getDescriptor().getProperties().actionAngle) return new SeekingState(); element.setTurretAngle((float) entityAngle); element.setGunElevation((float) -entityAngle2); if (Math.abs(target.posX - coord.x) > element.getDescriptor().getProperties().aimDistance || Math.abs(target.posZ - coord.z) > element.getDescriptor().getProperties().aimDistance) return new SeekingState(); List<Block> blockList = Utils.traceRay(coord.world(), coord.x + 0.5, coord.y + 0.5, coord.z + 0.5, target.posX, target.posY + target.getEyeHeight(), target.posZ); for (Block b : blockList) if (b.isOpaqueCube()) return new SeekingState(); if (element.getGunPosition() == 1 && element.isTargetReached() && element.energyBuffer >= element.getDescriptor().getProperties().impulseEnergy) return new ShootState(target); return this; } @Override public void leave() { } } class ShootState implements State { public ShootState(EntityLivingBase target) { this.target = target; } private final EntityLivingBase target; @Override public void enter() { if (target != null) target.attackEntityFrom(new DamageSource("Unknown"), 5); element.shoot(); element.play(new SoundCommand("eln:LaserGun")); } @Override public State state(double time) { if (target == null || target.isDead) return new SeekingState(); else return new AimingState(target); } @Override public void leave() { element.energyBuffer = 0; } } @Override public void process(double time) { double MaximalEnergy = element.getDescriptor().getProperties().impulseEnergy; element.energyBuffer += element.powerResistor.getP() * time; boolean full = element.energyBuffer > MaximalEnergy; if (full) { element.energyBuffer = MaximalEnergy; } if (element.coordonate().getBlockExist()) super.process(time); if (actualPower == 0 || full) element.powerResistor.highImpedance(); else element.powerResistor.setR(element.load.getU() * element.load.getU() / actualPower); } }