com.andgate.ikou.model.Player.java Source code

Java tutorial

Introduction

Here is the source code for com.andgate.ikou.model.Player.java

Source

/*
This file is part of Ikou.
Ikou 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 2 of the License.
Ikou 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 Ikou.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.andgate.ikou.model;

import com.andgate.ikou.Constants;
import com.andgate.ikou.Ikou;
import com.andgate.ikou.controller.PlayerDirectionGestureDetector.DirectionListener;
import com.andgate.ikou.io.ProgressDatabaseService;
import com.andgate.ikou.model.TileStack.Tile;
import com.andgate.ikou.render.PlayerRender;
import com.andgate.ikou.utility.AcceleratedTween;
import com.andgate.ikou.utility.LinearTween;
import com.andgate.ikou.utility.Vector3i;
import com.andgate.ikou.utility.graphics.ColorUtils;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Disposable;

import java.util.ArrayList;

public class Player implements DirectionListener, Disposable {
    private static final String TAG = "PlayerTransformer";
    private final Ikou game;

    public Matrix4 transform = new Matrix4();

    private int currentFloor;
    private Level level;
    private final PlayerRender playerRender;

    private AcceleratedTween fallingTween = new AcceleratedTween();

    private static final float SLIDE_SPEED = Constants.TILE_LENGTH * 15.0f / 1.0f; // units per second

    private static final float ROUGH_SLIDE_DISTANCE = Constants.TILE_LENGTH; // units
    private static final float ROUGH_SLIDE_INITIAL_SPEED = SLIDE_SPEED;
    private static final float ROUGH_SLIDE_FINAL_SPEED = 0;
    private static final float ROUGH_SLIDE_AVERAGE_SPEED = (ROUGH_SLIDE_FINAL_SPEED + ROUGH_SLIDE_INITIAL_SPEED)
            / 2.0f;
    private static final float ROUGH_SLIDE_TIME = ROUGH_SLIDE_DISTANCE / ROUGH_SLIDE_AVERAGE_SPEED;
    private static final float ROUGH_SLIDE_DECCELERATION = (ROUGH_SLIDE_FINAL_SPEED - ROUGH_SLIDE_INITIAL_SPEED)
            / ROUGH_SLIDE_TIME; // units per second per second

    private static final float FALL_SPEEDUP_FACTOR = 4.0f;
    private static final float FALL_DISTANCE = Constants.FLOOR_SPACING;
    private static final float FALL_SPEED_INITIAL = FALL_DISTANCE / 1.0f; // units per second
    private static final float FALL_SPEED_FINAL = FALL_SPEED_INITIAL * FALL_SPEEDUP_FACTOR; // units per second
    private static final float FALL_SPEED_AVERAGE = (FALL_SPEED_INITIAL + FALL_SPEED_FINAL) / 2.0f; // units per second
    private static final float FALL_TIME = FALL_DISTANCE / FALL_SPEED_AVERAGE; // seconds
    private static final float FALL_ACCELERATION = (FALL_SPEED_FINAL - FALL_SPEED_INITIAL) / FALL_TIME; // units per second per second

    private Vector3 position = new Vector3();
    private Vector3i direction = new Vector3i();

    public Player(final Ikou game, final Level level, final int startingFloor) {
        this.game = game;
        this.level = level;

        this.currentFloor = startingFloor;
        setPosition(level.getStartPosition(currentFloor));

        playerRender = new PlayerRender();
        playerRender.setColor(level.getFloor(currentFloor).getPalette().player);
        playerRender.getTransform().set(transform);
    }

    public PlayerRender getRender() {
        return playerRender;
    }

    public void setPosition(float x, float y, float z) {
        position.set(x, y, z);
        initialPosition.set(x, y, z);
        finalPosition.set(x, y, z);
        transform.idt().translate(position);
    }

    public void setPosition(Vector3 position) {
        setPosition(position.x, position.y, position.z);
    }

    public Vector3 getPosition() {
        return position;
    }

    private Vector3 distance = new Vector3();

    public void update(float delta) {
        Tile currentTile = getCurrentTile();

        if (!direction.isZero() || currentTile == Tile.End) {
            distance.set(getPosition());

            if (currentTile == Tile.End) {
                fallToNextFloor(delta);
            } else {
                updateNextTile(delta);
            }

            distance.sub(getPosition());
            distance.scl(-1);

            notifyPlayerTransformListeners(distance.x, distance.y, distance.z);

            playerRender.getTransform().idt().translate(position);
        }
    }

    public void updateNextTile(float delta) {
        Tile nextTile = getNextTile();

        switch (nextTile) {
        case Smooth:
            slideSmooth(delta);
            break;
        case Obstacle:
            hitObstacle();
            break;
        case Blank:
            hitObstacle();
            break;
        case Rough:
            slideRough(delta);
            break;
        case End:
            slideEnd(delta);
            break;
        default:
            break;
        }
    }

    boolean slideStarted = false;

    private LinearTween slideSmoothTween = new LinearTween();

    private boolean slideSmooth(float delta) {
        if (!slideStarted) {
            slideSmoothTween.setup(initialPosition, finalPosition, SLIDE_SPEED);
            slideStarted = true;
        }

        boolean isSlidingOver = slideSmoothTween.update(delta);
        position.set(slideSmoothTween.get());

        if (isSlidingOver) {
            slideStarted = false;
            moveInDirection(direction.x, direction.z, true);

            float leftOverDelta = slideSmoothTween.getLeftOverTime();

            if (leftOverDelta > 0.0f) {
                // Move completely is the next tile is smooth
                Tile nextTile = getNextTile();

                switch (nextTile) {
                case Smooth:
                    slideSmooth(leftOverDelta);
                    break;
                case Rough:
                    slideRough(leftOverDelta);
                    break;
                case End:
                    slideEnd(leftOverDelta);
                    break;
                default:
                    break;
                }
            }
        }

        return isSlidingOver;
    }

    private AcceleratedTween slideRoughTween = new AcceleratedTween();

    private boolean slideRough(float delta) {
        return slideStopGradually(delta, game.roughSound);
    }

    private boolean slideEnd(float delta) {
        return slideStopGradually(delta, null);
    }

    private boolean slideStopGradually(float delta, Sound soundEffect) {
        if (!slideStarted) {
            if (soundEffect != null) {
                soundEffect.play(0.2f);
            }

            slideRoughTween.setup(initialPosition, finalPosition, SLIDE_SPEED, ROUGH_SLIDE_DECCELERATION);
            slideStarted = true;
        }

        boolean isSlidingOver = slideRoughTween.update(delta);
        position.set(slideRoughTween.get());

        if (isSlidingOver) {
            slideStarted = false;
            direction.set(0, 0, 0);

            initialPosition.set(position);
        }

        return isSlidingOver;
    }

    private void hitObstacle() {
        game.hitSound.play(0.5f);

        direction.set(0, 0, 0);
        initialPosition.set(position);
    }

    private boolean isFalling = false;

    private boolean fallToNextFloor(float delta) {
        if (!isFalling) {
            game.fallSound.play(0.5f);

            initialPosition.set(position);
            finalPosition.set(position);
            finalPosition.y -= Constants.FLOOR_SPACING;
            fallingTween.setup(initialPosition, finalPosition, FALL_SPEED_INITIAL, FALL_ACCELERATION);

            isFalling = true;
        }

        boolean isFallingOver = fallingTween.update(delta);
        position.set(fallingTween.get());

        tweenBackground(fallingTween.getPercentComplete());
        tweenPlayerColor(fallingTween.getPercentComplete());

        if (isFallingOver) {
            isFalling = false;
            direction.set(0, 0, 0);

            initialPosition.set(position);
            game.hitSound.play();

            startNextFloor();
        }

        return isFallingOver;
    }

    private Color tmpBg = new Color();

    public void tweenBackground(float percent) {
        Color lastFloorColor = level.getFloor(currentFloor).getPalette().background;
        Color nextFloorColor = level.getFloor(currentFloor + 1).getPalette().background;

        ColorUtils.tween(lastFloorColor, nextFloorColor, percent, tmpBg);
        game.bloom.setClearColor(tmpBg.r, tmpBg.g, tmpBg.b, tmpBg.a);
    }

    private Color tmpColor = new Color();

    public void tweenPlayerColor(float percent) {
        Color lastFloorColor = level.getFloor(currentFloor).getPalette().player;
        Color nextFloorColor = level.getFloor(currentFloor + 1).getPalette().player;

        ColorUtils.tween(lastFloorColor, nextFloorColor, percent, tmpColor);
        playerRender.setColor(tmpColor);
    }

    public void startNextFloor() {
        saveProgress();
        currentFloor++;
    }

    private void saveProgress() {
        ProgressDatabase progressDB = ProgressDatabaseService.read();
        int completedFloors = progressDB.getFloorsCompleted(level.getName());
        if (currentFloor > completedFloors) {
            progressDB.setFloorsCompleted(level.getName(), currentFloor);
            ProgressDatabaseService.write(progressDB);
        }
    }

    Vector3 initialPosition = new Vector3();
    Vector3 finalPosition = new Vector3();

    @Override
    public void moveInDirection(Vector2 direction) {
        assert (direction.isUnit());

        if (this.direction.isZero()) {
            Tile nextTile = getTile((int) (position.x + direction.x), (int) (position.z + direction.y));

            if (!(nextTile == Tile.Obstacle || nextTile == Tile.Blank)) {
                moveInDirection((int) direction.x, (int) direction.y, false);
            }
        }
    }

    public void moveInDirection(int x, int z, boolean forceMove) {
        if ((direction.isZero() && !isFalling) || forceMove) {
            direction.set(x, 0, z);

            initialPosition.set(position);

            finalPosition.set(position);
            finalPosition.add(direction.x, direction.y, direction.z);
        }
    }

    public Tile getNextTile() {
        return getTile(finalPosition);
    }

    public Tile getCurrentTile() {
        return getTile(initialPosition);
    }

    public Tile getTile(int x, int z) {
        TileStack tileStack = level.getTileStack(currentFloor, x, z);
        Tile tile = tileStack.getTop();

        return tile;
    }

    public Tile getTile(Vector3 tileLocation) {
        int x = (int) tileLocation.x;
        int z = (int) tileLocation.z;

        return getTile(x, z);
    }

    private ArrayList<PlayerTransformListener> playerTransformListeners = new ArrayList<>();

    public interface PlayerTransformListener {
        public void playerTransformModified(float dx, float dy, float dz);
    }

    public void addPlayerTransformListener(PlayerTransformListener playerTransformListener) {
        playerTransformListeners.add(playerTransformListener);
    }

    public void removePlayerTransformListener(PlayerTransformListener playerTransformListener) {
        playerTransformListeners.remove(playerTransformListener);
    }

    public void notifyPlayerTransformListeners(float x, float y, float z) {
        for (PlayerTransformListener playerTransformListener : playerTransformListeners) {
            playerTransformListener.playerTransformModified(x, y, z);
        }
    }

    @Override
    public void dispose() {
        playerRender.dispose();
    }
}