com.agateau.pixelwheels.map.Track.java Source code

Java tutorial

Introduction

Here is the source code for com.agateau.pixelwheels.map.Track.java

Source

/*
 * Copyright 2017 Aurlien Gteau <mail@agateau.com>
 *
 * This file is part of Pixel Wheels.
 *
 * Tiny Wheels is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program 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 General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.agateau.pixelwheels.map;

import com.agateau.pixelwheels.Constants;
import com.agateau.pixelwheels.debug.Debug;
import com.agateau.pixelwheels.utils.OrientedPoint;
import com.agateau.utils.Assert;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.MapProperties;
import com.badlogic.gdx.maps.objects.EllipseMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.math.Ellipse;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;

/**
 * The map of the current game
 */
public class Track implements Disposable {
    private static final int CELL_ID_ROW_STRIDE = 10000;

    private final String mId;
    private final String mMapName;

    private TiledMap mMap;
    private Material[] mMaterialForTileId;
    private int mStartTileId = -1;
    private Array<TiledMapTileLayer> mBackgroundLayers;
    private Array<TiledMapTileLayer> mForegroundLayers;
    private MapLayer mBordersLayer;
    private final WaypointStore mWaypointStore = new WaypointStore();
    private float mTileWidth;
    private float mTileHeight;
    private Array<MapObject> mBorderObjects = new Array<MapObject>();
    private LapPositionTable mLapPositionTable;
    private Color mBackgroundColor;

    public Track(String id, String name) {
        mId = id;
        mMapName = name;
    }

    public void init() {
        if (mMap != null) {
            return;
        }
        TmxMapLoader loader = new TmxMapLoader();
        mMap = loader.load(Gdx.files.internal("maps/" + mId + ".tmx").path());
        mMaterialForTileId = computeMaterialForTileId();
        findSpecialTileIds();
        findLayers();
        readBorders();

        mTileWidth = Constants.UNIT_FOR_PIXEL * mBackgroundLayers.get(0).getTileWidth();
        mTileHeight = Constants.UNIT_FOR_PIXEL * mBackgroundLayers.get(0).getTileHeight();

        mLapPositionTable = LapPositionTableIO.load(mMap);
        readWaypoints();

        String bgColorText = mMap.getProperties().get("backgroundcolor", "#808080", String.class);
        bgColorText = bgColorText.substring(1); // Skip leading '#'
        mBackgroundColor = Color.valueOf(bgColorText);
    }

    private void findLayers() {
        mBackgroundLayers = findLayersMatching("bg");
        Assert.check(mBackgroundLayers.size > 0, "No background layers found");

        mForegroundLayers = findLayersMatching("fg");

        mBordersLayer = mMap.getLayers().get("Borders");
        Assert.check(mBordersLayer != null, "No \"Borders\" layer found");
    }

    private Array<TiledMapTileLayer> findLayersMatching(String match) {
        Array<TiledMapTileLayer> array = new Array<TiledMapTileLayer>();
        for (int idx = 0; idx < mMap.getLayers().getCount(); ++idx) {
            MapLayer layer = mMap.getLayers().get(idx);
            if (layer.getName().startsWith(match)) {
                array.add((TiledMapTileLayer) layer);
            }
        }
        return array;
    }

    public String getId() {
        return mId;
    }

    public Color getBackgroundColor() {
        return mBackgroundColor;
    }

    public int getTotalLapCount() {
        return Debug.instance.oneLapOnly ? 1 : 3;
    }

    public TiledMap getMap() {
        return mMap;
    }

    public float getTileWidth() {
        return mTileWidth;
    }

    public float getTileHeight() {
        return mTileHeight;
    }

    public float getMapWidth() {
        return mTileWidth * mBackgroundLayers.get(0).getWidth();
    }

    public float getMapHeight() {
        return mTileHeight * mBackgroundLayers.get(0).getHeight();
    }

    public MapLayer getBordersLayer() {
        return mBordersLayer;
    }

    public Array<MapObject> getBorderObjects() {
        return mBorderObjects;
    }

    public LapPositionTable getLapPositionTable() {
        return mLapPositionTable;
    }

    public WaypointStore getWaypointStore() {
        return mWaypointStore;
    }

    public int[] getExtraBackgroundLayerIndexes() {
        int[] indexes = new int[mBackgroundLayers.size - 1];
        for (int idx = 1; idx < mBackgroundLayers.size; ++idx) {
            indexes[idx - 1] = idx;
        }
        return indexes;
    }

