BuildingRenderer.java

package com.vikingz.unitycoon.render;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Timer;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.vikingz.unitycoon.achievements.SaviourAchievement;
import com.vikingz.unitycoon.building.Building;
import com.vikingz.unitycoon.building.BuildingInfo;
import com.vikingz.unitycoon.building.BuildingStats;
import com.vikingz.unitycoon.global.GameGlobals;
import com.vikingz.unitycoon.menus.RemoveBuildingMenu;
import com.vikingz.unitycoon.util.GameSounds;

/**
 * This class is in charge of drawing Buildings in the game.
 *
 * This class also does the collision calculations for buildings
 * which make sure that the user is unable to place buildings on top
 * of each other, as well as using right click to be able to remove the
 * buildings from the game.
 * 
 * This class has been refactored to make the code more readable and too add new UI_features
 * following the user evaluation.
 */
public class BuildingRenderer{

    // Used to draw buildings textures.
    final SpriteBatch batch;

    // Used to display removeBuildingMenu on the UIRenderer stage.
    // This allows the user to interact with buttons on the menu.
    Stage UIStage;

    // X and Y values used to place buildings.
    float previewX, previewY;

    // If building is being placed by user.
    boolean isPreviewing;

    // Prevents building being placed and menu opening in the same click.
    boolean openMenu;

    // Textures of Building, fire and construction.
    TextureRegion selectedTexture;
    Texture underConstructionTexture = new Texture("png\\UnderConstruction.png");
    Texture fireTexture = new Texture("png\\fire.png");

    // Current Building being placed information.
    BuildingInfo currentBuildingInfo = null;

    // GameRender used to get mouse position and background tiles.
    final GameRenderer gameRenderer;

    // Checks if the user wants to delete a building.
    RemoveBuildingMenu removeBuildingPopUp;

    // Pop Up when a player tries to place a building on a colliding square.
    TextButton collisionPopUp;

    /**
     * Creates a new Building Renderer
     * @param gameRenderer Parent renderer {@code GameRenderer}
     * @param skin Used to display building popups
     */
    public BuildingRenderer(GameRenderer gameRenderer, Skin skin) {

        this.gameRenderer = gameRenderer;
        
        // Initialised as a blank stage initially as UIRenderer is initialised later
        UIStage = new Stage();
        batch = new SpriteBatch();
        isPreviewing = false;
        selectedTexture = null;
        openMenu = true;

        removeBuildingPopUp = new RemoveBuildingMenu(skin);

        BuildingStats.nextBuildingFree = false;

        // Set collision popup
        collisionPopUp = new TextButton("Unable to place building here", skin);
        collisionPopUp.setColor(Color.RED);
        collisionPopUp.setWidth(350);
        collisionPopUp.setPosition((UIStage.getWidth() - collisionPopUp.getWidth()) / 2, 
            (UIStage.getHeight() - 100));
        collisionPopUp.getLabel().setFontScale((float)0.4,(float)0.4);
    }

    /**
     * Renders buildings
     * @param delta Time since last frame
     */
    public void render(float delta) {
        checkBuildings(delta);
    }

