aphelion.client.graphics.world.Projectile.java Source code

Java tutorial

Introduction

Here is the source code for aphelion.client.graphics.world.Projectile.java

Source

/*
 * Aphelion
 * Copyright (c) 2013  Joris van der Wel
 * 
 * This file is part of Aphelion
 * 
 * Aphelion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * Aphelion is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with Aphelion.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * In addition, the following supplemental terms apply, based on section 7 of
 * the GNU Affero General Public License (version 3):
 * a) Preservation of all legal notices and author attributions
 * b) Prohibition of misrepresentation of the origin of this material, and
 * modified versions are required to be marked in reasonable ways as
 * different from the original version (for example by appending a copyright notice).
 * 
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU Affero General Public License cover the whole combination.
 * 
 * As a special exception, the copyright holders of this library give you 
 * permission to link this library with independent modules to produce an 
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your 
 * choice, provided that you also meet, for each linked independent module,
 * the terms and conditions of the license of that module. An independent
 * module is a module which is not derived from or based on this library.
 */
package aphelion.client.graphics.world;

import aphelion.client.Client;
import aphelion.client.graphics.AnimatedColour;
import aphelion.client.graphics.screen.Camera;
import aphelion.shared.event.TickEvent;
import aphelion.shared.gameconfig.GCBoolean;
import aphelion.shared.gameconfig.GCColour;
import aphelion.shared.gameconfig.GCImage;
import aphelion.shared.gameconfig.WrappedValueAbstract;
import aphelion.shared.physics.EnvironmentConf;
import aphelion.shared.physics.PhysicsEnvironment;
import aphelion.shared.physics.entities.ProjectilePublic;
import aphelion.shared.physics.valueobjects.PhysicsPoint;
import aphelion.shared.physics.valueobjects.PhysicsPositionVector;
import aphelion.shared.resource.ResourceDB;
import aphelion.shared.swissarmyknife.Point;
import aphelion.shared.swissarmyknife.SwissArmyKnife;
import de.lessvoid.nifty.tools.Color;
import java.lang.ref.WeakReference;
import javax.annotation.Nonnull;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Animation;
import org.newdawn.slick.Image;
import org.newdawn.slick.SpriteSheetCounted;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.renderer.SGL;

/**
 * The graphic counter part of physics.Projectile. 
 * @author Joris
 */
public class Projectile extends MapEntity implements WrappedValueAbstract.ChangeListener, TickEvent {
    final ProjectilePublic physicsProjectile;
    public final Point shadowPosition = new Point(0, 0);
    public final Point shadowPosition_prev = new Point(0, 0);

    // RenderDelay:
    public final RenderDelayValue renderDelay_value = new RenderDelayValue(0);
    public long renderDelay_current;
    public WeakReference<ActorShip> renderDelay_basedOn;
    public long renderingAt_tick;

    private final GCImage imageNoBounce;
    private final GCImage imageBounces;
    private final GCImage imageInactive;
    private final GCImage imageTrail;
    private final GCBoolean imageTrailRandomized;
    private final GCColour colourRadar;

    private Animation animNoBounce;
    private Animation animBounces;
    private Animation animInactive;
    private AnimatedColour animRadar;

    public static final float TIMEWARP_ALPHA_VELOCITY_MIN = 0.003f; // 10 seconds to fully fade back in
    public static final float TIMEWARP_ALPHA_VELOCITY_MAX = 0.1f;
    public static final float TIMEWARP_ALPHA_VELOCITY_LOCAL_DIST_SMOOTHING = 500; // in pixels

    public Projectile(@Nonnull ResourceDB db, @Nonnull ProjectilePublic physicsProjectile) {
        super(db);
        this.physicsProjectile = physicsProjectile;
        imageNoBounce = physicsProjectile.getWeaponConfigImage("projectile-image", db);
        imageBounces = physicsProjectile.getWeaponConfigImage("projectile-image-bounces", db);
        imageInactive = physicsProjectile.getWeaponConfigImage("projectile-image-inactive", db);
        imageTrail = physicsProjectile.getWeaponConfigImage("projectile-image-trail", db);
        imageTrailRandomized = physicsProjectile.getWeaponConfigBoolean("projectile-image-trail-randomized");
        colourRadar = physicsProjectile.getWeaponConfigColour("projectile-radar-colour");
    }

    public @Nonnull ProjectilePublic getPhysicsProjectile() {
        return physicsProjectile;
    }

