public class ButtonLoader {

    //button texture
    private static final TextureAtlas groupOne = new TextureAtlas(Gdx.files.internal("btn/button.pack"));
    private static final TextureAtlas groupTwo = new TextureAtlas(Gdx.files.internal("btn/button2.pack"));

    //button skins
    private static final Skin skinOne = new Skin(groupOne);
    private static final Skin skinTwo = new Skin(groupTwo);



    //method to make box button style
    public static TextButtonStyle makeBoxStyle(){
        //make text button style
        TextButtonStyle style = new TextButtonStyle();
        //setup button textures
        style.up = skinOne.getDrawable("box");
        style.down = skinOne.getDrawable("boxpress");
        style.over = skinOne.getDrawable("boxpress");
        //setup font color
        style.fontColor = Color.WHITE;
        style.downFontColor = Color.GRAY;
        style.overFontColor = Color.GRAY;

        return style;
    }

    public static TextButtonStyle makeCircleStyle(){
        //make circle style
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinOne.getDrawable("circle");
        style.down = skinOne.getDrawable("cp");
        style.over = skinOne.getDrawable("cp");
        //setup font color
        style.fontColor = Color.WHITE;
        style.downFontColor = Color.GRAY;
        style.overFontColor = Color.GRAY;

        return style;
    }

    //method to make play button
    public static TextButton makePlay(){
        //get button style
        TextButtonStyle style = makeBoxStyle();
        //set button font
        style.font = FontLoader.font(1.5f);
        //make button
        TextButton play = new TextButton("Play", style);
        //set button size and position
        play.setHeight(Gdx.graphics.getHeight()/4.5f);
        play.setWidth(Gdx.graphics.getWidth()/4.5f);
        play.setPosition(Gdx.graphics.getWidth()/2-play.getWidth()/2,
                Gdx.graphics.getHeight()/2-Gdx.graphics.getHeight()/8);
        //return button
        return play;
    }

    public static TextButton makeLevelButton(String text, int level){
        //get button style
        TextButtonStyle style = makeBoxStyle();
        //set button font
        style.font = FontLoader.font(1f);
        //set button size
        TextButton button = new TextButton(text,style);
        button.setWidth(Gdx.graphics.getWidth()/8);
        button.setHeight(Gdx.graphics.getHeight()/8);
        //set button position
        switch (level){
            case 1:
                button.setPosition(Gdx.graphics.getWidth()/4,
                    Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10);
                break;

            case 2:
                button.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/15,
                    Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10);
                break;

            case 3:
                button.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/8,
                        Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10);
                break;

            case 4:
                button.setPosition(Gdx.graphics.getWidth()/4,
                        Gdx.graphics.getHeight()/3);
                break;