    /**
     * Checks if the user is currently adding or removing buildings.
     * This method has been refactored to complete FR_BUILD_TIME and UR_EVENTS
     * @param delta Time since last frame
     */
    private void checkBuildings(float delta){
        //Stops previewing building and background building being removed at once.
        Boolean removedPreviewing = false;

        // Update preview position to follow the mouse cursor
        if (isPreviewing && selectedTexture != null) {
            // Stops previewing building if user right clicks
            if(Gdx.input.isButtonJustPressed(Input.Buttons.RIGHT)){
                isPreviewing = false;
                selectedTexture = null;
                removedPreviewing = true;
            }

            // Makes sure that the mouse is in the center of the building texture
            Vector3 previewPoint = snapBuildingToGrid(Gdx.input.getX() - 
                GameGlobals.SCREEN_BUILDING_SIZE / 3, Gdx.input.getY() + 
                GameGlobals.SCREEN_BUILDING_SIZE / 3);

            previewX = previewPoint.x;
            previewY = previewPoint.y;
        }

        batch.begin();

        // Draw all placed textures
        for (Building building : GameGlobals.BUILDINGS_MAP.getPlacedBuildings()) {
            batch.draw(building.getTexture(), building.getX(), building.getY());
            // Checks if building is under construction
            if (building.getConstructing()) {
                batch.draw(underConstructionTexture, building.getX(), 
                    building.getY(), GameGlobals.SCREEN_BUILDING_SIZE, 
                        (int) (GameGlobals.SCREEN_BUILDING_SIZE * 0.75));

                // Starts or stops timer if needed, doesn't place building if not currently building 
                //buildings.
                if (building.getEndConstructionTime() == -1) {
                    building.setEndConstructionTime(GameGlobals.TIME_REMAINING - 10);
                }
                else if(building.getEndConstructionTime() >= GameGlobals.TIME_REMAINING && 
                    GameGlobals.buildingAllowed) {
                    building.setConstructing(false);
                    GameGlobals.BUILDINGS_MAP.builtBuilding(building);
                }

                // Adds the passed time to the end construction time if not currently building buildings.
                if (!GameGlobals.buildingAllowed) {
                    building.updateEndConstructionTime(delta);
                }
            }
            // Draws fire texture on building if on fire
            if(building.getOnFire()) {
                batch.draw(fireTexture, building.getX(),
                    building.getY(), GameGlobals.SCREEN_BUILDING_SIZE, GameGlobals.SCREEN_BUILDING_SIZE);
            }
        }

        // Draw the preview texture if one is selected
        if (isPreviewing && selectedTexture != null) {
            batch.draw(selectedTexture, previewX, previewY);
        }

        batch.end();

        // Removes the building the user right clicks on
        if(Gdx.input.isButtonJustPressed(Input.Buttons.RIGHT) && !removedPreviewing){
            Vector3 translatedPoint = gameRenderer.translateCoords(Gdx.input.getX(), Gdx.input.getY());
            Building buildingToRemove = GameGlobals.BUILDINGS_MAP.getBuildingAtPoint(translatedPoint.x, 
                translatedPoint.y);

            //If building exists brings up pop-up
            if(buildingToRemove != null) {
                removeBuildingPopUp.setPosition((UIStage.getWidth() - removeBuildingPopUp.getWidth()) / 2, 
                    (UIStage.getHeight() - removeBuildingPopUp.getHeight()) / 2);
                removeBuildingPopUp.setupPopUp(GameGlobals.BUILDINGS_MAP, buildingToRemove);
                UIStage.addActor(removeBuildingPopUp);
            }
        }

        // Stops fire if the building is on fire
        if(Gdx.input.isButtonJustPressed(Input.Buttons.LEFT) && selectedTexture == null){
            Vector3 translatedPoint = gameRenderer.translateCoords(Gdx.input.getX(), Gdx.input.getY());
            Building currentBuilding = GameGlobals.BUILDINGS_MAP.getBuildingAtPoint(translatedPoint.x, 
                translatedPoint.y);
            if(currentBuilding != null) {
                if(currentBuilding.getOnFire()){    
                    SaviourAchievement saviourAchievement = (SaviourAchievement) (
                        GameGlobals.ACHIEVEMENTS.getAchievement(SaviourAchievement.NAME));
                    saviourAchievement.burningBuildingSaved();
                }
                currentBuilding.setOnFire(false);
            }
        }

        // Check for left mouse click to place the texture
        if (Gdx.input.isButtonJustPressed(Input.Buttons.LEFT) && selectedTexture != null) {

            if (!GameGlobals.BUILDINGS_MAP.attemptAddBuilding(currentBuildingInfo, selectedTexture, 
                    previewX, previewY).isEmpty()) {
                // Plays the sound of a building being places
                GameSounds.playPlacedBuilding();

                // The building is no longer being placed
                isPreviewing = false;
                currentBuildingInfo = null;
                selectedTexture = null;
            }
            else {
                //Creates a task to remove the event from the screen after 3s.
                Timer timer = new Timer(3000, new ActionListener(){
                    @Override
                    public void actionPerformed(ActionEvent arg0) {
                        collisionPopUp.remove();
                    }
                }); 
                timer.setRepeats(false);

                UIStage.addActor(collisionPopUp);
                timer.start();

                GameSounds.playPlaceError();
            }

            //Stops menu from opening when placing buildings below buttons
            Vector3 translatedPoint = gameRenderer.translateCoords(Gdx.input.getX(), Gdx.input.getY());
            if (translatedPoint.x >= 616 && translatedPoint.x < 1176 && translatedPoint.y < 136){
                openMenu = false;
            }
        }

    }

    /**
     * Selects a building by building ID
     * @param buildingType buildingType of the building that the user wants to place down
     * @param index int the index of where it is in the dictionary
     */
    public void selectBuilding(BuildingStats.BuildingType buildingType, int index){

        isPreviewing = true;
        BuildingInfo newBuilding = BuildingStats.getInfo(buildingType,index);
        selectedTexture = BuildingStats.getTextureOfBuilding(BuildingStats.buildingDict.get(
                buildingType)[index]);
        currentBuildingInfo = newBuilding;
    }

    /**
     * Snaps the coordinates passed in to the grid.
     * @param x X
     * @param y Y
     * @return Point new coordinates that occur on an intersection of the tiles in the background.
     */
    private Vector3 snapBuildingToGrid(float x, float y){

        int gridSize = 32;
        Vector3 translatedPoint = gameRenderer.translateCoords(x, y);

        float newX = Math.round(translatedPoint.x / gridSize) * gridSize;
        float newY = Math.round(translatedPoint.y / gridSize) * gridSize;

        return new Vector3(newX, newY, 0);
    }

    public boolean getOpenMenu() {
        return openMenu;
    }

    public void setOpenMenu(boolean openMenu) {
        this.openMenu = openMenu;
    }

    /**
     * Disposes building being drawn for garbage collection.
     */
    public void dispose(){
        batch.dispose();
    }

    public void setUIStage(Stage stage) {
        this.UIStage = stage;
    }
}