org.jtrfp.trcl.beh.CollidesWithTerrain.java Source code

Java tutorial

Introduction

Here is the source code for org.jtrfp.trcl.beh.CollidesWithTerrain.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.beh;

import java.awt.Point;
import java.util.Collection;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.jtrfp.trcl.InterpolatingAltitudeMap;
import org.jtrfp.trcl.OverworldSystem;
import org.jtrfp.trcl.Submitter;
import org.jtrfp.trcl.World;
import org.jtrfp.trcl.core.TR;
import org.jtrfp.trcl.flow.Mission;
import org.jtrfp.trcl.obj.TerrainChunk;
import org.jtrfp.trcl.obj.TunnelEntranceObject;
import org.jtrfp.trcl.obj.WorldObject;

public class CollidesWithTerrain extends Behavior {
    private boolean groundLock = false;
    private Vector3D surfaceNormalVar;
    public static final double CEILING_Y_NUDGE = -5000;
    private int tickCounter = 0;
    private boolean autoNudge = false;
    private double nudgePadding = 5000;
    private boolean recentlyCollided = false;
    private boolean tunnelEntryCapable = false;
    private boolean ignoreHeadingForImpact = true;
    private boolean ignoreCeiling = false;
    private static TerrainChunk dummyTerrainChunk;

    @Override
    public void _tick(long tickTimeMillis) {
        if (tickCounter++ % 2 == 0 && !recentlyCollided)
            return;
        recentlyCollided = false;
        final WorldObject p = getParent();
        final TR tr = p.getTr();
        final World world = tr.getWorld();
        final InterpolatingAltitudeMap aMap;
        final Mission mission = tr.getGame().getCurrentMission();
        try {
            aMap = mission.getOverworldSystem().getAltitudeMap();
        } catch (NullPointerException e) {
            return;
        }
        if (mission.getOverworldSystem().isTunnelMode())
            return;//No terrain to collide with while in tunnel mode.
        if (aMap == null)
            return;
        final double[] thisPos = p.getPosition();
        final double groundHeightNorm = aMap.heightAt((thisPos[0] / TR.mapSquareSize),
                (thisPos[2] / TR.mapSquareSize));
        final double groundHeight = groundHeightNorm * (world.sizeY / 2);
        final double ceilingHeight = (1.99
                - aMap.heightAt((thisPos[0] / TR.mapSquareSize), (thisPos[2] / TR.mapSquareSize)))
                * (world.sizeY / 2) + CEILING_Y_NUDGE;
        final Vector3D groundNormal = (aMap.normalAt((thisPos[0] / TR.mapSquareSize),
                (thisPos[2] / TR.mapSquareSize)));
        Vector3D downhillDirectionXZ = new Vector3D(groundNormal.getX(), 0, groundNormal.getZ());
        if (downhillDirectionXZ.getNorm() != 0)
            downhillDirectionXZ = downhillDirectionXZ.normalize();
        else
            downhillDirectionXZ = Vector3D.PLUS_J;
        final OverworldSystem overworldSystem = tr.getGame().getCurrentMission().getOverworldSystem();
        if (overworldSystem == null)
            return;
        final boolean terrainMirror = overworldSystem.isChamberMode();
        final double thisY = thisPos[1];
        boolean groundImpact = thisY < (groundHeight + (autoNudge ? nudgePadding : 0));
        final boolean ceilingImpact = (thisY > ceilingHeight && terrainMirror && !ignoreCeiling);
        final Vector3D ceilingNormal = new Vector3D(groundNormal.getX(), -groundNormal.getY(), groundNormal.getZ());
        Vector3D surfaceNormal = groundImpact ? groundNormal : ceilingNormal;
        final double dot = surfaceNormal.dotProduct(getParent().getHeading());
        if (terrainMirror && groundHeightNorm > .97) {
            groundImpact = true;
            surfaceNormal = downhillDirectionXZ;
        } //end if(smushed between floor and ceiling)

        if (groundLock) {
            recentlyCollided = true;
            thisPos[1] = groundHeight;
            p.notifyPositionChange();
            return;
        } //end if(groundLock)
        if (tunnelEntryCapable && groundImpact && dot < 0) {
            final OverworldSystem os = mission.getOverworldSystem();
            if (!os.isTunnelMode()) {
                TunnelEntranceObject teo = mission.getTunnelEntranceObject(
                        new Point((int) (thisPos[0] / TR.mapSquareSize), (int) (thisPos[2] / TR.mapSquareSize)));
                if (teo != null && !mission.isBossFight()) {
                    mission.enterTunnel(teo.getSourceTunnel());
                    return;
                }
            } //end if(above ground)
        } //end if(tunnelEntryCapable())

        if (groundImpact || ceilingImpact) {// detect collision
            recentlyCollided = true;
            double padding = autoNudge ? nudgePadding : 0;
            padding *= groundImpact ? 1 : -1;
            thisPos[1] = (groundImpact ? groundHeight : ceilingHeight) + padding;
            p.notifyPositionChange();
            if (dot < 0 || ignoreHeadingForImpact) {//If toward ground, call impact listeners.
                surfaceNormalVar = surfaceNormal;
                final Behavior behavior = p.getBehavior();
                behavior.probeForBehaviors(sub, SurfaceImpactListener.class);
            } //end if(pointedTowardGround)
        } // end if(collision)
    }// end _tick