    public int[] getForegroundLayerIndexes() {
        int[] indexes = new int[mForegroundLayers.size];
        // Foreground layers are just after background layers
        int start = mBackgroundLayers.size;
        for (int idx = 0; idx < mForegroundLayers.size; ++idx) {
            indexes[idx] = start + idx;
        }
        return indexes;
    }

    private Material[] computeMaterialForTileId() {
        TiledMapTileSet tileSet = mMap.getTileSets().getTileSet(0);
        int maxId = 0;
        for (TiledMapTile tile : tileSet) {
            maxId = Math.max(maxId, tile.getId());
        }
        Material[] array = new Material[maxId + 1];
        for (int id = 0; id < array.length; ++id) {
            TiledMapTile tile = tileSet.getTile(id);
            array[id] = MapUtils.getTileMaterial(tile);
        }
        return array;
    }

    private void findSpecialTileIds() {
        TiledMapTileSet tileSet = mMap.getTileSets().getTileSet(0);
        for (TiledMapTile tile : tileSet) {
            MapProperties properties = tile.getProperties();
            if (MapUtils.getBooleanProperty(properties, "start", false)) {
                mStartTileId = tile.getId();
            }
        }
        Assert.check(mStartTileId != -1, "No start id");
    }

    private TiledMapTile getTopTileAt(Array<TiledMapTileLayer> layers, float x, float y) {
        int tx = MathUtils.floor(x / mTileWidth);
        int ty = MathUtils.floor(y / mTileHeight);
        for (int idx = layers.size - 1; idx >= 0; idx--) {
            TiledMapTileLayer.Cell cell = layers.get(idx).getCell(tx, ty);
            if (cell != null) {
                return cell.getTile();
            }
        }
        return null;
    }

    /**
     * Returns a "cell id" for the given screen coordinates
     * A cell id is a long representing the combination of x and y in map coordinates
     */
    public long getCellIdAt(float x, float y) {
        int tx = MathUtils.floor(x / mTileWidth);
        int ty = MathUtils.floor(y / mTileHeight);
        return ty * CELL_ID_ROW_STRIDE + tx;
    }

    public Material getMaterialAt(Vector2 pos) {
        return getMaterialAt(pos.x, pos.y);
    }

    public Material getMaterialAt(float x, float y) {
        TiledMapTile tile = getTopTileAt(mBackgroundLayers, x, y);
        if (tile == null) {
            return Material.ROAD;
        }
        return mMaterialForTileId[tile.getId()];
    }

    @Override
    public void dispose() {
        mMap.dispose();
        mMap = null;
    }

    public Array<Vector2> findStartTilePositions() {
        Array<Vector2> lst = new Array<Vector2>();
        TiledMapTileLayer groundLayer = mBackgroundLayers.get(0);
        for (int ty = 0; ty < groundLayer.getHeight(); ++ty) {
            for (int tx = 0; tx < groundLayer.getWidth(); ++tx) {
                TiledMapTileLayer.Cell cell = groundLayer.getCell(tx, ty);
                if (cell == null) {
                    continue;
                }
                int tileId = cell.getTile().getId();
                if (tileId == mStartTileId) {
                    Vector2 pos = new Vector2(tx * mTileWidth + mTileWidth / 2, ty * mTileHeight);
                    lst.add(pos);
                }
            }
        }
        return lst;
    }

    public Array<Vector2> findBonusSpotPositions() {
        final float U = Constants.UNIT_FOR_PIXEL;
        MapLayer layer = mMap.getLayers().get("BonusSpots");
        Assert.check(layer != null, "No BonusSpots layer");
        Array<Vector2> lst = new Array<Vector2>();

        for (MapObject object : layer.getObjects()) {
            if (!(object instanceof EllipseMapObject)) {
                throw new RuntimeException(
                        "BonusSpots layer should contains only ellipses. " + object + " is not an ellipse.");
            }
            Ellipse ellipse = ((EllipseMapObject) object).getEllipse();
            Vector2 pos = new Vector2(ellipse.x * U, ellipse.y * U);
            lst.add(pos);
        }
        return lst;
    }

    private void readWaypoints() {
        MapLayer layer = mMap.getLayers().get("Waypoints");
        Assert.check(layer != null, "No Waypoints layer");
        mWaypointStore.read(layer, mLapPositionTable);
    }

    public OrientedPoint getValidPosition(Vector2 pos, float lapDistance) {
        return mWaypointStore.getValidPosition(pos, lapDistance);
    }

    private void readBorders() {
        for (MapObject object : mBordersLayer.getObjects()) {
            mBorderObjects.add(object);
        }
    }

    @SuppressWarnings("unused")
    public String getMapName() {
        return mMapName;
    }
}