Java tutorial
/******************************************************************************* * This file is part of TERMINAL RECALL * Copyright (c) 2012-2014 Chuck Ritola * Part of the jTRFP.org project * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * chuck - initial API and implementation ******************************************************************************/ package org.jtrfp.trcl.obj; import java.util.Arrays; import org.apache.commons.math3.geometry.euclidean.threed.Vector3D; import org.jtrfp.trcl.beh.AdjustAltitudeToPlayerBehavior; import org.jtrfp.trcl.beh.AutoFiring; import org.jtrfp.trcl.beh.AutoLeveling; import org.jtrfp.trcl.beh.AutoLeveling.LevelingAxis; import org.jtrfp.trcl.beh.Behavior; import org.jtrfp.trcl.beh.Bobbing; import org.jtrfp.trcl.beh.CollidesWithTerrain; import org.jtrfp.trcl.beh.CustomDeathBehavior; import org.jtrfp.trcl.beh.CustomPlayerWithinRangeBehavior; import org.jtrfp.trcl.beh.DamageTrigger; import org.jtrfp.trcl.beh.DamageableBehavior; import org.jtrfp.trcl.beh.DamageableBehavior.SupplyNotNeededException; import org.jtrfp.trcl.beh.DamagedByCollisionWithGameplayObject; import org.jtrfp.trcl.beh.DamagedByCollisionWithSurface; import org.jtrfp.trcl.beh.DeathBehavior; import org.jtrfp.trcl.beh.DebrisOnDeathBehavior; import org.jtrfp.trcl.beh.ExplodesOnDeath; import org.jtrfp.trcl.beh.HorizAimAtPlayerBehavior; import org.jtrfp.trcl.beh.LeavesPowerupOnDeathBehavior; import org.jtrfp.trcl.beh.LoopingPositionBehavior; import org.jtrfp.trcl.beh.PositionLimit; import org.jtrfp.trcl.beh.ProjectileFiringBehavior; import org.jtrfp.trcl.beh.ResetsRandomlyAfterDeath; import org.jtrfp.trcl.beh.SmartPlaneBehavior; import org.jtrfp.trcl.beh.SpawnsRandomSmoke; import org.jtrfp.trcl.beh.SpinAccellerationBehavior; import org.jtrfp.trcl.beh.SpinAccellerationBehavior.SpinMode; import org.jtrfp.trcl.beh.SteadilyRotating; import org.jtrfp.trcl.beh.TerrainLocked; import org.jtrfp.trcl.beh.TunnelRailed; import org.jtrfp.trcl.beh.phy.AccelleratedByPropulsion; import org.jtrfp.trcl.beh.phy.HasPropulsion; import org.jtrfp.trcl.beh.phy.MovesByVelocity; import org.jtrfp.trcl.beh.phy.PulledDownByGravityBehavior; import org.jtrfp.trcl.beh.phy.RotationalDragBehavior; import org.jtrfp.trcl.beh.phy.RotationalMomentumBehavior; import org.jtrfp.trcl.beh.phy.VelocityDragBehavior; import org.jtrfp.trcl.core.TR; import org.jtrfp.trcl.file.DEFFile.EnemyDefinition; import org.jtrfp.trcl.file.DEFFile.EnemyDefinition.EnemyLogic; import org.jtrfp.trcl.file.DEFFile.EnemyPlacement; import org.jtrfp.trcl.gpu.Model; import org.jtrfp.trcl.obj.Explosion.ExplosionType; import org.jtrfp.trcl.snd.SoundSystem; public class DEFObject extends WorldObject { private final double boundingRadius; private WorldObject ruinObject; private final EnemyLogic logic; private final EnemyDefinition def; private boolean mobile, canTurn, foliage, boss, shieldGen, isRuin, spinCrash, ignoringProjectiles; private Anchoring anchoring; private static final String[] BIG_EXP_SOUNDS = new String[] { "EXP3.WAV", "EXP4.WAV", "EXP5.WAV" }; private static final String[] MED_EXP_SOUNDS = new String[] { "EXP1.WAV", "EXP2.WAV" }; public DEFObject(final TR tr, Model model, EnemyDefinition def, EnemyPlacement pl) { super(tr, model); this.def = def; boundingRadius = TR.legacy2Modern(def.getBoundingBoxRadius()) / 1.5; anchoring = Anchoring.floating; logic = def.getLogic(); mobile = true; canTurn = true; foliage = false; boss = def.isObjectIsBoss(); //Default Direction setDirection(new ObjectDirection(pl.getRoll(), pl.getPitch(), pl.getYaw() + 65536)); boolean customExplosion = false; this.setModelOffset(TR.legacy2Modern(def.getPivotX()), TR.legacy2Modern(def.getPivotY()), TR.legacy2Modern(def.getPivotZ())); switch (logic) { case groundDumb: mobile = false; canTurn = false; anchoring = Anchoring.terrain; break; case groundTargeting://Ground turrets { mobile = false; canTurn = true; addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); //TODO: def.getFiringVertices() needs actual vertex lookup. ProjectileFiringBehavior pfb; addBehavior(pfb = new ProjectileFiringBehavior() .setProjectileFactory( tr.getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]) .setFiringPositions(new Vector3D[] { new Vector3D(0, 0, 0) })); try { pfb.addSupply(9999999); } catch (SupplyNotNeededException e) { } addBehavior(new AutoFiring().setProjectileFiringBehavior(pfb) .setPatternOffsetMillis((int) (Math.random() * 2000)).setMaxFiringDistance(TR.mapSquareSize * 3) .setSmartFiring(false).setMaxFireVectorDeviation(.5).setTimePerPatternEntry(500)); anchoring = Anchoring.terrain; break; } case flyingDumb: canTurn = false; break; case groundTargetingDumb: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); anchoring = Anchoring.terrain; break; case flyingSmart: smartPlaneBehavior(tr, def, false); break; case bankSpinDrill: unhandled(def); break; case sphereBoss: projectileFiringBehavior(); mobile = true; break; case flyingAttackRetreatSmart: smartPlaneBehavior(tr, def, false); //addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); break; case splitShipSmart://TODO smartPlaneBehavior(tr, def, false); //addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); break; case groundStaticRuin://Destroyed object is replaced with another using SimpleModel i.e. weapons bunker mobile = false; canTurn = false; anchoring = Anchoring.terrain; break; case targetHeadingSmart: mobile = false;//Belazure's crane bots addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); anchoring = Anchoring.terrain; break; case targetPitchSmart: mobile = false; addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); anchoring = Anchoring.terrain; break; case coreBossSmart: mobile = false; projectileFiringBehavior(); break; case cityBossSmart: mobile = false; projectileFiringBehavior(); break; case staticFiringSmart: { //addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); final ProjectileFiringBehavior pfb = new ProjectileFiringBehavior(); try { pfb.addSupply(99999999); } catch (SupplyNotNeededException e) { } pfb.setProjectileFactory(tr.getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]); addBehavior(pfb); addBehavior(new AutoFiring().setProjectileFiringBehavior(pfb) .setPatternOffsetMillis((int) (Math.random() * 2000)).setMaxFiringDistance(TR.mapSquareSize * 8) .setSmartFiring(true)); mobile = false; canTurn = false; break; } case sittingDuck: canTurn = false; mobile = false; break; case tunnelAttack: { final ProjectileFiringBehavior pfb = new ProjectileFiringBehavior(); try { pfb.addSupply(99999999); } catch (SupplyNotNeededException e) { } pfb.setProjectileFactory(tr.getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]); addBehavior(pfb); //addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); addBehavior(new AutoFiring().setProjectileFiringBehavior(pfb) .setPatternOffsetMillis((int) (Math.random() * 2000)) .setMaxFiringDistance(TR.mapSquareSize * .2).setSmartFiring(false).setMaxFireVectorDeviation(.3) .setTimePerPatternEntry(2000)); /*addBehavior(new Bobbing(). setPhase(Math.random()). setBobPeriodMillis(10*1000+Math.random()*3000).setAmplitude(2000). setAdditionalHeight(0));*/ //Conflicts with TunnelRailed mobile = false; break; } case takeoffAndEscape: addBehavior(new MovesByVelocity()); addBehavior( (Behavior) (new HasPropulsion().setMinPropulsion(0).setPropulsion(def.getThrustSpeed() / 1.2))); addBehavior(new AccelleratedByPropulsion().setEnable(false)); addBehavior(new VelocityDragBehavior().setDragCoefficient(.86)); addBehavior(new CustomPlayerWithinRangeBehavior() { @Override public void withinRange() { DEFObject.this.getBehavior().probeForBehavior(AccelleratedByPropulsion.class) .setThrustVector(Vector3D.PLUS_J).setEnable(true); } }).setRange(TR.mapSquareSize * 10); addBehavior(new LoopingPositionBehavior()); addBehavior(new ExplodesOnDeath(ExplosionType.Blast, BIG_EXP_SOUNDS[(int) (Math.random() * 3)])); customExplosion = true; canTurn = false; mobile = false; break; case fallingAsteroid: anchoring = Anchoring.floating; fallingObjectBehavior(); customExplosion = true; addBehavior(new ExplodesOnDeath(ExplosionType.BigExplosion, MED_EXP_SOUNDS[(int) (Math.random() * 2)])); //setVisible(false); //addBehavior(new FallingDebrisBehavior(tr,model)); break; case cNome://Walky bot? anchoring = Anchoring.terrain; break; case cNomeLegs://Walky bot? anchoring = Anchoring.terrain; break; case cNomeFactory: mobile = false; break; case geigerBoss: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); anchoring = Anchoring.terrain; mobile = false; break; case volcanoBoss: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); anchoring = Anchoring.terrain; mobile = false; break; case volcano://Wat. unhandled(def); canTurn = false; mobile = false; anchoring = Anchoring.terrain; break; case missile://Silo? mobile = false;//TODO anchoring = Anchoring.terrain; break; case bob: addBehavior(new Bobbing().setAdditionalHeight(TR.mapSquareSize * 1)); addBehavior(new SteadilyRotating()); addBehavior(new ExplodesOnDeath(ExplosionType.Blast, MED_EXP_SOUNDS[(int) (Math.random() * 2)])); possibleBobbingSpinAndCrashOnDeath(.5, def); customExplosion = true; anchoring = Anchoring.floating; mobile = false; canTurn = false;//ironic? break; case alienBoss: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); mobile = false; break; case canyonBoss1: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); mobile = false; break; case canyonBoss2: addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); mobile = false; break; case lavaMan://Also terraform-o-bot addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); mobile = false; break; case arcticBoss: //ARTIC / Ymir. Hangs from ceiling. addBehavior(new HorizAimAtPlayerBehavior(tr.getGame().getPlayer())); projectileFiringBehavior(); mobile = false; anchoring = Anchoring.ceiling; break; case helicopter://TODO break; case tree: canTurn = false; mobile = false; foliage = true; anchoring = Anchoring.terrain; break; case ceilingStatic: canTurn = false; mobile = false; setTop(Vector3D.MINUS_J); anchoring = Anchoring.ceiling; break; case bobAndAttack: { addBehavior(new SteadilyRotating().setRotationPhase(2 * Math.PI * Math.random())); final ProjectileFiringBehavior pfb = new ProjectileFiringBehavior(); try { pfb.addSupply(99999999); } catch (SupplyNotNeededException e) { } pfb.setProjectileFactory(tr.getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]); addBehavior(pfb);//Bob and attack don't have the advantage of movement, so give them the advantage of range. addBehavior(new AutoFiring().setProjectileFiringBehavior(pfb) .setPatternOffsetMillis((int) (Math.random() * 2000)) .setMaxFiringDistance(TR.mapSquareSize * 17).setSmartFiring(true)); addBehavior(new Bobbing().setPhase(Math.random()).setBobPeriodMillis(10 * 1000 + Math.random() * 3000)); addBehavior(new ExplodesOnDeath(ExplosionType.Blast, BIG_EXP_SOUNDS[(int) (Math.random() * 3)])); possibleBobbingSpinAndCrashOnDeath(.5, def); customExplosion = true; mobile = false; canTurn = false; anchoring = Anchoring.floating; break; } case forwardDrive: canTurn = false; anchoring = Anchoring.terrain; break; case fallingStalag: fallingObjectBehavior(); customExplosion = true; addBehavior(new ExplodesOnDeath(ExplosionType.BigExplosion, MED_EXP_SOUNDS[(int) (Math.random() * 2)])); //canTurn=false; //mobile=false; anchoring = Anchoring.floating; break; case attackRetreatBelowSky: smartPlaneBehavior(tr, def, false); anchoring = Anchoring.floating; break; case attackRetreatAboveSky: smartPlaneBehavior(tr, def, true); anchoring = Anchoring.floating; break; case bobAboveSky: addBehavior(new Bobbing().setAdditionalHeight(TR.mapSquareSize * 5)); addBehavior(new SteadilyRotating()); possibleBobbingSpinAndCrashOnDeath(.5, def); mobile = false; canTurn = false; anchoring = Anchoring.floating; break; case factory: canTurn = false; mobile = false; anchoring = Anchoring.floating; break; }//end switch(logic) /////////////////////////////////////////////////////////// //Position Limit { final PositionLimit posLimit = new PositionLimit(); posLimit.getPositionMaxima()[1] = tr.getWorld().sizeY; posLimit.getPositionMinima()[1] = -tr.getWorld().sizeY; addBehavior(posLimit); } if (anchoring == Anchoring.terrain) { addBehavior(new CustomDeathBehavior(new Runnable() { @Override public void run() { tr.getGame().getCurrentMission().notifyGroundTargetDestroyed(); } })); addBehavior(new TerrainLocked()); } else if (anchoring == Anchoring.ceiling) { addBehavior(new TerrainLocked().setLockedToCeiling(true)); } else addBehavior(new CustomDeathBehavior(new Runnable() { @Override public void run() { tr.getGame().getCurrentMission().notifyAirTargetDestroyed(); }//end run() })); //Misc addBehavior(new TunnelRailed(tr));//Centers in tunnel when appropriate addBehavior(new DeathBehavior()); addBehavior(new DamageableBehavior().setHealth(pl.getStrength() + (spinCrash ? 16 : 0)) .setMaxHealth(pl.getStrength() + (spinCrash ? 16 : 0)).setEnable(!boss)); setActive(!boss); addBehavior(new DamagedByCollisionWithGameplayObject()); if (!foliage) addBehavior(new DebrisOnDeathBehavior()); else { addBehavior(new CustomDeathBehavior(new Runnable() { @Override public void run() { tr.getGame().getCurrentMission().notifyFoliageDestroyed(); } })); } if (canTurn || boss) { addBehavior(new RotationalMomentumBehavior()); addBehavior(new RotationalDragBehavior()).setDragCoefficient(.86); addBehavior(new AutoLeveling()); } if (foliage) { addBehavior(new ExplodesOnDeath(ExplosionType.Billow)); } else if ((!mobile || anchoring == Anchoring.terrain) && !customExplosion) { addBehavior(new ExplodesOnDeath(ExplosionType.BigExplosion, BIG_EXP_SOUNDS[(int) (Math.random() * 3)])); } else if (!customExplosion) { addBehavior(new ExplodesOnDeath(ExplosionType.Blast, MED_EXP_SOUNDS[(int) (Math.random() * 2)])); } if (mobile) { addBehavior(new MovesByVelocity()); addBehavior(new HasPropulsion()); addBehavior(new AccelleratedByPropulsion()); addBehavior(new VelocityDragBehavior()); if (anchoring == Anchoring.terrain) { } else {//addBehavior(new BouncesOffSurfaces().setReflectHeading(false)); addBehavior(new CollidesWithTerrain().setAutoNudge(true).setNudgePadding(40000)); } getBehavior().probeForBehavior(VelocityDragBehavior.class).setDragCoefficient(.86); getBehavior().probeForBehavior(Propelled.class).setMinPropulsion(0); getBehavior().probeForBehavior(Propelled.class).setPropulsion(def.getThrustSpeed() / 1.2); addBehavior(new LoopingPositionBehavior()); } //end if(mobile) if (def.getPowerup() != null && Math.random() * 100. < def.getPowerupProbability()) { addBehavior(new LeavesPowerupOnDeathBehavior(def.getPowerup())); } }//end DEFObject @Override public void destroy() { if (ruinObject != null) { //Give the ruinObject is own position because it is sharing positions with the original WorldObject, //which is going to be sent to xyz=Double.INFINITY soon. ruinObject.setPosition(Arrays.copyOf(ruinObject.getPosition(), 3)); ruinObject.setVisible(true); } super.destroy(); } private void projectileFiringBehavior() { ProjectileFiringBehavior pfb; addBehavior(pfb = new ProjectileFiringBehavior() .setProjectileFactory( getTr().getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]) .setFiringPositions(new Vector3D[] { new Vector3D(0, 0, 0) })); try { pfb.addSupply(99999999); } catch (SupplyNotNeededException e) { } final AutoFiring af; addBehavior(af = new AutoFiring().setProjectileFiringBehavior(pfb) .setPatternOffsetMillis((int) (Math.random() * 2000)).setMaxFiringDistance(TR.mapSquareSize * 5) .setSmartFiring(false).setMaxFireVectorDeviation(2.).setTimePerPatternEntry(!boss ? 2000 : 350)); if (boss) af.setFiringPattern(new boolean[] { true, true, true, true, false, false, true, false }) .setAimRandomness(.07); } private void unhandled(EnemyDefinition def) { System.err.println("UNHANDLED DEF LOGIC: " + def.getLogic() + ". MODEL=" + def.getComplexModelFile() + " DESC=" + def.getDescription()); } private void fallingObjectBehavior() { canTurn = false; mobile = false;//Technically wrong but propulsion is unneeded. //addBehavior(new PulledDownByGravityBehavior()); final MovesByVelocity mbv = new MovesByVelocity(); mbv.setVelocity(new Vector3D(3500, -100000, 5000)); addBehavior(mbv); //addBehavior(new VelocityDragBehavior().setDragCoefficient(.99)); // For some reason it falls like pine tar addBehavior(new DamageableBehavior().setMaxHealth(10).setHealth(10)); addBehavior(new DeathBehavior()); addBehavior(new CollidesWithTerrain().setIgnoreCeiling(true)); addBehavior(new DamagedByCollisionWithSurface()); addBehavior(new RotationalMomentumBehavior().setEquatorialMomentum(.01).setLateralMomentum(.02) .setPolarMomentum(.03)); { final DEFObject thisObject = this; final TR thisTr = getTr(); addBehavior(new ResetsRandomlyAfterDeath().setMinWaitMillis(1000).setMaxWaitMillis(5000) .setRunOnReset(new Runnable() { @Override public void run() { final Vector3D centerPos = thisObject.probeForBehavior(DeathBehavior.class) .getLocationOfLastDeath(); thisObject.probeForBehavior(MovesByVelocity.class) .setVelocity(new Vector3D(7000, -200000, 1000)); final double[] pos = thisObject.getPosition(); pos[0] = centerPos.getX() + Math.random() * TR.mapSquareSize * 3 - TR.mapSquareSize * 1.5; pos[1] = thisTr.getWorld().sizeY / 2 + thisTr.getWorld().sizeY * (Math.random()) * .3; pos[2] = centerPos.getZ() + Math.random() * TR.mapSquareSize * 3 - TR.mapSquareSize * 1.5; thisObject.notifyPositionChange(); }//end run() })); } } private void possibleSpinAndCrashOnDeath(double probability, final EnemyDefinition def) { spinCrash = Math.random() < probability; if (spinCrash) { final DamageTrigger spinAndCrash = new DamageTrigger() { @Override public void healthBelowThreshold() {// Spinout and crash final WorldObject parent = getParent(); final Behavior beh = parent.getBehavior(); //Trigger small boom final TR tr = parent.getTr(); tr.soundSystem.get().getPlaybackFactory().create( tr.getResourceManager().soundTextures.get("EXP2.WAV"), new double[] { .5 * SoundSystem.DEFAULT_SFX_VOLUME * 2, .5 * SoundSystem.DEFAULT_SFX_VOLUME * 2 }); addBehavior(new PulledDownByGravityBehavior().setEnable(true)); beh.probeForBehavior(DamagedByCollisionWithSurface.class).setEnable(true); beh.probeForBehavior(CollidesWithTerrain.class).setNudgePadding(0); beh.probeForBehavior(DamageableBehavior.class).setAcceptsProjectileDamage(false); beh.probeForBehavior(ExplodesOnDeath.class).setExplosionType(ExplosionType.BigExplosion) .setExplosionSound(BIG_EXP_SOUNDS[(int) (Math.random() * 3)]); if (def.getThrustSpeed() < 800000) { beh.probeForBehavior(HasPropulsion.class).setPropulsion(0); beh.probeForBehavior(VelocityDragBehavior.class).setEnable(false); } //Catastrophy final double spinSpeedCoeff = Math .max(def.getThrustSpeed() != 0 ? def.getThrustSpeed() / 1600000 : .3, .4); addBehavior(new SpinAccellerationBehavior().setSpinMode(SpinMode.LATERAL) .setSpinAccelleration(.009 * spinSpeedCoeff)); addBehavior(new SpinAccellerationBehavior().setSpinMode(SpinMode.EQUATORIAL) .setSpinAccelleration(.006 * spinSpeedCoeff)); addBehavior(new SpinAccellerationBehavior().setSpinMode(SpinMode.POLAR) .setSpinAccelleration(.007 * spinSpeedCoeff)); //TODO: Sparks, and other fun stuff. addBehavior(new SpawnsRandomExplosionsAndDebris(parent.getTr())); addBehavior(new SpawnsRandomSmoke(parent.getTr())); }//end healthBelowThreshold }.setThreshold(2048); addBehavior(new DamagedByCollisionWithSurface().setCollisionDamage(65535).setEnable(false)); addBehavior(spinAndCrash); } } private void possibleBobbingSpinAndCrashOnDeath(double probability, EnemyDefinition def) { possibleSpinAndCrashOnDeath(probability, def); if (spinCrash) { addBehavior(new CollidesWithTerrain()); addBehavior(new MovesByVelocity()).setEnable(false); addBehavior(new HasPropulsion()).setEnable(false); addBehavior(new AccelleratedByPropulsion()).setEnable(false); addBehavior(new VelocityDragBehavior()).setEnable(false); addBehavior(new RotationalMomentumBehavior()).setEnable(false); addBehavior(new RotationalDragBehavior()).setDragCoefficient(.86); final DamageTrigger spinAndCrashAddendum = new DamageTrigger() { @Override public void healthBelowThreshold() { final WorldObject parent = getParent(); parent.getBehavior().probeForBehavior(MovesByVelocity.class).setEnable(true); parent.getBehavior().probeForBehavior(HasPropulsion.class).setEnable(true); parent.getBehavior().probeForBehavior(AccelleratedByPropulsion.class).setEnable(true); parent.getBehavior().probeForBehavior(VelocityDragBehavior.class).setEnable(true); parent.getBehavior().probeForBehavior(RotationalMomentumBehavior.class).setEnable(true); parent.getBehavior().probeForBehavior(SteadilyRotating.class).setEnable(false); parent.getBehavior().probeForBehavior(Bobbing.class).setEnable(false); // parent.getBehavior().probeForBehavior(AutoFiring.class).setBerzerk(true) // .setFiringPattern(new boolean[]{true}).setTimePerPatternEntry(100); } }; addBehavior(spinAndCrashAddendum); } //end if(spinCrash) }//end possibleBobbingSpinAndCrashOnDeath private void smartPlaneBehavior(TR tr, EnemyDefinition def, boolean retreatAboveSky) { final HorizAimAtPlayerBehavior haapb = new HorizAimAtPlayerBehavior(tr.getGame().getPlayer()) .setLeftHanded(Math.random() >= .5); addBehavior(haapb); final AdjustAltitudeToPlayerBehavior aatpb = new AdjustAltitudeToPlayerBehavior(tr.getGame().getPlayer()) .setAccelleration(1000); addBehavior(aatpb); final ProjectileFiringBehavior pfb = new ProjectileFiringBehavior() .setProjectileFactory(tr.getResourceManager().getProjectileFactories()[def.getWeapon().ordinal()]); try { pfb.addSupply(99999999); } catch (SupplyNotNeededException e) { } addBehavior(pfb); possibleSpinAndCrashOnDeath(.4, def); if (spinCrash) { final DamageTrigger spinAndCrashAddendum = new DamageTrigger() { @Override public void healthBelowThreshold() { final WorldObject parent = getParent(); final Behavior beh = parent.getBehavior(); final HasPropulsion hp = beh.probeForBehavior(HasPropulsion.class); hp.setPropulsion(hp.getPropulsion() / 1); beh.probeForBehavior(AutoLeveling.class).setLevelingAxis(LevelingAxis.HEADING) .setLevelingVector(Vector3D.MINUS_J).setRetainmentCoeff(.985, .985, .985); } }; addBehavior(spinAndCrashAddendum); } //end if(spinCrash) AccelleratedByPropulsion escapeProp = null; if (retreatAboveSky) { escapeProp = new AccelleratedByPropulsion(); escapeProp.setThrustVector(new Vector3D(0, .1, 0)).setEnable(false); addBehavior(escapeProp); } final AutoFiring afb = new AutoFiring(); afb.setMaxFireVectorDeviation(.3); afb.setFiringPattern(new boolean[] { true, false, false, false, true, true, false }); afb.setTimePerPatternEntry((int) (400 + Math.random() * 300)); afb.setPatternOffsetMillis((int) (Math.random() * 1000)); afb.setProjectileFiringBehavior(pfb); addBehavior(afb); final SpinAccellerationBehavior sab = (SpinAccellerationBehavior) new SpinAccellerationBehavior() .setEnable(false); addBehavior(sab); addBehavior(new SmartPlaneBehavior(haapb, afb, sab, aatpb, escapeProp, retreatAboveSky)); } @Override public void setTop(Vector3D top) { super.setTop(top); } /** * @return the boundingRadius */ public double getBoundingRadius() { return boundingRadius; } public void setRuinObject(DEFObject ruin) { ruinObject = ruin; } /** * @return the logic */ public EnemyLogic getLogic() { return logic; } /** * @return the mobile */ public boolean isMobile() { return mobile; } /** * @return the canTurn */ public boolean isCanTurn() { return canTurn; } /** * @return the foliage */ public boolean isFoliage() { return foliage; } /** * @return the boss */ public boolean isBoss() { return boss; } /** * @return the groundLocked */ public boolean isGroundLocked() { return anchoring == Anchoring.terrain; } /** * @return the ignoringProjectiles */ public boolean isIgnoringProjectiles() { return ignoringProjectiles; } /** * @param ignoringProjectiles the ignoringProjectiles to set */ public void setIgnoringProjectiles(boolean ignoringProjectiles) { this.ignoringProjectiles = ignoringProjectiles; } public void setIsRuin(boolean b) { isRuin = b; } /** * @return the isRuin */ public boolean isRuin() { return isRuin; } /** * @param isRuin the isRuin to set */ public void setRuin(boolean isRuin) { this.isRuin = isRuin; } /** * @return the shieldGen */ public boolean isShieldGen() { return shieldGen; } /** * @param shieldGen the shieldGen to set */ public void setShieldGen(boolean shieldGen) { this.shieldGen = shieldGen; } @Override public String toString() { return "DEFObject Model=" + getModel().getDebugName() + " Logic=" + logic + " Anchoring=" + anchoring + "\n\tmobile=" + mobile + " isRuin=" + isRuin + " foliage=" + foliage + " boss=" + boss + " spinCrash=" + spinCrash + "\n\tignoringProjectiles=" + ignoringProjectiles + "\n" + "\tRuinObject=" + ruinObject; } enum Anchoring { floating(false), terrain(true), ceiling(true); private final boolean locked; private Anchoring(boolean locked) { this.locked = locked; } public boolean isLocked() { return locked; } }//end Anchoring }//end DEFObject