            case 5:
                button.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/15,
                        Gdx.graphics.getHeight()/3);
                break;

            default:
                button.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/8,
                        Gdx.graphics.getHeight()/3);
                break;
        }

        return button;
    }


    public static TextButton makeUp(){
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinOne.getDrawable("up");
        style.down = skinOne.getDrawable("upppress");
        style.over = skinOne.getDrawable("upppress");
        style.font = FontLoader.font(1f);

        TextButton up = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        up.setWidth(w);
        up.setHeight(h);
        up.setPosition(w / 2 + w/2 + h/10 + h/2, h + h+ 2*h);

        return up;
    }

    public static TextButton makeDown(){
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinOne.getDrawable("down");
        style.down = skinOne.getDrawable("downpress");
        style.over = skinOne.getDrawable("downpress");
        style.font = FontLoader.font(1f);

        TextButton down = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        down.setWidth(w);
        down.setHeight(h);
        down.setPosition(w / 2 + w/2 + h/10 + h/2, h/2);

        return down;
    }

    public static TextButton makeRight(){
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinOne.getDrawable("right");
        style.down = skinOne.getDrawable("rightpress");
        style.over = skinOne.getDrawable("rightpress");
        style.font = FontLoader.font(1f);

        TextButton right = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        right.setWidth(w);
        right.setHeight(h);
        right.setPosition(w + w / 4 + h + w, h - h / 4 + w / 3 + h);

        return right;
    }

    public static TextButton makeLeft(){
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinOne.getDrawable("left");
        style.down = skinOne.getDrawable("leftpress");
        style.over = skinOne.getDrawable("leftpress");
        style.font = FontLoader.font(1f);

        TextButton left = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        left.setWidth(w);
        left.setHeight(h);
        left.setPosition(0, h - h / 4 + w / 3 + h);

        return left;
    }

    public static TextButton makeDesc(){
        TextButtonStyle style = new TextButtonStyle();
        style.up = skinTwo.getDrawable("descend1");
        style.down = skinTwo.getDrawable("descend2");
        style.over = skinTwo.getDrawable("descend2");
        style.font = FontLoader.font(1f);

        TextButton dsc = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        dsc.setWidth(w);
        dsc.setHeight(h);
        dsc.setPosition(w / 2 + w/2 + h/10 + h/2, h - h / 4 + w / 3 + h);

        return dsc;
    }

    public static TextButton rx(){

        TextButtonStyle style = new TextButtonStyle();
        style.up = skinTwo.getDrawable("rotatex");
        style.down = skinTwo.getDrawable("rotatexpress");
        style.over = skinTwo.getDrawable("rotatexpress");
        style.font = FontLoader.font(1f);

        TextButton b = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;
        b.setHeight(h);
        b.setWidth(w);

        b.setPosition(Gdx.graphics.getWidth()-w - 3f*w, h - h / 4 + w / 3 + h);

        return b;
    }

    public static TextButton ry(){

        TextButtonStyle style = new TextButtonStyle();
        style.up = skinTwo.getDrawable("rotatey");
        style.down = skinTwo.getDrawable("rotateypress");
        style.over = skinTwo.getDrawable("rotateypress");
        style.font = FontLoader.font(1f);

        TextButton b = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        b.setHeight(h);
        b.setWidth(w);
        b.setPosition(Gdx.graphics.getWidth()-w - 1.75f* w, h - h / 4 + w / 3 + h);

        return b;
    }

    public static TextButton rz(){

        TextButtonStyle style = new TextButtonStyle();
        style.up = skinTwo.getDrawable("rotatez");
        style.down = skinTwo.getDrawable("rotatezpress");
        style.over = skinTwo.getDrawable("rotatezpress");
        style.font = FontLoader.font(1f);

        TextButton b = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        b.setHeight(h);
        b.setWidth(w);
        b.setPosition(Gdx.graphics.getWidth()-w - .5f* w, h - h / 4 + w / 3 + h);

        return b;
    }

    public static TextButton cam(){

        TextButtonStyle style = new TextButtonStyle();
        style.up = skinTwo.getDrawable("cam1");
        style.down = skinTwo.getDrawable("cam2");
        style.over = skinTwo.getDrawable("cam2");
        style.font = FontLoader.font(1f);

        TextButton b = new TextButton("",style);

        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        b.setHeight(h);
        b.setWidth(w);
        b.setPosition(Gdx.graphics.getWidth()-w - w,
                h + 7* h + h);

        return b;
    }

    public static TextButton question(){
        TextButtonStyle circle = makeCircleStyle();
        //set button font
        circle.font = FontLoader.font(.65f);
        //make button
        TextButton play = new TextButton("?", circle);
        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        //set button size and position
        play.setHeight(h);
        play.setWidth(w);
        play.setPosition(0, Gdx.graphics.getHeight()-play.getHeight()-play.getHeight()/5);
        //return button
        return play;
    }

    public static TextButton quit(){
        TextButtonStyle circle = makeCircleStyle();
        //set button font
        circle.font = FontLoader.font(.65f);
        //make button
        TextButton play = new TextButton("quit", circle);
        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        //set button size and position
        play.setHeight(h);
        play.setWidth(w);
        play.setPosition(Gdx.graphics.getWidth()-w - w,
                h + 5.5f* h + h);
        //return button
        return play;
    }

    public static TextButton yes(){
        TextButtonStyle circle = makeCircleStyle();
        //set button font
        circle.font = FontLoader.font(.65f);
        //make button
        TextButton play = new TextButton("Y", circle);
        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        //set button size and position
        play.setHeight(h);
        play.setWidth(w);
        play.setPosition(Gdx.graphics.getWidth()-w - w + w/15,
                h + 3.5f* h + h);
        //return button
        return play;
    }

    public static TextButton no(){
        TextButtonStyle circle = makeCircleStyle();
        //set button font
        circle.font = FontLoader.font(.65f);
        //make button
        TextButton play = new TextButton("N", circle);
        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;

        //set button size and position
        play.setHeight(h);
        play.setWidth(w);
        play.setPosition(Gdx.graphics.getWidth()-w - 2.5f* w,
                h + 3.5f* h + h);
        //return button
        return play;
    }

    public static Button pause(){
        Button.ButtonStyle p = new Button.ButtonStyle(skinTwo.getDrawable("pause1"),
                skinTwo.getDrawable("play1"),skinTwo.getDrawable("play1"));

        Button pause = new Button(p);
        float w = Gdx.graphics.getWidth() / 14;
        float h = Gdx.graphics.getHeight() / 12;
        pause.setHeight(h);
        pause.setWidth(w);
        pause.setPosition(Gdx.graphics.getWidth()-w - w,
                h + 8.5f* h + h);

        return pause;
    }

    public static TextButton replay(){
        TextButtonStyle box = makeBoxStyle();
        box.font = FontLoader.font(.8f);

        TextButton replay = new TextButton("Replay",box);
        replay.setWidth(Gdx.graphics.getWidth()/6);
        replay.setHeight(Gdx.graphics.getHeight()/8);
        replay.setPosition(Gdx.graphics.getWidth()/4, Gdx.graphics.getHeight()/4);

        return replay;
    }

    public static TextButton levelss(){
        TextButtonStyle box = makeBoxStyle();
        box.font = FontLoader.font(.8f);

        TextButton levels = new TextButton("Levels",box);
        levels.setWidth(Gdx.graphics.getWidth()/7);
        levels.setHeight(Gdx.graphics.getHeight()/8);
        levels.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/10, Gdx.graphics.getHeight()/4);

        return levels;
    }



    public static void dispose(){
        groupOne.dispose();
        groupTwo.dispose();
        skinOne.dispose();
        skinTwo.dispose();
    }


} public class LabelLoader {

    //heading label style
    private static final LabelStyle headingStyle = new LabelStyle(FontLoader.font(2.8f), Color.WHITE);
    private static final LabelStyle bodyStyle = new LabelStyle(FontLoader.font(1.2f), Color.WHITE);
    private static final LabelStyle scoreStyle = new LabelStyle(FontLoader.font(.75f),Color.WHITE);

    //method to make heading labels
    public static Label headingLabel(String title){
        Label heading = new Label(title,headingStyle);
        heading.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/4,
                Gdx.graphics.getHeight()/2 + Gdx.graphics.getHeight()/4);
        return heading;
    }

    //method to make score labels
    public static Label levelScore(int level, String tag){
        //get score from tag
        int score = ScoreManager.getScore(tag);
        //make label
        Label label = new Label(" Best-"+score,scoreStyle);
        //set label position
        switch (level){
            case 1:
                label.setPosition(Gdx.graphics.getWidth()/4-Gdx.graphics.getWidth()/30,
                        Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10-Gdx.graphics.getHeight()/14);
                break;

            case 2:
                label.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/15-Gdx.graphics.getWidth()/40,
                        Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10-Gdx.graphics.getHeight()/14);
                break;

            case 3:
                label.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/8-Gdx.graphics.getWidth()/30,
                        Gdx.graphics.getHeight()/2+Gdx.graphics.getHeight()/10-Gdx.graphics.getHeight()/14);
                break;

            case 4:
                label.setPosition(Gdx.graphics.getWidth()/4-Gdx.graphics.getWidth()/30,
                        Gdx.graphics.getHeight()/3-Gdx.graphics.getHeight()/14);
                break;

            case 5:
                label.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/15-Gdx.graphics.getWidth()/40,
                        Gdx.graphics.getHeight()/3-Gdx.graphics.getHeight()/14);
                break;

            default:
                label.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/8-Gdx.graphics.getWidth()/30,
                        Gdx.graphics.getHeight()/3-Gdx.graphics.getHeight()/14);
                break;
        }

        return label;
    }

    public static Label extraLevel(){
        Label ex = new Label("Score at least one point in any level to play",scoreStyle);
        ex.setWrap(true);
        ex.setWidth(Gdx.graphics.getWidth()/4);
        ex.setPosition(Gdx.graphics.getWidth()/2 + Gdx.graphics.getWidth()/8,
                Gdx.graphics.getHeight()/14);

        return ex;
    }

    public static Label scoreL(){
        Label scoreLabel = new Label("0",headingStyle);
        float h = Gdx.graphics.getHeight()/12;
        scoreLabel.setPosition(Gdx.graphics.getWidth()/2,
                h + 7.4f* h + h);

        return scoreLabel;

    }

    public static Label txtL(){
        Label scoreLabel = new Label("Level 1",bodyStyle);
        float h = Gdx.graphics.getHeight()/12;
        scoreLabel.setPosition(Gdx.graphics.getWidth()/25,
                Gdx.graphics.getHeight()-h-h-h);

        return scoreLabel;
    }

    public static Label gameScore(int score){
        Label scoreLabel = new Label("Scored: "+score,bodyStyle);
        float h = Gdx.graphics.getHeight()/12;
        scoreLabel.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/7,
                Gdx.graphics.getHeight()/2 + Gdx.graphics.getHeight()/8 );

        return scoreLabel;
    }

    public static Label highScore(int score){
        Label scoreLabel = new Label("Best: "+score,bodyStyle);
        float h = Gdx.graphics.getHeight()/12;
        scoreLabel.setPosition(Gdx.graphics.getWidth()/2 - Gdx.graphics.getWidth()/7,
                Gdx.graphics.getHeight()/2);

        return scoreLabel;
    }

} public class TextureLoader {

    //splash image texture
    private static Texture splash = new Texture("img/splash.png");
    //tutorial image texture
    private static Texture tuts = new Texture("img/tutorial.png");
    private static Texture h2o = new Texture("img/wat.png");
    private static Texture ch4 = new Texture("img/ch4.png");

    //method to load splash texture
    public static Texture getSplash(){
        //set a linear filter to clean image
        splash.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        //return image
        return splash;
    }

    //method to load tutorial texture
    public static Texture getTut(){
        //set a linear filter to clean image
        tuts.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        //return image
        return tuts;
    }

    //method to load tutorial texture
    public static Texture getWat(){
        //set a linear filter to clean image
        h2o.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        //return image
        return h2o;
    }

    //method to load tutorial texture
    public static Texture getCh4(){
        //set a linear filter to clean image
        ch4.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        //return image
        return ch4;
    }

    //method to dispose splash texture
    public static void disposeSplash(){
        splash.dispose();
    }

    public static void disposeTuts(){
        tuts.dispose();
    }

    public static void disposeWat(){
        h2o.dispose();
    }

    public static void disposeCh4(){
        ch4.dispose();
    }
} public class ScoreManager {

    //preference to store score
    private static Preferences scoreManager = Gdx.app.getPreferences("com.bms.chemtris");

    //level strings
    public static final String LEVEL_1 = "level1",
            LEVEL_2 = "level2",
            LEVEL_3 = "level3",
            LEVEL_4 = "level4",
            LEVEL_5 = "level5",
            LEVEL_6 = "level6",
            H2O = "water",
            CH4 = "methane";

    //constructor
    public static void setup(){
        //initializing high scores for each level
        if(!scoreManager.contains(LEVEL_1)){
            scoreManager.putInteger(LEVEL_1,0);
        }
        if(!scoreManager.contains(LEVEL_2)){
            scoreManager.putInteger(LEVEL_2,0);
        }
        if(!scoreManager.contains(LEVEL_3)){
            scoreManager.putInteger(LEVEL_3,0);
        }
        if(!scoreManager.contains(LEVEL_4)){
            scoreManager.putInteger(LEVEL_4,0);
        }
        if(!scoreManager.contains(LEVEL_5)){
            scoreManager.putInteger(LEVEL_5,0);
        }
        if(!scoreManager.contains(LEVEL_6)){
            scoreManager.putInteger(LEVEL_6,0);
        }
        if(!scoreManager.contains(H2O)){
            scoreManager.putInteger(H2O,0);
        }
        if(!scoreManager.contains(CH4)){
            scoreManager.putInteger(CH4,0);
        }
    }

    //method to return high score of a level
    public static int getScore(String key){
        return scoreManager.getInteger(key);
    }

    //method to set high score of a level
    public static void setScore(String key, int score){
        scoreManager.putInteger(key,score);
        scoreManager.flush();
    }

    //method to check if ever scored
    public static boolean scored(){
        if(scoreManager.getInteger(LEVEL_1) > 0){
            return true;
        }
        else if(scoreManager.getInteger(LEVEL_2) > 0){
            return true;
        }
        else if(scoreManager.getInteger(LEVEL_3) > 0){
            return true;
        }
        else if(scoreManager.getInteger(LEVEL_4) > 0){
            return true;
        }
        else if(scoreManager.getInteger(LEVEL_5) > 0){
            return true;
        }
        else if(scoreManager.getInteger(LEVEL_6) > 0){
            return true;
        }
        else{
            return false;
        }
    }
} public class Checker {

    private Storage storage;
    private int score, multiplier;
    private int rows;

    //constructor
    public Checker(Storage s, int size){
        rows = size * size;
        storage = s;
        score = 0;
        multiplier = 1;
    }

    public void checkLevel1(){
        int level1 = storage.level_1.size;

        while (level1 == rows){
            storage.swap(storage.level_1,storage.level_2);
            storage.swap_f(storage.level_1_w,storage.level_2_w);

            storage.swap(storage.level_2,storage.level_3);
            storage.swap_f(storage.level_2_w,storage.level_3_w);

            storage.swap(storage.level_3,storage.level_4);
            storage.swap_f(storage.level_3_w,storage.level_4_w);

            storage.swap(storage.level_4,storage.level_5);
            storage.swap_f(storage.level_4_w,storage.level_5_w);

            level1 = storage.level_1.size;

            score += 1;
            multiplier += 1;
        }
    }

    public void checkLevel2() {
        int level2 = storage.level_2.size;

        while (level2 == rows){

            storage.swap(storage.level_2,storage.level_3);
            storage.swap_f(storage.level_2_w,storage.level_3_w);

            storage.swap(storage.level_3,storage.level_4);
            storage.swap_f(storage.level_3_w,storage.level_4_w);

            storage.swap(storage.level_4,storage.level_5);
            storage.swap_f(storage.level_4_w,storage.level_5_w);

            level2 = storage.level_2.size;

            score += 1;
            multiplier += 1;
        }
    }

    public void checkLevel3(){

        int level3 = storage.level_3.size;

        while (level3 == rows){
            storage.swap(storage.level_3,storage.level_4);
            storage.swap_f(storage.level_3_w,storage.level_4_w);

            storage.swap(storage.level_4,storage.level_5);
            storage.swap_f(storage.level_4_w,storage.level_5_w);

            level3 = storage.level_1.size;

            score += 1;
            multiplier += 1;
        }
    }

    public void checkLevel4(){
        int level4 = storage.level_4.size;

        while (level4 == rows){
            storage.swap(storage.level_4,storage.level_5);
            storage.swap_f(storage.level_4_w,storage.level_5_w);

            level4 = storage.level_4.size;

            score += 1;
            multiplier += 1;
        }
    }

    public void checkLevel5(){
        if(storage.level_5.size == rows){
            storage.clear_row(5);
            ++score;
            ++multiplier;
        }
    }



    public int run(){
        for(int i = 0; i < 3; ++i){
            checkLevel1();
            checkLevel2();
            checkLevel3();
            checkLevel4();
            checkLevel5();
        }

        score *= multiplier;
        multiplier = 1;

        return score;
    }


} public class Controller {

    private Storage storage;
    private float time_elasped_anim;
    private float time_elasped_delay;
    private float descend_time;
    private ModelInstance box;

    //return codes
    public static final int ANIMATION = -10, CONTINUE = -20, DONE = -30, OVER = -100;

    public Controller(Storage store, float descend, ModelInstance platform_box){
        //initialize members
        storage = store;
        time_elasped_anim = 0f;
        time_elasped_delay = 0f;
        descend_time = descend;
        box = platform_box;
    }

    public int run(float delta, GameModel current) {


        //for desktop version
        if(Gdx.input.isKeyJustPressed(Input.Keys.DPAD_UP)){
            current.moveUp();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.DPAD_DOWN)){
            current.moveDown();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.DPAD_RIGHT)){
            current.moveRight();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.DPAD_LEFT)){
            current.moveLeft();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.A)){
            current.rotateX();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.S)){
            current.rotateY();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.D)){
            current.moveUp();
        }

        if(Gdx.input.isKeyJustPressed(Input.Keys.ENTER)){
            current.descend();
        }

        //increment descend time
        time_elasped_delay += delta;


        //if descend time has been meet or block can no longer go down
        if (time_elasped_delay >= descend_time || !current.canMove(0, -1, 0)) {
            if (current.canMove(0, -1, 0)) {
                current.descend();
                time_elasped_delay = 0f;
                return CONTINUE;
            }
            else {
                time_elasped_anim += delta;
                if (time_elasped_anim < .65f){
                    //change color to red
                    box.materials.get(0).set(ColorAttribute.createDiffuse(255 / 255f, 99 / 255f, 71 / 255f, 1f));
                    return ANIMATION;
                }
                else {
                    //change color to white
                    box.materials.get(0).set(ColorAttribute.createDiffuse(Color.WHITE));
                    time_elasped_delay = 0f;
                    time_elasped_anim = 0f;
                    return DONE;
                }
            }
        }
        else {
            return CONTINUE;
        }
    }

} public class Spawner {

    private float r = 70/255f, g = 70/255f, b = 70/255f;
    private int spawn_piece;
    public GameModel current;
    public boolean done, finished_spawning;
    private float time_elasped;
    private Random random;
    private static boolean [][][] matrix;
    private PerspectiveCamera camera;

    //constructor
    public Spawner(int pieces, boolean [][][] mx, PerspectiveCamera cam){
        spawn_piece = pieces;
        done = false;
        time_elasped = 0f;
        random = new Random();
        finished_spawning = true;
        matrix = mx;
        camera = cam;

    }

    //method to get random piece
    private void getPiece(){
        finished_spawning = false;
        int randNum = random.nextInt(spawn_piece);

        switch (randNum) {
            case 0:
                current = new Box(camera, matrix);
                break;
            case 1:
                current = new BoxTwo(camera, matrix);
                break;
            case 2:
                current = new BoxThree(camera, matrix);
                break;
            case 3:
                current = new Column(camera, matrix);
                break;
            case 4:
                current = new ColumnTwo(camera, matrix);
                break;
            case 5:
                current = new LShape(camera,matrix);
                break;
            case 6:
                current = new LShapeTwo(camera,matrix);
                break;

            default:
                current = new Box(camera,matrix);
                break;
        }

        done = true;

    }

    public GameModel run(float delta) {
        //check if piece finished spawning
        if (!done) {
            getPiece();
        }

        //animate fading in effect for pieces
        if (time_elasped == 0f) {
            for (ModelInstance part : current.parts) {
                part.materials.get(0).set(ColorAttribute.createDiffuse(r, g, b, 1f));
            }

        }

        time_elasped += delta;

        if (time_elasped < 1.25f) {
            r += 10 / 255f;
            g += 10 / 255f;
            b += 10 / 255f;
            for (ModelInstance part : current.parts) {
                part.materials.get(0).set(ColorAttribute.createDiffuse(r, g, b, 1f));
            }
            return null;
        }
        else {
            r = 255 / 255f;
            g = 255 / 255f;
            b = 255 / 255f;
            for (ModelInstance part : current.parts) {
                part.materials.get(0).set(ColorAttribute.createDiffuse(r, g, b, 1f));
            }
            done = false;
            finished_spawning = true;
            time_elasped = 0f;
            r = 70/255f;
            g = 70/255f;
            b = 70/255f;

            //when animation is done return the piece
            return current;

        }
    }


} public class Storage {

    //game pieces
    public Array<ModelInstance> level_1;
    public Array<ModelInstance> level_2;
    public Array<ModelInstance> level_3;
    public Array<ModelInstance> level_4;
    public Array<ModelInstance> level_5;

    public Array<ModelInstance> level_1_w;
    public Array<ModelInstance> level_2_w;
    public Array<ModelInstance> level_3_w;
    public Array<ModelInstance> level_4_w;
    public Array<ModelInstance> level_5_w;

    private static Environment environment; //rendering environment
    public int cc_type;

    private int frame;

    //ModelBatch Renderer and Wireframe shader
    private static final ModelBatch fillRenderer = new ModelBatch();
    private static final ModelBatch wireRenderer = new ModelBatch(new DefaultShaderProvider() {
        @Override
        protected Shader createShader(Renderable renderable) {
            return new WireframeShader(renderable, config);
        }
    });

    //colors
    private Color dark = new Color(255/255f,8/255f,0f,1f);
    private Color lDark = new Color(255/255f,69/255f,0f,1f);
    private Color medium = new Color(255/255f,127/255f,0f,1f);
    private Color normal = new Color(255/255f,155/255f,0f,1f);
    private Color light = new Color(255/255f,204/255f,0f,1f);

    //perspective camera
    private PerspectiveCamera camera;

    //collision matrix
    private static boolean [][][] matrix;

    private static final Random rand = new Random();


    //constructor
    public Storage(boolean [][][] collision, PerspectiveCamera cam){
        camera = cam;
        matrix = collision;
        frame = 0;

        level_1 =  new Array<ModelInstance>();
        level_2 =  new Array<ModelInstance>();
        level_3 =  new Array<ModelInstance>();
        level_4 =  new Array<ModelInstance>();
        level_5 =  new Array<ModelInstance>();

        level_1_w =  new Array<ModelInstance>();
        level_2_w =  new Array<ModelInstance>();
        level_3_w =  new Array<ModelInstance>();
        level_4_w =  new Array<ModelInstance>();
         level_5_w =  new Array<ModelInstance>();

        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));

        cc_type = rand.nextInt(3);

        if(cc_type == 0){
            dark = new Color(255/255f,8/255f,0f,1f);
            lDark = new Color(255/255f,69/255f,0f,1f);
            medium = new Color(255/255f,127/255f,0f,1f);
            normal = new Color(255/255f,155/255f,0f,1f);
            light = new Color(255/255f,204/255f,0f,1f);
        }
        else if(cc_type == 1){
            dark = new Color(115/255f,158/255f,225/255f,1f);
            lDark = new Color(138/255f,146/255f,236/255f,1f);
            medium = new Color(150/255f,140/255f,241/255f,1f);
            normal = new Color(161/255f,134/255f,247/255f,1f);
            light = new Color(173/255f,128/255f,252/255f,1f);
        }else{
            dark = new Color(224/255f,240/255f,53/255f,1f);
            lDark = new Color(199/255f,240/255f,64/255f,1f);
            medium = new Color(175/255f,240/255f,76/255f,1f);
            normal = new Color(114/255f,240/255f,105/255f,1f);
            light = new Color(53/255f,240/255f,134/255f,1f);
        }
    }

    public boolean add(GameModel model){

        try {

            for (int i = 0; i < model.parts.size; ++i) {

                Vector3 v = new Vector3();
                model.parts.get(i).transform.getTranslation(v);

                ModelInstance p = model.parts.get(i);
                ModelInstance f = model.frame.get(i);

                int x = Math.round(v.x);
                int z = Math.round(v.z);
                int y = Math.round(v.y);

                if (y < 6) {
                    matrix[x][y][z] = true;
                }

                switch (y) {
                    case 1:
                        level_1.add(p);
                        p.materials.get(0).set(ColorAttribute.createDiffuse(dark));
                        level_1_w.add(f);
                        break;

                    case 2:
                        level_2.add(p);
                        p.materials.get(0).set(ColorAttribute.createDiffuse(lDark));
                        level_2_w.add(f);
                        break;

                    case 3:
                        level_3.add(p);
                        p.materials.get(0).set(ColorAttribute.createDiffuse(medium));
                        level_3_w.add(f);
                        break;

                    case 4:
                        level_4.add(p);
                        p.materials.get(0).set(ColorAttribute.createDiffuse(normal));
                        level_4_w.add(f);
                        break;

                    case 5:
                        level_5.add(p);
                        p.materials.get(0).set(ColorAttribute.createDiffuse(light));
                        level_5_w.add(f);
                        break;

                    default:
                        return false;
                }
            }

            return true;
        }catch (Exception e){
            return false;
        }

    }

    public void shift_row(Array<ModelInstance> level, Array<ModelInstance> addRow, boolean wire){
        try {
            for (ModelInstance part : level) {

                Vector3 v = new Vector3();
                part.transform.getTranslation(v);

                int x = Math.round(v.x);
                int z = Math.round(v.z);
                int y = Math.round(v.y);

                part.transform.translate(0f, -1f, 0f);

                if (!wire) {
                    matrix[x][y][z] = false;
                    matrix[x][y - 1][z] = true;
                }

                addRow.add(part);
            }

            level.clear();
        }catch (Exception e){
            return;
        }
    }

    public void clear_row(int row){
        switch (row){
            case 1:
                shift_row(level_2,level_1,false);
                shift_row(level_2_w,level_1_w,true);
                shift_row(level_3,level_2,false);
                shift_row(level_3_w,level_2_w,true);
                shift_row(level_4,level_3,false);
                shift_row(level_4_w,level_3_w,false);
                shift_row(level_5,level_4,false);
                shift_row(level_5_w,level_4_w,false);
                break;

            case 2:
                shift_row(level_3,level_2,false);
                shift_row(level_3_w,level_2_w,true);
                shift_row(level_4,level_3,false);
                shift_row(level_4_w,level_3_w,false);
                shift_row(level_5,level_4,false);
                shift_row(level_5_w,level_4_w,false);
                break;

            case 3:
                shift_row(level_4,level_3,false);
                shift_row(level_4_w,level_3_w,false);
                shift_row(level_5,level_4,false);
                shift_row(level_5_w,level_4_w,false);
                break;

            case 4:
                shift_row(level_5,level_4,false);
                shift_row(level_5_w,level_4_w,false);
                break;

            case 5:
                for(int i = 0;  i < level_5.size; ++i){
                    Vector3 v = new Vector3();
                    level_5.get(i).transform.getTranslation(v);

                    int x = Math.round(v.x);
                    int z = Math.round(v.z);
                    int y = Math.round(v.y);

                    matrix[x][y][z] = false;
                }

                level_5.clear();
                level_5_w.clear();
                break;

            default:
                return;
        }
    }



    public void swap(Array<ModelInstance>first,Array<ModelInstance>second){

        for(int i = 0; i < first.size; i++){
            Vector3 t  = new Vector3();
            first.get(i).transform.getTranslation(t);
            int x = Math.round(t.x);
            int y = Math.round(t.y);
            int z = Math.round(t.z);
            matrix[x][y][z] = false;
        }
        first.clear();
        for(int i = 0; i < second.size; i++){
            Vector3 t  = new Vector3();
            second.get(i).transform.getTranslation(t);
            int x = Math.round(t.x);
            int y = Math.round(t.y);
            int z = Math.round(t.z);
            matrix[x][y][z] = false;
            second.get(i).transform.translate(0,-1f,0);
            first.add(second.get(i));
            matrix[x][y-1][z]= true;
        }
        second.clear();

    }

    public void swap_f(Array<ModelInstance>first,Array<ModelInstance>second){

        for(int i = 0; i < first.size; i++){
            Vector3 t  = new Vector3();
            first.get(i).transform.getTranslation(t);
            int x = Math.round(t.x);
            int y = Math.round(t.y);
            int z = Math.round(t.z);
        }
        first.clear();
        for(int i = 0; i < second.size; i++){
            Vector3 t  = new Vector3();
            second.get(i).transform.getTranslation(t);
            int x = Math.round(t.x);
            int y = Math.round(t.y);
            int z = Math.round(t.z);
            second.get(i).transform.translate(0,-1f,0);
            first.add(second.get(i));
        }
        second.clear();

    }


    public void render(){
        for(ModelInstance part : level_1){
            part.materials.get(0).set(ColorAttribute.createDiffuse(dark));
            if(frame == 0){
                part.transform.translate(-0.1f,0,0);
            }else{
                part.transform.translate(0.1f,0,0);
            }
        }

        for(ModelInstance part : level_2){
            part.materials.get(0).set(ColorAttribute.createDiffuse(lDark));
            if(frame == 0){
                part.transform.translate(-0.1f,0,0);
            }else{
                part.transform.translate(0.1f,0,0);
            }
        }

        for(ModelInstance part : level_3){
            part.materials.get(0).set(ColorAttribute.createDiffuse(medium));
            if(frame == 0){
                part.transform.translate(-0.1f,0,0);
            }else{
                part.transform.translate(0.1f,0,0);
            }
        }

        for(ModelInstance part : level_4){
            part.materials.get(0).set(ColorAttribute.createDiffuse(normal));
            if(frame == 0){
                part.transform.translate(-0.1f,0,0);
            }else{
                part.transform.translate(0.1f,0,0);
            }
        }

        for(ModelInstance part : level_5){
            part.materials.get(0).set(ColorAttribute.createDiffuse(light));
            if(frame == 0){
                part.transform.translate(-0.1f,0,0);
            }else{
                part.transform.translate(0.1f,0,0);
            }
        }

        fillRenderer.begin(camera);
        fillRenderer.render(level_1,environment);
        fillRenderer.render(level_2,environment);
        fillRenderer.render(level_3,environment);
        fillRenderer.render(level_4,environment);
        fillRenderer.render(level_5,environment);
        fillRenderer.end();

        wireRenderer.begin(camera);
        wireRenderer.render(level_1_w,environment);
        wireRenderer.render(level_2_w,environment);
        wireRenderer.render(level_3_w,environment);
        wireRenderer.render(level_4_w,environment);
        wireRenderer.render(level_5_w,environment);
        wireRenderer.end();

        if(frame == 0){
            frame = 1;
        }else{
            frame = 0;
        }


    }






} public abstract class GameModel {

    public Array<ModelInstance> parts; //model parts
    public Array<ModelInstance>shadow; //model shadow
    public Array<ModelInstance>frame; //model frame

    //model builder to make the model
    private static final ModelBuilder builder = new ModelBuilder();

    //3d models to make up the parts
    public static final Model white = builder.createBox(
            1f,1f,1f,
            new Material(ColorAttribute.createDiffuse(Color.WHITE)),
            VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
    );

    public static final Model gray = builder.createBox(
            1f,1f,1f,
            new Material(ColorAttribute.createDiffuse(105/255f,105/255f,105/255f,1f)),
            VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
    );


    public static final Model wire = builder.createBox(
            1.01f,1.01f,1.01f,
            new Material(ColorAttribute.createDiffuse(184/255f,182/255f,182/255f,1f)),
            VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
    );

    private static Environment environment; //rendering environment

    //ModelBatch Renderer and Wireframe shader
    private static final ModelBatch fillRenderer = new ModelBatch();
    private static final ModelBatch wireRenderer = new ModelBatch(new DefaultShaderProvider() {
        @Override
        protected Shader createShader(Renderable renderable) {
            return new WireframeShader(renderable, config);
        }
    });

    //collision array to check
    private static boolean [][][] matrix;

    //perspective camera
    private PerspectiveCamera camera;

    //constructor
    public GameModel(PerspectiveCamera cam, boolean [][][] collision){
        //setup the model instances
        parts = new Array<ModelInstance>();
        frame = new Array<ModelInstance>();
        shadow = new Array<ModelInstance>();

        //setup environment
        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));

        //setup collision matrix
        matrix = collision;

        //setup camera
        camera = cam;



    }

    //method to check if models can move to specific location
    public boolean canMove(int xOffset, int yOffset, int zOffset){

        try {

            for (ModelInstance part : parts) {
                Vector3 v = new Vector3();
                part.transform.getTranslation(v);

                int x = Math.round(v.x);
                int z = Math.round(v.z);
                int y = Math.round(v.y);

                if(x+xOffset < 0){
                    return false;
                }
                if(z + zOffset < 0){
                    return false;
                }
                if(x+xOffset > matrix.length-1){
                    return false;
                }
                if(z+zOffset > matrix[0][0].length-1){
                    return false;
                }
                if (y == 1) {
                    return false;
                } else if (matrix[x + xOffset][y + yOffset][z + zOffset]) {
                    return false;
                }
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
            return false;
        }

        return true;

    }

    public void descend(){
        if (canMove(0,-1,0)){
            for(int i = 0; i < parts.size; ++i){
                parts.get(i).transform.translate(0,-1f,0);
                frame.get(i).transform.translate(0,-1f,0);
            }
        }
    }

    public boolean canMovePart(int xOffset, int yOffset, int zOffset){
        Vector3 v = new Vector3();
        parts.get(0).transform.getTranslation(v);

        int x = Math.round(v.x);
        int z = Math.round(v.z);
        int y = Math.round(v.y);

        if(x+xOffset < 0){
            return false;
        }
        if(z + zOffset < 0){
            return false;
        }
        if(x+xOffset > matrix.length-1){
            return false;
        }
        if(z+zOffset > matrix[0][0].length-1){
            return false;
        }
        if (matrix[x + xOffset][y + yOffset][z + zOffset]) {
            return false;
        }


        return true;
    }

    public void movePart(int part, float x, float y, float z){
        parts.get(part).transform.set(parts.get(0).transform);
        parts.get(part).transform.translate(x,y,z);
    }

    public boolean canMovePartW (int part, int xOffset, int yOffset, int zOffset){
        Vector3 v = new Vector3();
        parts.get(part).transform.getTranslation(v);

        int x = Math.round(v.x);
        int z = Math.round(v.z);
        int y = Math.round(v.y);

        if(x+xOffset < 0){
            return false;
        }
        if(z + zOffset < 0){
            return false;
        }
        if(x+xOffset > matrix.length-1){
            return false;
        }
        if(z+zOffset > matrix[0][0].length-1){
            return false;
        }
        if (matrix[x + xOffset][y + yOffset][z + zOffset]) {
            return false;
        }

        return true;
    }

    public void movePartW(int part, float x, float y, float z){
        parts.get(part).transform.translate(x,y,z);
    }

    public boolean moveShadow(int xOffset, int yOffset, int zOffset){
        try {

            for (ModelInstance part : shadow) {
                Vector3 v = new Vector3();
                part.transform.getTranslation(v);

                int x = Math.round(v.x);
                int z = Math.round(v.z);
                int y = Math.round(v.y);

                if(x+xOffset < 0){
                    return false;
                }
                if(z + zOffset < 0){
                    return false;
                }
                if(x+xOffset > matrix.length-1){
                    return false;
                }
                if(z+zOffset > matrix[0][0].length-1){
                    return false;
                }
                if (y == 1) {
                    return false;
                } else if (matrix[x + xOffset][y + yOffset][z + zOffset]) {
                    return false;
                }
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
            return false;
        }

        return true;
    }

    //method to render the parts
    public void renderParts(){
        //render parts
        fillRenderer.begin(camera);
        fillRenderer.render(parts,environment);
        fillRenderer.end();
    }

    //method to render the shadow
    public void renderShadow(){
        //render shadow
        fillRenderer.begin(camera);
        fillRenderer.render(shadow,environment);
        fillRenderer.end();

    }

    //method to render wireframe
    public void renderWireframe(){
        //render wireframe shapes
        wireRenderer.begin(camera);
        wireRenderer.render(frame,environment);
        wireRenderer.end();
    }



    //method to move right
    public void moveRight(){
        //front
        if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z > 0){
            if(canMove(1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(1f,0f,0f);
                    shadow.get(i).transform.translate(1f,0f,0f);
                    frame.get(i).transform.translate(1f,0f,0f);
                }
            }
        }
        //back
        else if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z < 0){
            if(canMove(1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(1f,0f,0f);
                    shadow.get(i).transform.translate(1f,0f,0f);
                    frame.get(i).transform.translate(1f,0f,0f);
                }
            }
        }
        //left
        else if(camera.position.x < 0){
            if(canMove(0,0,1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,1f);
                    shadow.get(i).transform.translate(0f,0f,1f);
                    frame.get(i).transform.translate(0f,0f,1f);
                }
            }
        }
        //right
        else{
            if(canMove(0,0,-1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,-1f);
                    shadow.get(i).transform.translate(0f,0f,-1f);
                    frame.get(i).transform.translate(0f,0f,-1f);
                }
            }
        }
    }

    //method to move left
    public void moveLeft(){
        //front
        if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z > 0){
            if(canMove(-1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(-1f,0f,0f);
                    shadow.get(i).transform.translate(-1f,0f,0f);
                    frame.get(i).transform.translate(-1f,0f,0f);
                }
            }
        }
        //back
        else if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z < 0){
            if(canMove(-1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(-1f,0f,0f);
                    shadow.get(i).transform.translate(-1f,0f,0f);
                    frame.get(i).transform.translate(-1f,0f,0f);
                }
            }
        }
        //left
        else if(camera.position.x < 0){
            if(canMove(0,0,-1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,-1f);
                    shadow.get(i).transform.translate(0f,0f,-1f);
                    frame.get(i).transform.translate(0f,0f,-1f);
                }
            }
        }
        //right
        else{
            if(canMove(0,0,1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,1f);
                    shadow.get(i).transform.translate(0f,0f,1f);
                    frame.get(i).transform.translate(0f,0f,1f);
                }
            }
        }
    }

    //method to move up
    public void moveUp(){
        //front
        if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z > 0){
            if(canMove(0,0,-1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,-1f);
                    shadow.get(i).transform.translate(0f,0f,-1f);
                    frame.get(i).transform.translate(0f,0f,-1f);
                }
            }
        }
        //back
        else if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z < 0){
            if(canMove(0,0,1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,1f);
                    shadow.get(i).transform.translate(0f,0f,1f);
                    frame.get(i).transform.translate(0f,0f,1f);
                }
            }
        }
        //left
        else if(camera.position.x < 0){
            if(canMove(1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(1f,0f,0f);
                    shadow.get(i).transform.translate(1f,0f,0f);
                    frame.get(i).transform.translate(1f,0f,0f);
                }
            }
        }
        //right
        else{
            if(canMove(-1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(-1f,0f,0f);
                    shadow.get(i).transform.translate(-1f,0f,0f);
                    frame.get(i).transform.translate(-1f,0f,0f);
                }
            }
        }
    }

    //method to move down
    public void moveDown(){
        //front
        if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z > 0){
            if(canMove(0,0,1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,1f);
                    shadow.get(i).transform.translate(0f,0f,1f);
                    frame.get(i).transform.translate(0f,0f,1f);
                }
            }
        }
        //back
        else if(camera.position.x >= 0 && camera.position.x < 15 && camera.position.z < 0){
            if(canMove(0,0,-1)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(0f,0f,-1f);
                    shadow.get(i).transform.translate(0f,0f,-1f);
                    frame.get(i).transform.translate(0f,0f,-1f);
                }
            }
        }
        //left
        else if(camera.position.x < 0){
            if(canMove(-1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(-1f,0f,0f);
                    shadow.get(i).transform.translate(-1f,0f,0f);
                    frame.get(i).transform.translate(-1f,0f,0f);
                }
            }
        }
        //right
        else{
            if(canMove(1,0,0)){
                for(int i = 0; i < parts.size; ++i){
                    parts.get(i).transform.translate(1f,0f,0f);
                    shadow.get(i).transform.translate(1f,0f,0f);
                    frame.get(i).transform.translate(1f,0f,0f);
                }
            }
        }
    }

    //method to trace shadow
    public void traceShadow(){
        for(int i = 0; i < parts.size; ++i){
            shadow.get(i).transform.set(parts.get(i).transform);
            frame.get(i).transform.set(parts.get(i).transform);
        }

        while (moveShadow(0,-1,0)){
            for(int i = 0; i < shadow.size; ++i){
                shadow.get(i).transform.translate(0f,-1f,0f);
            }
        }

        while (moveShadow(0,-1,0)){
            for(int i = 0; i < shadow.size; ++i){
                shadow.get(i).transform.translate(0f,-1f,0f);
            }
        }

        while (moveShadow(0,-1,0)){
            for(int i = 0; i < shadow.size; ++i){
                shadow.get(i).transform.translate(0f,-1f,0f);
            }
        }


    }

    //method to rotate in x direction
    public abstract void rotateX();

    //method to rotate in y direction
    public abstract void rotateY();

    //method to rotate in z direction
    public abstract void rotateZ();

} public class Platform {

    //platform parts
    public Array<ModelInstance> parts;
    //platform wires
    public Array<ModelInstance> wires;
    //platform box
    public ModelInstance box;

    private static Environment environment; //rendering environment

    //ModelBatch Renderer and Wireframe shader
    private static final ModelBatch fillRenderer = new ModelBatch();
    private static final ModelBatch wireRenderer = new ModelBatch(new DefaultShaderProvider() {
        @Override
        protected Shader createShader(Renderable renderable) {
            return new WireframeShader(renderable, config);
        }
    });

    //perspective camera
    private PerspectiveCamera camera;

    //constructor
    public Platform(float size, PerspectiveCamera cam, boolean [][][] matrix){

        //setup models
        ModelBuilder builder = new ModelBuilder();
        Model white = builder.createBox(
                1f,1f,1f,
                new Material(ColorAttribute.createDiffuse(Color.WHITE)),
                VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
        );

        Model wire = builder.createBox(
                1.01f,1.01f,1.01f,
                new Material(ColorAttribute.createDiffuse(Color.BLACK)),
                VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
        );

        Model boxModel = builder.createBox(
                size,6f,size,
                new Material(ColorAttribute.createDiffuse(Color.WHITE)),
                VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal
        );

        parts = new Array<ModelInstance>();
        wires = new Array<ModelInstance>();

        camera = cam;

        //setup environment
        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
        environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));

        for(float x = 0f; x < size; ++x){
            for(float z = 0f; z < size; ++z){
                ModelInstance p = new ModelInstance(white,x,0f,z);
                ModelInstance w = new ModelInstance(wire,x,0f,z);
                parts.add(p);
                wires.add(w);

                matrix[Math.round(x)][0][Math.round(z)] = true;
            }
        }

        float offset = (size/2f)-.5f;

        box = new ModelInstance(boxModel,offset,2.5f,offset);

    }

    public void render(){
        fillRenderer.begin(camera);
        fillRenderer.render(parts, environment);
        fillRenderer.end();

        wireRenderer.begin(camera);
        wireRenderer.render(wires,environment);
        wireRenderer.render(box,environment);
        wireRenderer.end();
    }
} public class Background {
    //member to render background shape
    private static final ShapeRenderer shapeRenderer = new ShapeRenderer();
    //light color
    private static final Color light = new Color(126f/255f,165f/255f,232f/255f,1f);
    //medium color
    private static final Color medium = new Color(223f/255f,96f/255f,213f/255f,1f);
    //normal color
    private static final Color normal = new Color(.94f,.35f,.52f,1f);
    //dark color
    private static final Color dark = new Color(.99f,.78f,.77f,1f);

    //method to render gradient background
    public static void render(){
        //set viewport
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        //temporarily clear to normal color
        Gdx.gl.glClearColor(42 / 255f, 66 / 255f, 101 / 255f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

        //start gradient rendering
        shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        //set colors and size
        shapeRenderer.rect(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),
                dark,normal,medium,light);
        //end gradient rendering
        shapeRenderer.end();

    }

} public class ParticleRender {
    public boolean isFinished;
    public static final int red = 1, blue = 2, yellow = 3;
    private int current;
    private int ctr;
    private Stage stage;
    private Image particle;
    private String color_path;

    public ParticleRender(int color, Stage stg){
        current = color;
        isFinished = true;
        ctr = 0;
        stage = stg;

        switch (current){
            case red:
                color_path = "img/particle_red/";
                break;

            case blue:
                color_path = "img/particle_blue/";
                break;

            case yellow:
                color_path = "img/particle_yellow/";
                break;
        }
        Texture p = new Texture(color_path+"0.png");
        p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
        particle = new Image(p);
        particle.setWidth(Gdx.graphics.getWidth());
        particle.setHeight(Gdx.graphics.getHeight());
        particle.setPosition(0,Gdx.graphics.getHeight()/4);
        ctr = 0;
    }

    public boolean renderParticle(){
        isFinished = false;
        if(ctr == 0){
            stage.addActor(particle);
        }
        if(ctr == 4){
            Texture p = new Texture(color_path+"1.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 8){
            Texture p = new Texture(color_path+"2.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 12){
            Texture p = new Texture(color_path+"3.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 16){
            Texture p = new Texture(color_path+"4.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 20){
            Texture p = new Texture(color_path+"5.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 24){
            Texture p = new Texture(color_path+"6.png");
            p.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
            particle.setDrawable(new SpriteDrawable(new Sprite(p)));
        }
        if(ctr == 28){
            isFinished = true;
            particle.remove();
            ctr = 0;
            return isFinished;
        }
        ctr += 1;
        return isFinished;
    }
} public class ActorAccessor implements TweenAccessor<Actor> {

    //
    public static final int Y = 0, //variable to move the actors up/down
            ALPHA = 1; //variable to control actor transparency

    //get member values (Y,ALPHA)
    @Override
    public int getValues(Actor target, int tweenType, float[] returnValues) {
        switch (tweenType) {
            case Y:
                returnValues[0]= target.getY();
                return 1;
            case ALPHA:
                returnValues[0] = target.getColor().a;
                return 1;
            default:
                assert false;
                return -1;
        }
    }

    //set member values (Y,ALPHA)
    @Override
    public void setValues(Actor target, int tweenType, float[] newValues) {
        switch (tweenType){
            case Y:
                target.setY(newValues[0]);
                break;
            case ALPHA:
                target.setColor(target.getColor().r, target.getColor().g, target.getColor().b, newValues[0]);
                break;
            default:
                assert false;
                break;
        }
    }
}

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.bms.chemtris.asset.ButtonLoader;
import com.bms.chemtris.asset.LabelLoader;
import com.bms.chemtris.asset.TextureLoader;
import com.bms.chemtris.game.ScoreManager;
import com.bms.chemtris.helpers.Checker;
import com.bms.chemtris.helpers.Controller;
import com.bms.chemtris.helpers.Spawner;
import com.bms.chemtris.helpers.Storage;
import com.bms.chemtris.models.GameModel;
import com.bms.chemtris.models.Platform;
import com.bms.chemtris.render.Background;
import com.bms.chemtris.render.ParticleRender;
import com.bms.chemtris.tween.ActorAccessor;
import java.util.Random;
import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Timeline;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.TweenManager;
                }
                return false;
            }
        });

        ry.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                if(gameState == GameState.CONTROL){
                    current.rotateY();
                    return true;
                }
                return false;
            }
        });

        rz.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                if(gameState == GameState.CONTROL){
                    current.rotateZ();
                    return true;
                }
                return false;
            }
        });

        descend.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                if(gameState == GameState.CONTROL){
                    current.descend();
                    return true;
                }
                return false;
            }
        });

        cam.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                camera.view.idt();
                camera.position.set(12f, 12f, 12f);
                camera.lookAt(0, 0, 0);
                camera.near = 1f;
                camera.far = 300f;
                camera.up.set(0f, 1f, 0f);
                camera.update();
                return true;
            }
        });

        //game pause button
        pause.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {

                if(gameState == GameState.PAUSE){
                    gameState = preState;
                }
                else{
                    preState = gameState;
                    textLabel.setText("Game Paused");
                    textLabel.addAction(Actions.fadeIn(1.5f));
                    gameState = GameState.PAUSE;
                }

                return true;
            }
        });

        //game quit button
        quit.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {

                if(gameState != GameState.QUIT){
                    preState = gameState;
                    textLabel.setText("Quit?");
                    textLabel.addAction(Actions.fadeIn(2f));
                    yes.addAction(Actions.fadeIn(1f));
                    no.addAction(Actions.fadeIn(1f));
                    gameState = GameState.QUIT;
                }

                return true;
            }
        });

        //quit confirm yes button
        yes.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                gameState = GameState.LOSE;
                no.addAction(Actions.fadeOut(1f));
                yes.addAction(Actions.fadeOut(1f));
                return true;
            }
        });

        //quit confirm no button
        no.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                gameState = preState;
                textLabel.addAction(Actions.fadeOut(1f));
                no.addAction(Actions.fadeOut(1f));
                yes.addAction(Actions.fadeOut(1f));
                return true;
            }
        });

        //hint button
        question.addListener(new InputListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {

                preState = gameState;
                gameState = GameState.TUTORIAL;

                Tween.to(tutorialImage, ActorAccessor.ALPHA,1f).target(1).start(tweenManager);