    private final Submitter<SurfaceImpactListener> sub = new Submitter<SurfaceImpactListener>() {
        @Override
        public void submit(SurfaceImpactListener item) {
            item.collidedWithSurface(getDummyTerrainChunk(getParent().getTr()), surfaceNormalVar.toArray());
        }

        @Override
        public void submit(Collection<SurfaceImpactListener> items) {
            for (SurfaceImpactListener l : items) {
                submit(l);
            } //end for(items)
        }//end submit(...)
    };

    /**
     * @return the autoNudge
     */
    public boolean isAutoNudge() {
        return autoNudge;
    }

    /**
     * @param autoNudge
     *            the autoNudge to set
     */
    public CollidesWithTerrain setAutoNudge(boolean autoNudge) {
        this.autoNudge = autoNudge;
        return this;
    }

    /**
     * @return the nudgePadding
     */
    public double getNudgePadding() {
        return nudgePadding;
    }

    /**
     * @param nudgePadding
     *            the nudgePadding to set
     */
    public CollidesWithTerrain setNudgePadding(double nudgePadding) {
        this.nudgePadding = nudgePadding;
        return this;
    }

    /**
     * @return the tunnelEntryCapable
     */
    public boolean isTunnelEntryCapable() {
        return tunnelEntryCapable;
    }

    /**
     * @param tunnelEntryCapable the tunnelEntryCapable to set
     */
    public CollidesWithTerrain setTunnelEntryCapable(boolean tunnelEntryCapable) {
        this.tunnelEntryCapable = tunnelEntryCapable;
        return this;
    }

    /**
     * @param ignoreHeadingForImpact the ignoreHeadingForImpact to set
     */
    public CollidesWithTerrain setIgnoreHeadingForImpact(boolean ignoreHeadingForImpact) {
        this.ignoreHeadingForImpact = ignoreHeadingForImpact;
        return this;
    }

    public CollidesWithTerrain setIgnoreCeiling(boolean ignoreCeiling) {
        this.ignoreCeiling = ignoreCeiling;
        return this;
    }

    /**
     * @return the ignoreCeiling
     */
    public boolean isIgnoreCeiling() {
        return ignoreCeiling;
    }

    private static TerrainChunk getDummyTerrainChunk(TR tr) {
        if (dummyTerrainChunk == null)
            dummyTerrainChunk = new TerrainChunk(tr);
        return dummyTerrainChunk;
    }
}// end BouncesOffTerrain