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

Java tutorial

Introduction

Here is the source code for org.jtrfp.trcl.beh.AutoFiring.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 org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.jtrfp.trcl.core.TR;
import org.jtrfp.trcl.math.Vect3D;
import org.jtrfp.trcl.obj.Player;
import org.jtrfp.trcl.obj.Velocible;
import org.jtrfp.trcl.obj.WorldObject;

public class AutoFiring extends Behavior {
    private double maxFiringDistance = TR.mapSquareSize * 10;
    private double minFiringDistance = TR.mapSquareSize * 0;
    private int lastIndexVisited = 0;
    private boolean smartFiring = false;
    private boolean[] firingPattern = new boolean[] { true, true, false, false, false, true, false, false, true,
            false, false, false, false };
    private int timePerPatternEntry = 400;
    private int totalFiringPatternTimeMillis = firingPattern.length * timePerPatternEntry;
    private ProjectileFiringBehavior projectileFiringBehavior;
    private final double[] firingVector = new double[3];
    private final double[] headingDelta = new double[3];
    private int patternOffsetMillis = 0;
    private double maxFireVectorDeviation = 1.1;
    private boolean berzerk = false;
    private double aimRandomness = 0;

    @Override
    public void _tick(long timeMillis) {
        final WorldObject thisObject = getParent();
        final Player player = thisObject.getTr().getGame().getPlayer();
        if (player.getBehavior().probeForBehavior(Cloakable.class).isCloaked())
            return;
        final double[] thisPos = thisObject.getPosition();
        final double[] playerPos = player.getPosition();
        final double dist = Vect3D.distance(thisPos, playerPos);
        if (dist < maxFiringDistance || dist > minFiringDistance) {
            final int patIndex = (int) (((timeMillis + patternOffsetMillis) % totalFiringPatternTimeMillis)
                    / timePerPatternEntry);
            if (patIndex != lastIndexVisited) {//end if(lastVisited)
                if (firingPattern[patIndex]) {
                    Vector3D result;
                    if (smartFiring) {
                        final Vector3D playerVelocity = player.getBehavior().probeForBehavior(Velocible.class)
                                .getVelocity();
                        final double projectileSpeed = projectileFiringBehavior.getProjectileFactory().getWeapon()
                                .getSpeed() / TR.crossPlatformScalar;
                        Vector3D virtualPlayerPos = interceptOf(new Vector3D(playerPos), playerVelocity,
                                new Vector3D(thisPos), projectileSpeed);
                        if (virtualPlayerPos == null)
                            virtualPlayerPos = new Vector3D(playerPos);
                        Vect3D.subtract(virtualPlayerPos.toArray(), thisPos, firingVector);
                    } else {
                        Vect3D.subtract(playerPos, thisPos, firingVector);
                    }
                    result = new Vector3D(Vect3D.normalize(firingVector, firingVector));
                    final double[] objectHeading = thisObject.getHeadingArray();
                    Vect3D.subtract(objectHeading, result.toArray(), headingDelta);
                    if (Vect3D.norm(headingDelta) > maxFireVectorDeviation)
                        return;
                    if (berzerk)
                        result = new Vector3D(Math.random(), Math.random(), Math.random()).normalize();
                    Vector3D rand = new Vector3D((Math.random() * 2. - 1.) * aimRandomness,
                            (Math.random() * 2. - 1.) * aimRandomness, (Math.random() * 2. - 1.) * aimRandomness);
                    result = result.add(rand).normalize();
                    projectileFiringBehavior.requestFire(result);
                }
            }
            lastIndexVisited = patIndex;
        } //end in range
    }//end _tick(...)

    /**
     * Adapted from <a href='http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/'>this article.</a>
     * @param targetPos
     * @param targetVel
     * @param attackPos
     * @param attackSpeed
     * @return
     * @since Feb 13, 2014
     */
    private static Vector3D interceptOf(final Vector3D targetPos, final Vector3D targetVel,
            final Vector3D attackPos, final double attackSpeed) {
        final double dX = targetPos.getX() - attackPos.getX();
        final double dY = targetPos.getY() - attackPos.getY();
        final double dZ = targetPos.getZ() - attackPos.getZ();

        final double h1 = targetVel.getX() * targetVel.getX() + targetVel.getY() * targetVel.getY()
                + targetVel.getZ() * targetVel.getZ() - attackSpeed * attackSpeed;
        final double h2 = dX * targetVel.getX() + dY * targetVel.getY() + dZ * targetVel.getZ();
        double t;
        if (h1 == 0)
            t = -(dX * dX + dY * dY + dZ * dZ) / 2 * h2;
        else {
            final double minusPHalf = -h2 / h1;
            final double disc = minusPHalf * minusPHalf - (dX * dX + dY * dY + dZ * dZ) / h1;
            if (disc < 0)
                return null;

            final double root = Math.sqrt(disc);
            final double t1 = minusPHalf + root;
            final double t2 = minusPHalf - root;
            final double tMin = Math.min(t1, t2);
            final double tMax = Math.max(t1, t2);
            t = tMin > 0 ? tMin : tMax;

            if (t < 0)
                return null;
        } //end else(calculate full)
        return new Vector3D(targetPos.getX() + t * targetVel.getX(), targetPos.getY() + t * targetVel.getY(),
                targetPos.getZ() + t * targetVel.getZ());
    }//and interceptOf

