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.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