org.jtrfp.trcl.flow.Mission.java Source code

Java tutorial

Introduction

Here is the source code for org.jtrfp.trcl.flow.Mission.java

Source

/*******************************************************************************
 * 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.flow;

import java.awt.Point;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.jtrfp.trcl.Camera;
import org.jtrfp.trcl.NAVSystem;
import org.jtrfp.trcl.OverworldSystem;
import org.jtrfp.trcl.SkySystem;
import org.jtrfp.trcl.Tunnel;
import org.jtrfp.trcl.World;
import org.jtrfp.trcl.beh.Behavior;
import org.jtrfp.trcl.beh.CollidesWithTerrain;
import org.jtrfp.trcl.beh.CollidesWithTunnelWalls;
import org.jtrfp.trcl.beh.HeadingXAlwaysPositiveBehavior;
import org.jtrfp.trcl.beh.LoopingPositionBehavior;
import org.jtrfp.trcl.beh.MatchDirection;
import org.jtrfp.trcl.beh.MatchPosition;
import org.jtrfp.trcl.beh.SkyCubeCloudModeUpdateBehavior;
import org.jtrfp.trcl.beh.phy.MovesByVelocity;
import org.jtrfp.trcl.core.Renderer;
import org.jtrfp.trcl.core.ResourceManager;
import org.jtrfp.trcl.core.TR;
import org.jtrfp.trcl.file.AbstractTriplet;
import org.jtrfp.trcl.file.DirectionVector;
import org.jtrfp.trcl.file.LVLFile;
import org.jtrfp.trcl.file.Location3D;
import org.jtrfp.trcl.file.NAVFile.NAVSubObject;
import org.jtrfp.trcl.file.NAVFile.START;
import org.jtrfp.trcl.file.TDFFile;
import org.jtrfp.trcl.flow.LoadingProgressReporter.UpdateHandler;
import org.jtrfp.trcl.flow.NAVObjective.Factory;
import org.jtrfp.trcl.obj.ObjectDirection;
import org.jtrfp.trcl.obj.Player;
import org.jtrfp.trcl.obj.Projectile;
import org.jtrfp.trcl.obj.ProjectileFactory;
import org.jtrfp.trcl.obj.Propelled;
import org.jtrfp.trcl.obj.TunnelEntranceObject;
import org.jtrfp.trcl.obj.WorldObject;
import org.jtrfp.trcl.snd.GPUResidentMOD;
import org.jtrfp.trcl.snd.MusicPlaybackEvent;
import org.jtrfp.trcl.snd.SoundSystem;

public class Mission {
    // PROPERTIES
    public static final String MISSION_MODE = "missionMode";
    public static final String SATELLITE_VIEW = "satelliteView";

    private final TR tr;
    private final List<NAVObjective> navs = new LinkedList<NAVObjective>();
    private final LVLFile lvl;
    private final HashMap<String, Tunnel> tunnels = new HashMap<String, Tunnel>();
    private double[] playerStartPosition = new double[3];
    private List<NAVSubObject> navSubObjects;
    private ObjectDirection playerStartDirection;
    private final Game game;
    private final String levelName;
    private OverworldSystem overworldSystem;
    private final Result[] missionEnd = new Result[] { null };
    private int groundTargetsDestroyed = 0, airTargetsDestroyed = 0, foliageDestroyed = 0;
    private int totalNumTunnels;
    private final LinkedList<Tunnel> tunnelsRemaining = new LinkedList<Tunnel>();
    private final boolean showIntro;
    private volatile MusicPlaybackEvent bgMusic;
    private final Object missionLock = new Object();
    private final Map<Integer, TunnelEntranceObject> tunnelMap = new HashMap<Integer, TunnelEntranceObject>();
    private boolean bossFight = false, satelliteView = false;
    private MissionMode missionMode = new Mission.LoadingMode();
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private Tunnel currentTunnel;

    private enum LoadingStages {
        navs, tunnels, overworld
    }// end LoadingStages

    public Mission(TR tr, Game game, LVLFile lvl, String levelName, boolean showIntro) {
        this.tr = tr;
        this.lvl = lvl;
        this.game = game;
        this.levelName = levelName;
        this.showIntro = showIntro;
    }// end Mission
    /*
    public TRFutureTask<Result> go(){
    synchronized(missionTask){
    missionTask[0] = tr.getThreadManager().submitToThreadPool(new Callable<Result>(){
       @Override
       public Result call() throws Exception {
      return _go();
       }});
    }//end sync{}
    return missionTask[0];
    }//end go()
    */

    public Result go() {
        setMissionMode(new Mission.LoadingMode());
        synchronized (missionLock) {
            synchronized (missionEnd) {
                if (missionEnd[0] != null)
                    return missionEnd[0];
            }
            tr.getThreadManager().setPaused(true);
            for (ProjectileFactory pf : tr.getResourceManager().getProjectileFactories())
                for (Projectile proj : pf.getProjectiles())
                    proj.destroy();
            System.out.println("Starting GampeplayLevel loading sequence...");
            final LoadingProgressReporter rootProgress = LoadingProgressReporter.Impl
                    .createRoot(new UpdateHandler() {
                        @Override
                        public void update(double unitProgress) {
                            game.getLevelLoadingScreen().setLoadingProgress(unitProgress);
                        }
                    });
            final LoadingProgressReporter[] progressStages = rootProgress
                    .generateSubReporters(LoadingStages.values().length);
            final Renderer renderer = tr.mainRenderer.get();
            renderer.getCamera().probeForBehavior(SkyCubeCloudModeUpdateBehavior.class).setEnable(false);
            renderer.getSkyCube().setSkyCubeGen(GameShell.DEFAULT_GRADIENT);
            final Camera camera = renderer.getCamera();
            camera.setHeading(Vector3D.PLUS_I);
            camera.setTop(Vector3D.PLUS_J);
            game.setDisplayMode(game.levelLoadingMode);
            game.getUpfrontDisplay().submitPersistentMessage(levelName);
            try {
                final ResourceManager rm = tr.getResourceManager();
                final Player player = tr.getGame().getPlayer();
                final World world = tr.getWorld();
                final TDFFile tdf = rm.getTDFData(lvl.getTunnelDefinitionFile());
                player.setActive(false);
                // Abort check
                synchronized (missionEnd) {
                    if (missionEnd[0] != null)
                        return missionEnd[0];
                }

                overworldSystem = new OverworldSystem(tr, progressStages[LoadingStages.overworld.ordinal()]);
                getOverworldSystem().loadLevel(lvl, tdf);
                System.out.println("\t...Done.");
                // Install NAVs
                final NAVSystem navSystem = tr.getGame().getNavSystem();
                navSubObjects = rm.getNAVData(lvl.getNavigationFile()).getNavObjects();

                START s = (START) navSubObjects.get(0);
                Location3D l3d = s.getLocationOnMap();
                playerStartPosition[0] = TR.legacy2Modern(l3d.getZ());
                playerStartPosition[2] = TR.legacy2Modern(l3d.getX());
                final double HEIGHT_PADDING = 10000;
                playerStartPosition[1] = Math.max(
                        HEIGHT_PADDING + (world.sizeY / 2) * getOverworldSystem().getAltitudeMap()
                                .heightAt(TR.legacy2MapSquare(l3d.getZ()), TR.legacy2MapSquare(l3d.getX())),
                        TR.legacy2Modern(l3d.getY()));
                playerStartDirection = new ObjectDirection(s.getRoll(), s.getPitch(), s.getYaw());
                // ////// INITIAL HEADING
                player.setPosition(getPlayerStartPosition());
                player.setDirection(getPlayerStartDirection());
                player.setHeading(player.getHeading().negate());// Kludge to fix
                // incorrect heading
                ///////// STATE
                final Propelled propelled = player.probeForBehavior(Propelled.class);
                propelled.setPropulsion(propelled.getMinPropulsion());

                installTunnels(tdf, progressStages[LoadingStages.tunnels.ordinal()]);
                Factory f = new NAVObjective.Factory(tr);

                final LoadingProgressReporter[] navProgress = progressStages[LoadingStages.navs.ordinal()]
                        .generateSubReporters(navSubObjects.size());
                for (int i = 0; i < navSubObjects.size(); i++) {
                    final NAVSubObject obj = navSubObjects.get(i);
                    f.create(tr, obj, navs);
                    navProgress[i].complete();
                } // end for(navSubObjects)
                navSystem.updateNAVState();
                player.resetVelocityRotMomentum();
                final String startX = System.getProperty("org.jtrfp.trcl.startX");
                final String startY = System.getProperty("org.jtrfp.trcl.startY");
                final String startZ = System.getProperty("org.jtrfp.trcl.startZ");
                final double[] playerPos = player.getPosition();
                if (startX != null && startY != null && startZ != null) {
                    System.out.println("Using user-specified start point");
                    final int sX = Integer.parseInt(startX);
                    final int sY = Integer.parseInt(startY);
                    final int sZ = Integer.parseInt(startZ);
                    playerPos[0] = sX;
                    playerPos[1] = sY;
                    playerPos[2] = sZ;
                    player.notifyPositionChange();
                } // end if(user start point)
                System.out.println("Start position set to " + player.getPosition()[0] + " "
                        + player.getPosition()[1] + " " + player.getPosition()[2]);
                System.out.println("Setting sun vector");
                final AbstractTriplet sunVector = lvl.getSunlightDirectionVector();
                tr.getThreadManager().submitToGL(new Callable<Void>() {
                    @Override
                    public Void call() throws Exception {
                        tr.mainRenderer.get().setSunVector(
                                new Vector3D(sunVector.getX(), sunVector.getY(), sunVector.getZ()).normalize());
                        return null;
                    }
                }).get();
                System.out.println("\t...Done.");
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (System.getProperties().containsKey("org.jtrfp.trcl.flow.Mission.skipNavs")) {
                try {
                    final int skips = Integer.parseInt(System.getProperty("org.jtrfp.trcl.flow.Mission.skipNavs"));
                    System.out.println("Skipping " + skips + " navs.");
                    for (int i = 0; i < skips; i++) {
                        removeNAVObjective(currentNAVObjective());
                    } // end for(skips)
                } catch (NumberFormatException e) {
                    System.err.println(
                            "Invalid format for property \"org.jtrfp.trcl.flow.Mission.skipNavs\". Must be integer.");
                }
            } // end if(containsKey)
              //System.out.println("Invoking JVM's garbage collector...");
              //TR.nuclearGC();
              //System.out.println("Mission.go() complete.");
              // Transition to gameplay mode.
              // Abort check
            synchronized (missionEnd) {
                if (missionEnd[0] != null)
                    return missionEnd[0];
            } //end sync(missionEnd)
            tr.getThreadManager().submitToThreadPool(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    final SoundSystem ss = Mission.this.tr.soundSystem.get();
                    MusicPlaybackEvent evt;
                    Mission.this.tr.soundSystem.get().enqueuePlaybackEvent(evt = ss.getMusicFactory().create(
                            new GPUResidentMOD(tr, tr.getResourceManager().getMOD(lvl.getBackgroundMusicFile())),
                            true));
                    synchronized (Mission.this) {
                        if (bgMusic != null)
                            return null;
                        bgMusic = evt;
                        bgMusic.play();
                    } //end sync(Mission.this)
                    return null;
                }// end call()
            });
            game.getUpfrontDisplay().removePersistentMessage();
            tr.getThreadManager().setPaused(false);
            if (showIntro) {
                setMissionMode(new Mission.IntroMode());
                game.getBriefingScreen().briefingSequence(lvl);
            }
            setMissionMode(new Mission.AboveGroundMode());
            getOverworldSystem().activate();
            final SkySystem skySystem = getOverworldSystem().getSkySystem();
            tr.mainRenderer.get().getCamera().probeForBehavior(SkyCubeCloudModeUpdateBehavior.class)
                    .setEnable(true);
            renderer.getSkyCube().setSkyCubeGen(skySystem.getBelowCloudsSkyCubeGen());
            renderer.setAmbientLight(skySystem.getSuggestedAmbientLight());
            renderer.setSunColor(skySystem.getSuggestedSunColor());
            game.getNavSystem().activate();
            game.setDisplayMode(game.gameplayMode);

            game.getPlayer().setActive(true);
            tr.getGame().setPaused(false);
            //Wait for mission end
            synchronized (missionEnd) {
                while (missionEnd[0] == null) {
                    try {
                        missionEnd.wait();
                    } catch (InterruptedException e) {
                        break;
                    }
                }
            }
            //Completion summary
            if (missionEnd[0] != null)
                if (!missionEnd[0].isAbort()) {
                    setMissionMode(new Mission.MissionSummaryMode());
                    game.getBriefingScreen().missionCompleteSummary(lvl, missionEnd[0]);
                } //end if(proper ending)
            tr.getThreadManager().submitToThreadPool(new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    bgMusic.stop();
                    return null;
                }// end call()
            });
            cleanup();
            return missionEnd[0];
        } //end sync
    }// end go()

    public NAVObjective currentNAVObjective() {
        if (navs.isEmpty())
            return null;
        return navs.get(0);
    }//end currentNAVObjective()

    public void removeNAVObjective(NAVObjective o) {
        navs.remove(o);
        if (navs.size() == 0) {
            missionCompleteSequence();
        } else
            tr.getGame().getNavSystem().updateNAVState();
    }// end removeNAVObjective(...)

    public static class Result {
        private final int airTargetsDestroyed, groundTargetsDestroyed, foliageDestroyed;
        private final double tunnelsFoundPctNorm;
        private boolean abort = false;

        public Result(int airTargetsDestroyed, int groundTargetsDestroyed, int foliageDestroyed,
                double tunnelsFoundPctNorm) {
            this.airTargetsDestroyed = airTargetsDestroyed;
            this.groundTargetsDestroyed = groundTargetsDestroyed;
            this.foliageDestroyed = foliageDestroyed;
            this.tunnelsFoundPctNorm = tunnelsFoundPctNorm;
        }//end constructor

        /**
         * @return the airTargetsDestroyed
         */
        public int getAirTargetsDestroyed() {
            return airTargetsDestroyed;
        }

        /**
         * @return the groundTargetsDestroyed
         */
        public int getGroundTargetsDestroyed() {
            return groundTargetsDestroyed;
        }

        /**
         * @return the foliageDestroyed
         */
        public int getFoliageDestroyed() {
            return foliageDestroyed;
        }

        /**
         * @return the tunnelsFoundPctNorm
         */
        public double getTunnelsFoundPctNorm() {
            return tunnelsFoundPctNorm;
        }

        /**
         * @return the abort
         */
        public boolean isAbort() {
            return abort;
        }

        /**
         * @param abort the abort to set
         */
        public void setAbort(boolean abort) {
            this.abort = abort;
        }
    }// end Result

    /**
     * @return the playerStartPosition
     */
    public double[] getPlayerStartPosition() {
        return playerStartPosition;
    }

    /**
     * @return the playerStartDirection
     */
    public ObjectDirection getPlayerStartDirection() {
        return playerStartDirection;
    }

    private void installTunnels(TDFFile tdf, LoadingProgressReporter reporter) {
        TDFFile.Tunnel[] tuns = tdf.getTunnels();
        tuns = tuns == null ? new TDFFile.Tunnel[0] : tuns;//Null means no tunnels.
        final LoadingProgressReporter[] reporters = reporter.generateSubReporters(tuns.length);
        if (tuns != null) {
            int tIndex = 0;
            // Build tunnels
            for (TDFFile.Tunnel tun : tuns) {
                tr.getReporter().report("org.jtrfp.trcl.TunnelInstaller.tunnel." + tIndex + ".entrance",
                        tun.getEntrance());
                tr.getReporter().report("org.jtrfp.trcl.TunnelInstaller.tunnel." + tIndex + ".exit", tun.getExit());
                newTunnel(tun, reporters[tIndex]);
                tIndex++;
            } //end if(tuns!=null)
        } // end if(tuns!=null)
        totalNumTunnels = tunnelsRemaining.size();
    }//end installTunnels()

    private Tunnel newTunnel(org.jtrfp.trcl.file.TDFFile.Tunnel tun, LoadingProgressReporter reporter) {
        final Tunnel result = new Tunnel(tr, tun, reporter);
        DirectionVector v = tun.getEntrance();
        tunnelsRemaining.add(result);
        addTunnelEntrance(new Point((int) (TR.legacy2MapSquare(v.getZ())), (int) (TR.legacy2MapSquare(v.getX()))),
                result);
        tunnels.put(tun.getTunnelLVLFile().toUpperCase(), result);
        return result;
    }

    public Tunnel getTunnelByFileName(String tunnelFileName) {
        return tunnels.get(tunnelFileName.toUpperCase());
    }

    public TunnelEntranceObject getNearestTunnelEntrance(double xInLegacyUnits, double yInLegacyUnits,
            double zInLegacyUnits) {
        TunnelEntranceObject result = null;
        double closestDistance = Double.POSITIVE_INFINITY;
        final Vector3D entPos = new Vector3D(TR.legacy2Modern(zInLegacyUnits), //Intentionally backwards
                TR.legacy2Modern(yInLegacyUnits), TR.legacy2Modern(xInLegacyUnits));
        System.out.println("Requested entry pos=" + entPos);
        for (TunnelEntranceObject teo : tunnelMap.values()) {
            final Vector3D pos = new Vector3D(teo.getPosition());
            System.out.println("Found tunnel at " + pos);
            final double distance = pos.distance(entPos);
            if (distance < closestDistance) {
                closestDistance = distance;
                result = teo;
            }
        } // end for(tunnels)
        return result;
    }// end getTunnelWhoseEntranceClosestTo(...)

    private void missionCompleteSequence() {
        new Thread() {
            @Override
            public void run() {
                // TODO: Behavior change: Camera XZ static, lag Y by ~16
                // squares, heading/top affix toward player
                // TODO: Turn off all player control behavior
                // TODO: Behavior change: Player turns upward, top rolls on
                // heading, speed at full throttle
                // TODO: Wait 3 seconds
                // TODO: Lightning shell on
                // TODO: Wait 1 second
                // TODO: Turbo forward
                // TODO: Wait 500ms
                // TODO: Jet thrust noise
                // TODO: Player invisible.
                System.out.println("MISSION COMPLETE.");
                notifyMissionEnd(new Result(airTargetsDestroyed, groundTargetsDestroyed, foliageDestroyed,
                        1. - (double) tunnelsRemaining.size() / (double) totalNumTunnels));
            }// end run()
        }.start();
    }//end missionCompleteSequence()

    public void playerDestroyed() {
        new Thread() {
            @Override
            public void run() {
                // TODO Behavior change: Camera XYZ static, heading/top affix
                // toward player
                // TODO: Turn off all player control behavior
                // TODO Player behavior change: Slow spin along heading axis,
                // slow downward drift of heading
                // TODO: Add behavior: explode and destroy on impact with ground
                System.out.println("MISSION FAILED.");
                notifyMissionEnd(null);
            }// end run()
        }.start();
    }// end playerDestroyed()

    private void notifyMissionEnd(Result r) {
        synchronized (missionEnd) {
            missionEnd[0] = r;
            missionEnd.notifyAll();
        }
    }//end notifyMissionEnd()

    public List<NAVObjective> getRemainingNAVObjectives() {
        return navs;
    }

    /**
     * @return the navSubObjects
     */
    public List<NAVSubObject> getNavSubObjects() {
        return navSubObjects;
    }

    /**
     * @param navSubObjects
     *            the navSubObjects to set
     */
    public void setNavSubObjects(List<NAVSubObject> navSubObjects) {
        this.navSubObjects = navSubObjects;
    }

    public void missionComplete() {
        missionCompleteSequence();
    }

    public OverworldSystem getOverworldSystem() {
        return overworldSystem;
    }

    public Mission notifyAirTargetDestroyed() {
        airTargetsDestroyed++;
        return this;
    }

    public Mission notifyGroundTargetDestroyed() {
        groundTargetsDestroyed++;
        return this;
    }

    public Mission notifyTunnelFound(Tunnel tun) {
        tunnelsRemaining.remove(tun);
        return this;
    }

    public Mission notifyFoliageDestroyed() {
        foliageDestroyed++;
        return this;
    }

    public void enterBossMode(final String bossMusicFile) {
        setBossFight(true);
        tr.getThreadManager().submitToThreadPool(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                MusicPlaybackEvent evt;
                final SoundSystem ss = Mission.this.tr.soundSystem.get();
                Mission.this.tr.soundSystem.get().enqueuePlaybackEvent(evt = ss.getMusicFactory()
                        .create(tr.getResourceManager().gpuResidentMODs.get(bossMusicFile), true));
                synchronized (Mission.this) {
                    evt.play();
                    if (bgMusic != null)
                        bgMusic.stop();
                    bgMusic = evt;
                }
                return null;
            }// end call()
        });
    }//end enterBossMode()

    public void exitBossMode() {
        setBossFight(false);
        tr.getThreadManager().submitToThreadPool(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                MusicPlaybackEvent evt;
                final SoundSystem ss = Mission.this.tr.soundSystem.get();
                Mission.this.tr.soundSystem.get().enqueuePlaybackEvent(evt = ss.getMusicFactory()
                        .create(tr.getResourceManager().gpuResidentMODs.get(lvl.getBackgroundMusicFile()), true));
                synchronized (Mission.this) {
                    evt.play();
                    bgMusic.stop();
                    bgMusic = evt;
                }
                return null;
            }// end call()
        });
    }//end exitBossMode()

    public void abort() {
        final Result result = new Result(airTargetsDestroyed, groundTargetsDestroyed, foliageDestroyed,
                1. - (double) tunnelsRemaining.size() / (double) totalNumTunnels);
        result.setAbort(true);
        notifyMissionEnd(result);
        //Wait for mission to end
        synchronized (missionLock) {//Don't execute while mission is in progress.
            cleanup();
        } //end sync{}
    }//end abort()

    private void cleanup() {
        if (overworldSystem != null)
            overworldSystem.deactivate();
    }

    /**
     * Find a tunnel at the given map square, if any.
     * @param mapSquareXZ Position in cells, not world coords.
     * @return The Tunnel at this map square, or null if none here.
     * @since Jan 13, 2015
     */
    public TunnelEntranceObject getTunnelEntranceObject(Point mapSquareXZ) {
        final int key = pointToHash(mapSquareXZ);
        System.out.println("getTunnelEntranceObject " + mapSquareXZ);
        for (TunnelEntranceObject teo : tunnelMap.values())
            System.out.print(" " + new Vector3D(teo.getPosition()).scalarMultiply(1 / TR.mapSquareSize));
        System.out.println();
        return tunnelMap.get(key);
    }

    public void addTunnelEntrance(Point mapSquareXZ, Tunnel tunnel) {
        TunnelEntranceObject teo;
        overworldSystem.add(teo = new TunnelEntranceObject(tr, tunnel));
        tunnelMap.put(pointToHash(mapSquareXZ), teo);
    }

    private int pointToHash(Point point) {
        final int key = (int) point.getX() + (int) point.getY() * 65536;
        return key;
    }

    public void enterTunnel(Tunnel tunnel) {
        final Game game = tr.getGame();
        final OverworldSystem overworldSystem = game.getCurrentMission().getOverworldSystem();
        currentTunnel = tunnel;
        game.getCurrentMission().notifyTunnelFound(tunnel);
        setMissionMode(new TunnelMode());
        //Turn off overworld
        overworldSystem.deactivate();
        //Turn on tunnel
        tunnel.activate();
        //Move player to tunnel
        tr.mainRenderer.get().getSkyCube().setSkyCubeGen(Tunnel.TUNNEL_SKYCUBE_GEN);
        //Ensure chamber mode is off
        overworldSystem.setChamberMode(false);
        overworldSystem.setTunnelMode(true);
        //Update debug data
        tr.getReporter().report("org.jtrfp.Tunnel.isInTunnel?", "true");

        final ProjectileFactory[] pfs = tr.getResourceManager().getProjectileFactories();
        for (ProjectileFactory pf : pfs) {
            Projectile[] projectiles = pf.getProjectiles();
            for (Projectile proj : projectiles) {
                ((WorldObject) proj).getBehavior().probeForBehavior(LoopingPositionBehavior.class).setEnable(false);
            } //end for(projectiles)
        } //end for(projectileFactories)
        final Player player = tr.getGame().getPlayer();
        player.setActive(false);
        player.resetVelocityRotMomentum();
        final Behavior playerBehavior = player.getBehavior();
        playerBehavior.probeForBehavior(CollidesWithTunnelWalls.class).setEnable(true);
        playerBehavior.probeForBehavior(MovesByVelocity.class).setVelocity(Vector3D.ZERO);
        playerBehavior.probeForBehavior(LoopingPositionBehavior.class).setEnable(false);
        playerBehavior.probeForBehavior(HeadingXAlwaysPositiveBehavior.class).setEnable(true);
        playerBehavior.probeForBehavior(CollidesWithTerrain.class).setEnable(false);
        //entranceObject.getBehavior().probeForBehaviors(TELsubmitter, TunnelEntryListener.class);
        tunnel.dispatchTunnelEntryNotifications();
        player.setPosition(Tunnel.TUNNEL_START_POS.toArray());
        player.setDirection(Tunnel.TUNNEL_START_DIRECTION);
        player.notifyPositionChange();
        /*
        final NAVObjective navObjective = getNavObjectiveToRemove();
        if(navObjective!=null && navTargeted){
            final Mission m = game.getCurrentMission();
            if(!(onlyRemoveIfCurrent&&navObjective!=m.currentNAVObjective()))m.removeNAVObjective(navObjective);
        }//end if(have NAV to remove
        */
        player.setActive(true);
    }//end enterTunnel()

    /**
     * @param listener
     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    /**
     * @param propertyName
     * @param listener
     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
     */
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(propertyName, listener);
    }

    /**
     * @return
     * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
     */
    public PropertyChangeListener[] getPropertyChangeListeners() {
        return pcs.getPropertyChangeListeners();
    }

    /**
     * @param propertyName
     * @return
     * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners(java.lang.String)
     */
    public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
        return pcs.getPropertyChangeListeners(propertyName);
    }

    /**
     * @param propertyName
     * @return
     * @see java.beans.PropertyChangeSupport#hasListeners(java.lang.String)
     */
    public boolean hasListeners(String propertyName) {
        return pcs.hasListeners(propertyName);
    }

    /**
     * @param listener
     * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    /**
     * @param propertyName
     * @param listener
     * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String, java.beans.PropertyChangeListener)
     */
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(propertyName, listener);
    }

    public static interface MissionMode {
    }

    public static class LoadingMode implements MissionMode {
    }

    public static class BriefingMode implements MissionMode {
    }

    public static class IntroMode extends BriefingMode {
    }

    public static class EnemyIntroMode extends IntroMode {
    }

    public static class PlanetIntroMode extends IntroMode {
    }

    public static class MissionSummaryMode extends BriefingMode {
    }

    public static class GameplayMode implements MissionMode {
    }

    public static class TunnelMode extends GameplayMode {
    }

    public static class ChamberMode extends GameplayMode {
    }

    public static class AboveGroundMode extends GameplayMode {
    }

    /**
     * @return the missionMode
     */
    public MissionMode getMissionMode() {
        return missionMode;
    }

    /**
     * @param missionMode the missionMode to set
     */
    public void setMissionMode(MissionMode missionMode) {
        pcs.firePropertyChange(MISSION_MODE, this.missionMode, missionMode);
        this.missionMode = missionMode;
    }

    /**
     * @return the bossFight
     */
    public boolean isBossFight() {
        return bossFight;
    }

    /**
     * @param bossFight the bossFight to set
     */
    public void setBossFight(boolean bossFight) {
        pcs.firePropertyChange("bossFight", this.bossFight, bossFight);
        this.bossFight = bossFight;
    }

    public void setSatelliteView(boolean satelliteView) {
        if (!(getMissionMode() instanceof AboveGroundMode) && satelliteView)
            throw new IllegalArgumentException("Cannot activate satellite view while mission mode is "
                    + getMissionMode().getClass().getSimpleName());
        if (satelliteView && tr.getGame().isPaused())
            throw new IllegalArgumentException("Cannot activate satellite view while paused.");
        pcs.firePropertyChange(SATELLITE_VIEW, this.satelliteView, satelliteView);
        if (satelliteView != this.satelliteView) {
            final Game game = tr.getGame();
            final Camera cam = tr.mainRenderer.get().getCamera();
            if (satelliteView) {//Switched on
                tr.getThreadManager().setPaused(true);
                game.getNavSystem().deactivate();
                game.getHUDSystem().deactivate();
                cam.setFogEnabled(false);
                cam.probeForBehavior(MatchPosition.class).setEnable(false);
                cam.probeForBehavior(MatchDirection.class).setEnable(false);
                final Vector3D pPos = new Vector3D(game.getPlayer().getPosition());
                final Vector3D pHeading = tr.getGame().getPlayer().getHeading();
                cam.setPosition(new Vector3D(pPos.getX(), TR.mapSquareSize * 25, pPos.getZ()));
                cam.setHeading(Vector3D.MINUS_J);
                cam.setTop(new Vector3D(pHeading.getX(), .0000000001, pHeading.getZ()).normalize());
                tr.getGame().getSatDashboard().setVisible(true);
            } else {//Switched off
                tr.getThreadManager().setPaused(false);
                tr.getGame().getNavSystem().activate();
                game.getHUDSystem().activate();
                cam.setFogEnabled(true);
                cam.probeForBehavior(MatchPosition.class).setEnable(true);
                cam.probeForBehavior(MatchDirection.class).setEnable(true);
                tr.getGame().getSatDashboard().setVisible(false);
            } //end !satelliteView
        } //end if(change)
        this.satelliteView = satelliteView;
    }

    /**
     * @return the satelliteView
     */
    public boolean isSatelliteView() {
        System.out.println("isSatelliteView=" + satelliteView);
        return satelliteView;
    }

    public Tunnel getCurrentTunnel() {
        if (!(getMissionMode() instanceof TunnelMode))
            return null;
        return currentTunnel;
    }
}// end Mission