    /**
     * @return the maxFiringDistance
     */
    public double getMaxFiringDistance() {
        return maxFiringDistance;
    }

    /**
     * @param maxFiringDistance the maxFiringDistance to set
     */
    public AutoFiring setMaxFiringDistance(double maxFiringDistance) {
        this.maxFiringDistance = maxFiringDistance;
        return this;
    }

    /**
     * @return the minFiringDistance
     */
    public double getMinFiringDistance() {
        return minFiringDistance;
    }

    /**
     * @param minFiringDistance the minFiringDistance to set
     */
    public AutoFiring setMinFiringDistance(double minFiringDistance) {
        this.minFiringDistance = minFiringDistance;
        return this;
    }

    /**
     * @return the firingPattern
     */
    public boolean[] getFiringPattern() {
        return firingPattern;
    }

    /**
     * @param firingPattern the firingPattern to set
     */
    public AutoFiring setFiringPattern(boolean[] firingPattern) {
        this.firingPattern = firingPattern;
        totalFiringPatternTimeMillis = firingPattern.length * timePerPatternEntry;
        return this;
    }

    /**
     * @return the timePerPatternEntry
     */
    public int getTimePerPatternEntry() {
        return timePerPatternEntry;
    }

    /**
     * @param timePerPatternEntry the timePerPatternEntry to set
     */
    public AutoFiring setTimePerPatternEntry(int timePerPatternEntry) {
        this.timePerPatternEntry = timePerPatternEntry;
        totalFiringPatternTimeMillis = firingPattern.length * timePerPatternEntry;
        return this;
    }

    /**
     * @return the totalFiringPatternTimeMillis
     */
    public int getTotalFiringPatternTimeMillis() {
        return totalFiringPatternTimeMillis;
    }

    /**
     * @return the projectileFiringBehavior
     */
    public ProjectileFiringBehavior getProjectileFiringBehavior() {
        return projectileFiringBehavior;
    }

    /**
     * @param projectileFiringBehavior the projectileFiringBehavior to set
     */
    public AutoFiring setProjectileFiringBehavior(ProjectileFiringBehavior projectileFiringBehavior) {
        this.projectileFiringBehavior = projectileFiringBehavior;
        return this;
    }

    /**
     * @return the patternOffsetMillis
     */
    public int getPatternOffsetMillis() {
        return patternOffsetMillis;
    }

    /**
     * @param patternOffsetMillis the patternOffsetMillis to set
     */
    public AutoFiring setPatternOffsetMillis(int patternOffsetMillis) {
        this.patternOffsetMillis = patternOffsetMillis;
        return this;
    }

    /**
     * @return the smartFiring
     */
    public boolean isSmartFiring() {
        return smartFiring;
    }

    /**
     * Compensates for distance when calculating a firing vector. Use with care, it's quite brutal.
     * @param smartFiring the smartFiring to set
     */
    public AutoFiring setSmartFiring(boolean smartFiring) {
        this.smartFiring = smartFiring;
        return this;
    }

    /**
     * @return the maxFireVectorDeviation
     */
    public double getMaxFireVectorDeviation() {
        return maxFireVectorDeviation;
    }

    /**
     * @param maxFireVectorDeviation the maxFireVectorDeviation to set
     */
    public AutoFiring setMaxFireVectorDeviation(double maxFireVectorDeviation) {
        this.maxFireVectorDeviation = maxFireVectorDeviation;
        return this;
    }

    public AutoFiring setBerzerk(boolean berzerk) {
        this.berzerk = berzerk;
        return this;
    }

    public AutoFiring setAimRandomness(double randomnessCoeff) {
        aimRandomness = randomnessCoeff;
        return this;
    }
}//end AutoFiring