org.destinationsol.game.chunk.ChunkFiller.java Source code

Java tutorial

Introduction

Here is the source code for org.destinationsol.game.chunk.ChunkFiller.java

Source

/*
 * Copyright 2015 MovingBlocks
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.destinationsol.game.chunk;

import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.math.Vector2;
import org.destinationsol.Const;
import org.destinationsol.TextureManager;
import org.destinationsol.common.SolColor;
import org.destinationsol.common.SolMath;
import org.destinationsol.game.*;
import org.destinationsol.game.asteroid.FarAsteroid;
import org.destinationsol.game.dra.Dra;
import org.destinationsol.game.dra.DraLevel;
import org.destinationsol.game.dra.FarDras;
import org.destinationsol.game.dra.RectSprite;
import org.destinationsol.game.input.AiPilot;
import org.destinationsol.game.input.MoveDestProvider;
import org.destinationsol.game.input.Pilot;
import org.destinationsol.game.input.StillGuard;
import org.destinationsol.game.maze.Maze;
import org.destinationsol.game.planet.*;
import org.destinationsol.game.ship.FarShip;
import org.destinationsol.game.ship.hulls.HullConfig;

import java.util.ArrayList;

public class ChunkFiller {
    public static final float DUST_DENSITY = .2f;
    public static final float ASTEROID_DENSITY = .008f;
    public static final float MIN_SYS_A_SZ = .5f;
    public static final float MAX_SYS_A_SZ = 1.2f;
    public static final float MIN_BELT_A_SZ = .4f;
    public static final float MAX_BELT_A_SZ = 2.4f;
    private static final float MAX_A_SPD = .2f;

    private static final float BELT_A_DENSITY = .04f;

    public static final float JUNK_MAX_SZ = .3f;
    public static final float JUNK_MAX_ROT_SPD = 45f;
    public static final float JUNK_MAX_SPD_LEN = .3f;

    public static final float FAR_JUNK_MAX_SZ = 2f;
    public static final float FAR_JUNK_MAX_ROT_SPD = 10f;

    public static final float ENEMY_MAX_SPD = .3f;
    public static final float ENEMY_MAX_ROT_SPD = 15f;
    public static final float DUST_SZ = .02f;
    private static final float MAZE_ZONE_BORDER = 20;
    private final TextureAtlas.AtlasRegion myDustTex;

    public ChunkFiller(TextureManager textureManager) {
        myDustTex = textureManager.getTex("deco/space/dust", null);
    }

    /**
     * Fill the background of a given chunk with floating junk.
     *
     * @param game    The {@link SolGame} instance to work with
     * @param chunk   The coordinates of the chunk
     * @param remover
     * @param farBg   Determines which of the background layers should be filled. <code>true</code> fills the layers furthest away, <code>false</code> fills the closer one.
     */
    public void fill(SolGame game, Vector2 chunk, RemoveController remover, boolean farBg) {
        if (DebugOptions.NO_OBJS)
            return;

        // Determine the center of the chunk by multiplying the chunk coordinates with the chunk size and adding half a chunk's size
        Vector2 chCenter = new Vector2(chunk);
        chCenter.scl(Const.CHUNK_SIZE);
        chCenter.add(Const.CHUNK_SIZE / 2, Const.CHUNK_SIZE / 2);

        // Define the density multiplier for different layers of junk in the far background
        float[] densityMul = { 1 };

        // Get the environment configuration
        SpaceEnvConfig conf = getConfig(game, chCenter, densityMul, remover, farBg);

        if (farBg) {
            fillFarJunk(game, chCenter, remover, DraLevel.FAR_DECO_3, conf, densityMul[0]);
            fillFarJunk(game, chCenter, remover, DraLevel.FAR_DECO_2, conf, densityMul[0]);
            fillFarJunk(game, chCenter, remover, DraLevel.FAR_DECO_1, conf, densityMul[0]);
        } else {
            fillDust(game, chCenter, remover);
            fillJunk(game, remover, conf, chCenter);
        }
    }

    private SpaceEnvConfig getConfig(SolGame game, Vector2 chCenter, float[] densityMul, RemoveController remover,
            boolean farBg) {
        PlanetManager pm = game.getPlanetMan();
        SolSystem sys = pm.getNearestSystem(chCenter);
        float toSys = sys.getPos().dst(chCenter);
        if (toSys < sys.getRadius()) {
            if (toSys < Const.SUN_RADIUS)
                return null;
            for (SystemBelt belt : sys.getBelts()) {
                if (belt.contains(chCenter)) {
                    if (!farBg)
                        fillAsteroids(game, remover, true, chCenter);
                    SysConfig beltConfig = belt.getConfig();
                    for (ShipConfig enemyConf : beltConfig.tempEnemies) {
                        if (!farBg)
                            fillEnemies(game, remover, enemyConf, chCenter);
                    }
                    return beltConfig.envConfig;
                }
            }
            float perc = toSys / sys.getRadius() * 2;
            if (perc > 1)
                perc = 2 - perc;
            densityMul[0] = perc;
            if (!farBg) {
                Planet p = pm.getNearestPlanet(chCenter);
                float toPlanet = p.getPos().dst(chCenter);
                boolean planetNear = toPlanet < p.getFullHeight() + Const.CHUNK_SIZE;
                if (!planetNear)
                    fillForSys(game, chCenter, remover, sys);
            }
            return sys.getConfig().envConfig;
        }
        Maze m = pm.getNearestMaze(chCenter);
        float dst = m.getPos().dst(chCenter);
        float zoneRad = m.getRadius() + MAZE_ZONE_BORDER;
        if (dst < zoneRad) {
            densityMul[0] = 1 - dst / zoneRad;
            return m.getConfig().envConfig;
        }
        return null;
    }

    private void fillForSys(SolGame game, Vector2 chCenter, RemoveController remover, SolSystem sys) {
        SysConfig conf = sys.getConfig();
        Vector2 mainStationPos = game.getGalaxyFiller().getMainStationPos();
        Vector2 startPos = mainStationPos == null ? new Vector2() : mainStationPos;
        float dst = chCenter.dst(startPos);
        if (Const.CHUNK_SIZE < dst) {
            fillAsteroids(game, remover, false, chCenter);
            ArrayList<ShipConfig> enemies = sys.getPos().dst(chCenter) < sys.getInnerRad() ? conf.innerTempEnemies
                    : conf.tempEnemies;
            for (ShipConfig enemyConf : enemies) {
                fillEnemies(game, remover, enemyConf, chCenter);
            }
        }
    }

    private void fillEnemies(SolGame game, RemoveController remover, ShipConfig enemyConf, Vector2 chCenter) {
        int count = getEntityCount(enemyConf.density);
        if (count == 0)
            return;
        for (int i = 0; i < count; i++) {
            Vector2 enemyPos = getFreeRndPos(game, chCenter);
            FarShip ship = buildSpaceEnemy(game, enemyPos, remover, enemyConf);
            if (ship != null)
                game.getObjMan().addFarObjNow(ship);
        }
    }

    public FarShip buildSpaceEnemy(SolGame game, Vector2 pos, RemoveController remover, ShipConfig enemyConf) {
        if (pos == null)
            return null;
        Vector2 spd = new Vector2();
        SolMath.fromAl(spd, SolMath.rnd(180), SolMath.rnd(0, ENEMY_MAX_SPD));
        float rotSpd = SolMath.rnd(ENEMY_MAX_ROT_SPD);
        MoveDestProvider dp = new StillGuard(pos, game, enemyConf);
        Pilot provider = new AiPilot(dp, false, Faction.EHAR, true, null, Const.AI_DET_DIST);
        HullConfig config = enemyConf.hull;
        int money = enemyConf.money;
        float angle = SolMath.rnd(180);
        return game.getShipBuilder().buildNewFar(game, pos, spd, angle, rotSpd, provider, enemyConf.items, config,
                remover, false, money, null, true);
    }

    private void fillAsteroids(SolGame game, RemoveController remover, boolean forBelt, Vector2 chCenter) {
        float density = forBelt ? BELT_A_DENSITY : ASTEROID_DENSITY;
        int count = getEntityCount(density);
        if (count == 0)
            return;
        for (int i = 0; i < count; i++) {
            Vector2 asteroidPos = getFreeRndPos(game, chCenter);
            if (asteroidPos == null)
                continue;
            float minSz = forBelt ? MIN_BELT_A_SZ : MIN_SYS_A_SZ;
            float maxSz = forBelt ? MAX_BELT_A_SZ : MAX_SYS_A_SZ;
            float sz = SolMath.rnd(minSz, maxSz);
            Vector2 spd = new Vector2();
            SolMath.fromAl(spd, SolMath.rnd(180), MAX_A_SPD);

            FarAsteroid a = game.getAsteroidBuilder().buildNewFar(asteroidPos, spd, sz, remover);
            game.getObjMan().addFarObjNow(a);
        }
    }

    /**
     * Add a bunch of a certain type of junk to the background layers furthest away.
     * <p/>
     * This type of junk does not move on its own, it merely changes position as the camera moves, simulating different
     * depths relative to the camera.
     *
     * @param game       The {@link SolGame} instance to work with
     * @param chCenter   The center of the chunk
     * @param remover
     * @param draLevel   The depth of the junk
     * @param conf       The environment configuration
     * @param densityMul A density multiplier. This will be multiplied with the density defined in the environment configuration
     */
    private void fillFarJunk(SolGame game, Vector2 chCenter, RemoveController remover, DraLevel draLevel,
            SpaceEnvConfig conf, float densityMul) {
        if (conf == null)
            return;
        int count = getEntityCount(conf.farJunkDensity * densityMul);
        if (count == 0)
            return;

        ArrayList<Dra> dras = new ArrayList<Dra>();
        TextureManager textureManager = game.getTexMan();

        for (int i = 0; i < count; i++) {
            // Select a random far junk texture
            TextureAtlas.AtlasRegion tex = SolMath.elemRnd(conf.farJunkTexs);
            // Flip texture for every other piece of junk
            if (SolMath.test(.5f))
                tex = textureManager.getFlipped(tex);
            // Choose a random size (within a range)
            float sz = SolMath.rnd(.3f, 1) * FAR_JUNK_MAX_SZ;
            // Apply a random rotation speed
            float rotSpd = SolMath.rnd(FAR_JUNK_MAX_ROT_SPD);
            // Select a random position in the chunk centered around chCenter, relative to the position of the chunk.
            Vector2 junkPos = getRndPos(chCenter);
            junkPos.sub(chCenter);

            // Create the resulting sprite and add it to the list
            RectSprite s = new RectSprite(tex, sz, 0, 0, junkPos, draLevel, SolMath.rnd(180), rotSpd, SolColor.DDG,
                    false);
            dras.add(s);
        }

        // Create a common FarDras instance for the pieces of junk and only allow the junk to be drawn when it's not hidden by a planet
        FarDras so = new FarDras(dras, new Vector2(chCenter), new Vector2(), remover, true);
        // Add the collection of objects to the object manager
        game.getObjMan().addFarObjNow(so);
    }

    /**
     * Add a bunch of a certain type of junk to the background layer closest to the front.
     * <p/>
     * This type of junk moves at the same speed as the camera (similar to the dust) but additionally has its own floating
     * direction and angle for every individual piece of junk.
     *
     * @param game     The {@link SolGame} instance to work with
     * @param remover
     * @param conf     The environment configuration
     * @param chCenter The center of the chunk
     */
    private void fillJunk(SolGame game, RemoveController remover, SpaceEnvConfig conf, Vector2 chCenter) {
        if (conf == null)
            return;
        int count = getEntityCount(conf.junkDensity);
        if (count == 0)
            return;

        for (int i = 0; i < count; i++) {
            // Select a random position in the chunk centered around chCenter, relative to the entire map.
            Vector2 junkPos = getRndPos(chCenter);

            // Select a random junk texture
            TextureAtlas.AtlasRegion tex = SolMath.elemRnd(conf.junkTexs);
            // Flip texture for every other piece of junk
            if (SolMath.test(.5f))
                tex = game.getTexMan().getFlipped(tex);
            // Choose a random size (within a range)
            float sz = SolMath.rnd(.3f, 1) * JUNK_MAX_SZ;
            // Apply a random rotation speed
            float rotSpd = SolMath.rnd(JUNK_MAX_ROT_SPD);

            // Create the resulting sprite and add it to the list as the only element
            RectSprite s = new RectSprite(tex, sz, 0, 0, new Vector2(), DraLevel.DECO, SolMath.rnd(180), rotSpd,
                    SolColor.LG, false);
            ArrayList<Dra> dras = new ArrayList<Dra>();
            dras.add(s);

            // Create a FarDras instance for this piece of junk and only allow it to be drawn when it's not hidden by a planet
            Vector2 spd = new Vector2();
            SolMath.fromAl(spd, SolMath.rnd(180), SolMath.rnd(JUNK_MAX_SPD_LEN));
            FarDras so = new FarDras(dras, junkPos, spd, remover, true);
            // Add the object to the object manager
            game.getObjMan().addFarObjNow(so);
        }
    }

    /**
     * Add specks of dust to the background layer closest to the front.
     * <p/>
     * Dust is fixed in the world and therefore moves opposite to the cameras movement.
     *
     * @param game     The {@link SolGame} instance to work with
     * @param chCenter The center of the chunk
     * @param remover
     */
    private void fillDust(SolGame game, Vector2 chCenter, RemoveController remover) {
        ArrayList<Dra> dras = new ArrayList<Dra>();
        int count = getEntityCount(DUST_DENSITY);
        if (count == 0)
            return;

        TextureAtlas.AtlasRegion tex = myDustTex;
        for (int i = 0; i < count; i++) {
            // Select a random position in the chunk centered around chCenter, relative to the position of the chunk.
            Vector2 dustPos = getRndPos(chCenter);
            dustPos.sub(chCenter);
            // Create the resulting sprite and add it to the list
            RectSprite s = new RectSprite(tex, DUST_SZ, 0, 0, dustPos, DraLevel.DECO, 0, 0, SolColor.W, false);
            dras.add(s);
        }

        // Create a common FarDras instance for the specks of dust and only allow the dust to be drawn when it's not hidden by a planet
        FarDras so = new FarDras(dras, chCenter, new Vector2(), remover, true);
        game.getObjMan().addFarObjNow(so);
    }

    /**
     * Find a random position in a chunk centered around chCenter, relative to the entire map, and make sure it is not yet
     * occupied by another entity.
     * <p/>
     * Up to 100 tries will be made to find an unoccupied position; if by then none has been found, <code>null</code> will be returned.
     *
     * @param g        The {@link SolGame} instance to work with
     * @param chCenter The center of a chunk in which a random position should be found
     * @return A random, unoccupied position in a chunk centered around chCenter, relative to the entire map, or <code>null</code> if within 100 tries no unoccupied position has been found
     */
    private Vector2 getFreeRndPos(SolGame g, Vector2 chCenter) {
        for (int i = 0; i < 100; i++) {
            Vector2 pos = getRndPos(new Vector2(chCenter));
            if (g.isPlaceEmpty(pos, true))
                return pos;
        }
        return null;
    }

    /**
     * Returns a random position in a chunk centered around chCenter, relative to the entire map.
     *
     * @param chCenter The center of a chunk in which a random position should be found
     * @return A random position in a chunk centered around chCenter, relative to the entire map.
     */
    private Vector2 getRndPos(Vector2 chCenter) {
        Vector2 pos = new Vector2(chCenter);
        pos.x += SolMath.rnd(Const.CHUNK_SIZE / 2);
        pos.y += SolMath.rnd(Const.CHUNK_SIZE / 2);
        return pos;
    }

    /**
     * Determine the number of objects per chunk for a given density, based on the chunk size.
     * If the number turns out to be less than 1, 1 will be returned randomly with a probability of the resulting number, otherwise 0.
     *
     * @param density The density of the objects per chunk
     * @return The number of objects for the chunk based on the given density.
     */
    private int getEntityCount(float density) {
        float amt = Const.CHUNK_SIZE * Const.CHUNK_SIZE * density;
        if (amt >= 1)
            return (int) amt;
        return SolMath.test(amt) ? 1 : 0;
    }

}