    public void calculateRenderAtTick(@Nonnull PhysicsEnvironment physicsEnv) {
        renderDelay_current = SwissArmyKnife.clip(renderDelay_value.get(), 0, physicsEnv.getConfig().HIGHEST_DELAY);

        this.renderingAt_tick = physicsEnv.getTick() - renderDelay_current;
    }

    public void setShadowPositionFromPhysics(int x, int y) {
        shadowPosition_prev.set(shadowPosition);
        shadowPosition.x = x / 1024f;
        shadowPosition.y = y / 1024f;
    }

    @Override
    public void tick(long tick) {
        super.tick(tick);
        renderDelay_value.tick(tick);
    }

    private void updateAnimObjects() {
        if (animNoBounce == null) {
            animNoBounce = imageNoBounce.newAnimation();
            if (animNoBounce != null) {
                animNoBounce.setLooping(true);
            }
        }

        if (animBounces == null) {
            animBounces = imageBounces.newAnimation();
            if (animBounces != null) {
                animBounces.setLooping(true);
            }
        }

        if (animInactive == null) {
            animInactive = imageInactive.newAnimation();
            if (animInactive != null) {
                animInactive.setLooping(true);
            }
        }

        if (animRadar == null) {
            animRadar = colourRadar.getAnimation();
        }
    }

    @Override
    public void gameConfigValueChanged(@Nonnull WrappedValueAbstract val) {
        if (val == this.imageNoBounce) {
            this.animNoBounce = null;
        }

        if (val == this.imageBounces) {
            this.animBounces = null;
        }

        if (val == this.imageInactive) {
            this.animInactive = null;
        }

        if (val == this.colourRadar) {
            this.animRadar = null;
        }
    }

    @Override
    public boolean render(@Nonnull Camera camera, int iteration) {
        if (!isExists()) {
            return false;
        }

        if (iteration > 1) {
            return false;
        }

        updateAnimObjects();

        Point screenPos = new Point();
        camera.mapToScreenPosition(pos, screenPos);

        if (camera.radarRendering) {
            if (iteration == 0 && this.animRadar != null) {
                org.newdawn.slick.Color color = this.animRadar.get();
                TextureImpl.bindNone();
                GL11.glColor4f(color.r, color.g, color.b, color.a * this.alpha);
                float x = screenPos.x - 0.5f;
                float y = screenPos.y - 0.5f;
                GL11.glBegin(SGL.GL_QUADS);
                GL11.glVertex2f(x, y);
                GL11.glVertex2f(x + 1, y);
                GL11.glVertex2f(x + 1, y + 1);
                GL11.glVertex2f(x, y + 1);
                GL11.glEnd();
            }

            return false;
        }

        if (iteration == 0) {
            // render the trail
            SpriteSheetCounted trail = this.imageTrail.getSpriteSheet();
            if (trail != null) {
                Point trailPos = new Point();
                PhysicsPositionVector phyPos = new PhysicsPositionVector();

                long rand = imageTrailRandomized.get() ? SwissArmyKnife.fastRandomIsh() : 0;

                long tick = this.renderingAt_tick - (rand & 0b11); // 0, 1, 2, 3
                rand >>= 2;

                for (int tile = 0; tile < trail.getTilesCount(); tile += (rand & 0b1) + 1, rand >>= 1) {
                    physicsProjectile.getHistoricPosition(phyPos, tick - 2 * tile, true);

                    if (phyPos.pos.set) {
                        trailPos.set(phyPos.pos);
                        trailPos.divide(1024);
                        camera.mapToScreenPosition(trailPos, trailPos);

                        Image img = trail.getSubImage(tile);
                        img.draw(trailPos.x - img.getWidth() / 2, trailPos.y - img.getHeight() / 2,
                                this.alphaFilter);
                    }

                }
            }
        } else if (iteration == 1) {
            Animation anim;

            if (imageInactive.isSet() && !this.physicsProjectile.isActive()) {
                anim = animInactive;
            } else if (imageBounces.isSet() && this.physicsProjectile.getBouncesLeft() != 0) {
                anim = animBounces;
            } else {
                anim = animNoBounce; // also fallback
            }

            if (anim != null) {
                anim.draw(screenPos.x - anim.getWidth() / 2 * camera.zoom,
                        screenPos.y - anim.getHeight() / 2 * camera.zoom, anim.getWidth() * camera.zoom,
                        anim.getHeight() * camera.zoom, this.alphaFilter);
            }

            if (Client.showDebug) {
                camera.renderPlayerText(this.renderDelay_current + "", screenPos.x + 5, screenPos.y + 5,
                        Color.WHITE);
            }
        }

        return iteration < 1;
    }
}