diff options
Diffstat (limited to 'src/main/java/net/sowgro/npehero')
25 files changed, 2804 insertions, 0 deletions
diff --git a/src/main/java/net/sowgro/npehero/Driver.java b/src/main/java/net/sowgro/npehero/Driver.java new file mode 100755 index 0000000..b5e226e --- /dev/null +++ b/src/main/java/net/sowgro/npehero/Driver.java @@ -0,0 +1,130 @@ +package net.sowgro.npehero; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.geometry.Side; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCombination; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundImage; +import javafx.scene.layout.BackgroundPosition; +import javafx.scene.layout.BackgroundRepeat; +import javafx.scene.layout.BackgroundSize; +import javafx.scene.layout.Pane; +import javafx.stage.Stage; +import net.sowgro.npehero.gui.MainMenu; +import net.sowgro.npehero.main.LevelController; +import net.sowgro.npehero.main.SettingsController; +import net.sowgro.npehero.main.SoundController; + +import java.net.URL; +import java.util.Objects; + + +public class Driver extends Application  +{ + +    public static Stage primaryStage; +    static Pane primaryPane = new Pane(); + +    public static SettingsController settingsController = new SettingsController(); +    public static SoundController soundController = new SoundController(); +    public static LevelController levelController = new LevelController(); +//    public static DebugMenu debug = new DebugMenu(); + +    /* +     * starts javafx +     */ +    public static void main(String[] args)  +    { +        launch(args); +    } +     +    /* +     * sets up game windows and starts controllers +     * (automatically called by javafx on start) +     */ +    @Override +    public void start(Stage newPrimaryStage) +    { +        primaryStage = newPrimaryStage; + +        Scene primaryScene = new Scene(primaryPane, 800,600); + +        primaryScene.getStylesheets().add(getClass().getResource("style.css").toExternalForm()); + +        primaryStage.setScene(primaryScene); +        primaryStage.setTitle("NPE Hero"); + +         +        setMenu(new MainMenu()); +        setMenuBackground(); + +        primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> { //full screen stuff +            if (KeyCode.F11.equals(event.getCode())) { +                primaryStage.setFullScreen(!primaryStage.isFullScreen()); +            } +        }); +        primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH); +        primaryStage.setFullScreenExitHint(""); +        primaryStage.show(); +    } + +    /** +     * Replaces/adds a new pane to the primaryPane +     * @param pane  the new pane +     */ +    public static void setMenu(Pane pane) +    { +        if (! primaryPane.getChildren().isEmpty()) +        { +            primaryPane.getChildren().remove(0); +        } +        primaryPane.getChildren().add(pane); +        pane.prefWidthProperty().bind(primaryPane.widthProperty()); //makes pane fill the window +        pane.prefHeightProperty().bind(primaryPane.heightProperty()); +        primaryPane.requestFocus(); //make the pane itself focused by the keyboard naviagtion so no button is highlighted by default +    } + +    /** +     * @return the current pane in primaryPane +     */ +    public static Pane getMenu(){ +        return (Pane) primaryPane.getChildren().get(0); +    } + +    /** +     * replaces the background image with a new one +     * @param image   the url of the image to set +     */ +    public static void setBackground(Image image) //replaces background with a new one +    { +        primaryPane.setBackground(new Background( +            new BackgroundImage( +                    image, +                    BackgroundRepeat.REPEAT, BackgroundRepeat.NO_REPEAT, +                    new BackgroundPosition(Side.LEFT, 0, true, Side.BOTTOM, 0, true), +                    new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, true, true, false, true) +            ))); +    } + +    public static void setMenuBackground() +    { +        setBackground(new Image(Driver.class.getResource("mountains.png").toExternalForm())); +    } + +    /** +     * quits the application +     */ +    public static void quit() +    { +        Platform.exit(); +    } + +    public static URL getResource(String fileName) { +        return Driver.class.getResource(fileName); +    } +} diff --git a/src/main/java/net/sowgro/npehero/devmenu/DebugMenu.java b/src/main/java/net/sowgro/npehero/devmenu/DebugMenu.java new file mode 100755 index 0000000..aecd438 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/devmenu/DebugMenu.java @@ -0,0 +1,41 @@ +package net.sowgro.npehero.devmenu; + +import net.sowgro.npehero.Driver; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +public class DebugMenu  +{ +    public Stage primaryStage = new Stage(); + +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    VBox primaryPane = new VBox(); +    public DebugMenu() +    { +        Button testVol = new Button(); +        testVol.setText("print volumes"); +        testVol.setOnAction(e -> System.out.println("setc:"+Driver.settingsController.effectsVol+" sndc:"+Driver.soundController.songMediaPlayer.getVolume())); + +        primaryPane.getChildren().addAll(testVol); +         +        Scene primaryScene = new Scene(primaryPane); +        primaryStage.setScene(primaryScene); +        primaryStage.setTitle("debug"); +    } + +    public void show() +    { +        primaryStage.show(); +    } + +    public void addButton(Button b) +    { +        primaryPane.getChildren().add(b); +    } +} diff --git a/src/main/java/net/sowgro/npehero/devmenu/DiffEditor.java b/src/main/java/net/sowgro/npehero/devmenu/DiffEditor.java new file mode 100755 index 0000000..d4ebb02 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/devmenu/DiffEditor.java @@ -0,0 +1,79 @@ +package net.sowgro.npehero.devmenu; + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; + +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.gui.LevelSurround; +import net.sowgro.npehero.gui.MainMenu; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.Stage; +import net.sowgro.npehero.main.Difficulty; + +public class DiffEditor +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public DiffEditor(Difficulty diff) +    { +        Stage primaryStage = new Stage(); + +        Text folderNameLabel = new Text("Folder name (ordered alphabetically)"); +        TextField folderName = new TextField(diff.thisDir.getName()); +        folderName.setDisable(true); + +        Text titleLabel = new Text("Title"); +        TextField title = new TextField(diff.title); + +        Text bpmLabel = new Text("BPM"); +        TextField bpm = new TextField(diff.bpm+""); + +        Text numBeatsLabel = new Text("Number of beats (set by notes editor)"); +        TextField numBeats = new TextField(diff.numBeats+""); + +        Text priorityLabel = new Text("priority (lower first)"); +        TextField priority = new TextField(diff.priority+""); + +        Button editNotes = new Button("Edit notes"); +        editNotes.setOnAction(e -> { +            try { +                new NotesEditor(diff); +            } catch (FileNotFoundException | UnsupportedEncodingException e1) { +                e1.printStackTrace(); +            } +        }); + +        Button editScores = new Button("Clear leaderboard"); +        editScores.setOnAction(e -> diff.getLeaderboard().clear()); + +        Button playLevel = new Button("Launch level"); +        playLevel.setOnAction(e -> Driver.setMenu(new LevelSurround(diff.level, diff, new MainMenu()))); + +        Button refresh = new Button("Refresh"); +        refresh.setOnAction( e -> { +            numBeats.setText(diff.numBeats+""); +        }); + +        Button save = new Button("Save"); +        save.setOnAction(e -> { //assigns text feilds to values +            diff.title = title.getText(); +            diff.bpm = Double.parseDouble(bpm.getText()); +            diff.numBeats = Integer.parseInt(numBeats.getText()); +            diff.priority = Integer.parseInt(priority.getText()); +            diff.writeMetadata(); +        }); + +        VBox main = new VBox(); +        main.getChildren().addAll(folderNameLabel,folderName,titleLabel,title,bpmLabel,bpm,numBeatsLabel,numBeats,refresh,priorityLabel,priority,editNotes,editScores,playLevel,save); +        Scene scene = new Scene(main); +        primaryStage.setScene(scene); +        primaryStage.show(); +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/devmenu/LevelEditor.java b/src/main/java/net/sowgro/npehero/devmenu/LevelEditor.java new file mode 100755 index 0000000..94fcc16 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/devmenu/LevelEditor.java @@ -0,0 +1,150 @@ +package net.sowgro.npehero.devmenu; + +import java.io.File; + +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ColorPicker; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import javafx.stage.FileChooser.ExtensionFilter; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.Level; + +public class LevelEditor +{  +    private File selectedSong = null; +    private File selectedPreview = null; +    private File selectedBackground = null; + +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public LevelEditor(Level level) +    { +        Stage primaryStage = new Stage(); + +        Text folderNameLabel = new Text("Folder name"); +        TextField folderName = new TextField(level.thisDir.getName()); +        folderName.setDisable(true); + +        Text titleLabel = new Text("Title"); +        TextField title = new TextField(level.getTitle()); + +        Text artistLabel = new Text("Artist"); +        TextField artist = new TextField(level.getArtist()); + +        Text descLabel = new Text("Description"); +        TextField desc = new TextField(level.desc); + +        Text colorsLabel = new Text("Colors (Left to right)"); +        ColorPicker c1 = new ColorPicker(level.colors[0]); +        ColorPicker c2 = new ColorPicker(level.colors[1]); +        ColorPicker c3 = new ColorPicker(level.colors[2]); +        ColorPicker c4 = new ColorPicker(level.colors[3]); +        ColorPicker c5 = new ColorPicker(level.colors[4]); + +        Text filesLabel = new Text("Files"); + +        FileChooser backgroundChooser = new FileChooser(); +        backgroundChooser.getExtensionFilters().add(new ExtensionFilter("PNG", "*.png")); +        Button backgroundButton = new Button("Import background PNG"); +        backgroundButton.setOnAction(e -> {selectedBackground = backgroundChooser.showOpenDialog(primaryStage);}); + +        FileChooser previewChooser = new FileChooser(); +        previewChooser.getExtensionFilters().add(new ExtensionFilter("PNG", "*.png")); +        Button previewButton = new Button("Import preview PNG"); +        previewButton.setOnAction(e -> {selectedPreview = previewChooser.showOpenDialog(primaryStage);}); + +        FileChooser songChooser = new FileChooser(); +        songChooser.getExtensionFilters().add(new ExtensionFilter("WAV", "*.wav")); +        Button songButton = new Button("Import song WAV"); +        songButton.setOnAction(e -> selectedSong = songChooser.showOpenDialog(primaryStage)); + +        Text diffLabel = new Text("Difficulties"); + +        TableView<Difficulty> diffList = new TableView<Difficulty>(); +         +        TableColumn<Difficulty,String> diffCol = new TableColumn<Difficulty,String>("Difficulty"); +        TableColumn<Difficulty,Boolean> validCol = new TableColumn<Difficulty,Boolean>("Valid?"); + +        diffList.getColumns().add(diffCol); +        diffList.getColumns().add(validCol); + +        diffCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().title)); +        validCol.setCellValueFactory(data -> new ReadOnlyBooleanWrapper(data.getValue().isValid)); + +        diffList.setItems(level.getDiffList()); +         + +        Button edit = new Button("Edit"); +        edit.setOnAction(e -> new DiffEditor(diffList.getSelectionModel().getSelectedItem())); + +        Button remove = new Button("Delete"); +        remove.setOnAction(e -> level.removeDiff(diffList.getSelectionModel().getSelectedItem())); + +        Button refresh = new Button("Refresh"); +        refresh.setOnAction(e -> { +            level.readData(); +            diffList.setItems(level.getDiffList()); +        }); + +        HBox buttons = new HBox(); +        buttons.getChildren().addAll(edit,remove,refresh); + +        TextField newDiff = new TextField("new"); +        Button newDiffButton = new Button("add"); +        newDiffButton.setOnAction(e -> level.addDiff(newDiff.getText())); +        HBox newDiffBox = new HBox(); +        newDiffBox.getChildren().addAll(newDiff,newDiffButton); + +        Button save = new Button("Save"); +        save.setOnAction(e -> { //asigns feilds to values +            level.setTitle(title.getText()); +            level.setArtist(artist.getText()); +            level.desc = desc.getText(); +            level.colors[0] = c1.getValue(); +            level.colors[1] = c2.getValue(); +            level.colors[2] = c3.getValue(); +            level.colors[3] = c4.getValue(); +            level.colors[4] = c5.getValue(); +            if (selectedBackground != null && selectedBackground.exists()) +            { +                level.addFile(selectedBackground,"background.png"); +            }    +            if (selectedPreview != null && selectedPreview.exists()) +            { +                level.addFile(selectedPreview,"preview.png"); +            } +            if (selectedSong != null) +            { +                level.addFile(selectedSong,"song.wav"); +            } +            level.writeMetadata(); +        }); + +        VBox options = new VBox(); +        options.getChildren().addAll(folderNameLabel,folderName,titleLabel,title,artistLabel,artist,descLabel,desc,colorsLabel, +            c1,c2,c3,c4,c5,filesLabel,previewButton,backgroundButton,songButton,save); + +        VBox diffBox = new VBox(); +        diffBox.getChildren().addAll(diffLabel,diffList,buttons,newDiffBox); + +        HBox mainBox = new HBox(); +        mainBox.getChildren().addAll(options,diffBox); +     +        Scene scene = new Scene(mainBox); +        primaryStage.setScene(scene); +        primaryStage.show(); +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/devmenu/LevelList.java b/src/main/java/net/sowgro/npehero/devmenu/LevelList.java new file mode 100755 index 0000000..8d5e0cb --- /dev/null +++ b/src/main/java/net/sowgro/npehero/devmenu/LevelList.java @@ -0,0 +1,72 @@ +package net.sowgro.npehero.devmenu; + +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyStringWrapper; +import net.sowgro.npehero.Driver; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import net.sowgro.npehero.main.Level; +import net.sowgro.npehero.main.LevelController; + +public class LevelList  +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public LevelList() +    { +        //sets up table view: requires special getters, setters and constructors to work +        TableView<Level> levels = new TableView<Level>(); + +        TableColumn<Level,String> titleCol = new TableColumn<Level,String>("Title"); +        TableColumn<Level,String> artistCol = new TableColumn<Level,String>("Artist"); +        TableColumn<Level,Boolean> validCol = new TableColumn<>("Valid?"); + +        levels.getColumns().add(titleCol); +        levels.getColumns().add(artistCol); +        levels.getColumns().add(validCol); + +        titleCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getTitle())); +        artistCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getArtist())); +        validCol.setCellValueFactory(data -> new ReadOnlyBooleanWrapper(data.getValue().isValid())); + +        levels.setItems(LevelController.getLevelList()); + + +        Button edit = new Button("Edit"); +        edit.setOnAction(e -> new LevelEditor(levels.getSelectionModel().getSelectedItem())); + +        Button remove = new Button("Delete"); +        remove.setOnAction(e -> Driver.levelController.removeLevel(levels.getSelectionModel().getSelectedItem())); + +        Button refresh = new Button("Refresh"); +        refresh.setOnAction(e -> { +            Driver.levelController.readData(); +            levels.setItems(LevelController.getLevelList()); +        }); + +        HBox buttons = new HBox(); +        buttons.getChildren().addAll(edit,remove,refresh); + +        TextField newLevel = new TextField("new"); +        Button newLevelButton = new Button("add"); +        newLevelButton.setOnAction(e -> Driver.levelController.addLevel(newLevel.getText())); +        HBox newLevelBox = new HBox(); +        newLevelBox.getChildren().addAll(newLevel,newLevelButton); + +        VBox main = new VBox(); +        main.getChildren().addAll(levels,buttons,newLevelBox); +        Scene scene = new Scene(main, 400, 400); +        Stage primaryStage = new Stage(); +        primaryStage.setScene(scene); +        primaryStage.show(); +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/devmenu/NotesEditor.java b/src/main/java/net/sowgro/npehero/devmenu/NotesEditor.java new file mode 100755 index 0000000..25b21f1 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/devmenu/NotesEditor.java @@ -0,0 +1,101 @@ +package net.sowgro.npehero.devmenu; + +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import net.sowgro.npehero.gameplay.Timer; +import net.sowgro.npehero.Driver; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.stage.Stage; +import net.sowgro.npehero.main.Difficulty; + +public class NotesEditor +{ +    Text help; +    String t1 = "Press Start to begin recording. Use the same keys. Note: existing notes will be overwitten."; +    String t2 = "Now recording. Press Stop or ESC to finish"; +    Difficulty diff; +    Timer timer; +    PrintWriter writer; +    public NotesEditor(Difficulty diff) throws FileNotFoundException, UnsupportedEncodingException +    { +        this.diff = diff; + +        help = new Text(t1); +        Text cur = new Text("-----"); + +        Button start = new Button("Start"); +        start.setOnAction(e -> start()); +        start.setFocusTraversable(false); + +        Button stop = new Button("Stop"); +        stop.setOnAction(e -> stop()); +        stop.setFocusTraversable(false); + +        VBox main = new VBox(); +        main.getChildren().addAll(help,cur,start,stop); + +        Scene scene = new Scene(main); +        Stage primaryStage = new Stage(); +        primaryStage.setScene(scene); +        primaryStage.show(); + +        writer = new PrintWriter(diff.notes, "UTF-8"); + +        scene.setOnKeyPressed(e -> { +			if (e.getCode() == KeyCode.D) { +				writer.println("d"+timer); +                cur.setText("d"+timer); +			} +			if (e.getCode() == KeyCode.F) { +				writer.println("f"+timer); +                cur.setText("f"+timer); +			} +			if (e.getCode() == KeyCode.SPACE) { +				writer.println("s"+timer); +                cur.setText("s"+timer); +			} +			if (e.getCode() == KeyCode.J) { +				writer.println("j"+timer); +                cur.setText("j"+timer); +			} +			if (e.getCode() == KeyCode.K) { +				writer.println("k"+timer); +                cur.setText("k"+timer); +			} +            if (e.getCode() == KeyCode.ESCAPE) +            { +                stop(); +            } +		}); + +        primaryStage.setOnCloseRequest(e -> stop()); +    } + +    private void start() +    { +        Driver.soundController.playSong(diff.level.song); +        timer = new Timer(diff.bpm); +        help.setText(t2); +    } + +    private void stop() +    { +        try  +        { +            Driver.soundController.endSong(); +            diff.numBeats = (int)Double.parseDouble(timer.toString()); +            timer = null; +            writer.close(); +            help.setText(t1); +        } +        catch (Exception e) +        { +            //System.err.println("tried to stop but already stopped"); +        } +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/gameplay/Block.java b/src/main/java/net/sowgro/npehero/gameplay/Block.java new file mode 100755 index 0000000..5bc9d9c --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gameplay/Block.java @@ -0,0 +1,27 @@ +//glowing block of color c (jfx node) + +package net.sowgro.npehero.gameplay; + +import javafx.scene.effect.BlurType; +import javafx.scene.effect.DropShadow; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +public class Block extends Rectangle +{ +    public Block(Color c, double a, double b, int r) +    { +        super(); +        DropShadow dropShadow = new DropShadow(); +        dropShadow.setRadius(200.0); +        dropShadow.setColor(c); +        dropShadow.setBlurType(BlurType.GAUSSIAN); +        +        super.setFill(c); +        super.setWidth(a); +        super.setHeight(b); +        super.setArcHeight(r); +        super.setArcWidth(r); +        super.setEffect(dropShadow); +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/gameplay/NoteInfo.java b/src/main/java/net/sowgro/npehero/gameplay/NoteInfo.java new file mode 100755 index 0000000..a34e939 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gameplay/NoteInfo.java @@ -0,0 +1,28 @@ +/*Name:	Guitar Hero Project + *Description: Contains the info for when to send a note + */ +package net.sowgro.npehero.gameplay; + +import javafx.scene.paint.Color; + +public class NoteInfo +{ +    private double sendTime; +    private Color col; + +    public NoteInfo(double t) { +        sendTime = t; +    } +     +    public double getTime() { +        return sendTime; +    } +     +    public Color getColor() { +        return col; +    } + +    public double compareTo(NoteInfo other) { +        return sendTime - other.sendTime; +    } +} diff --git a/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java b/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java new file mode 100755 index 0000000..39932a5 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java @@ -0,0 +1,360 @@ +package net.sowgro.npehero.gameplay; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Scanner; + +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; + +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.gui.GameOver; +import javafx.geometry.Pos; +import javafx.scene.CacheHint; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.animation.*; +import javafx.util.*; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.Level; +import net.sowgro.npehero.main.ScoreController; + + + +//hi aidan here are some objects you can use +// cntrl.setScore(0) - pass in int +// cntrl.getScore() - returns int +// d.bpm - int, defined in difficulty metadata +// lvl.colors - array of colors (size 5) for the block colors +// d.notes - File, notes.txt in the difficulty folder + +// gui.Driver.setMenu(new GameOver(lvl, d, p, cntrl.getScore())); + +//d.numBeats - int +//d.song - File + + +public class SongPlayer extends Pane { +	private Double bpm;		//initializes the bpm of the song, to be read in from a metadata file later +	private int songLength; //initializes the length of the song in terms of the song's bpm, to be read in later + +	private File song; +	private boolean songIsPlaying = false; +	private boolean missMute = false; + +	private Level level; +	private Difficulty difficulty; +	private Pane pane; + +	Timer timer;			//the timer that determines when notes will fall, counted in terms of the song's bpm +	final int TIME = 1000;  //delay for notes falling down the screen + +	ScoreController scoreCounter = new ScoreController();	//used to keep track of the user's score + +	HBox buttonBox = new HBox();	//used to align the buttons horizontally +	VBox place = new VBox();		//used to place the buttons within the frame + +	Target dButton = new Target(Color.RED, 50, 50, 5, 'd');	//Initializes the button, each parameter is a placeholder that is changed later +	Queue<NoteInfo> dSends = new LinkedList<NoteInfo>(); 		//Queue that dictates when to send the notes +	ArrayList<Block> dLane = new ArrayList<Block>(); 			//Array list containing all the notes currently on the field for that lane +																//process is repeated for the following four buttons +	Target fButton = new Target(Color.BLUE, 50, 50, 5, 'f'); +	Queue<NoteInfo> fSends = new LinkedList<NoteInfo>(); +	ArrayList<Block> fLane = new ArrayList<Block>(); + +	Target sButton = new Target(Color.GREEN, 50, 50, 5, '_'); +	Queue<NoteInfo> spaceSends = new LinkedList<NoteInfo>(); +	ArrayList<Block> spaceLane = new ArrayList<Block>(); + +	Target jButton = new Target(Color.PURPLE, 50, 50, 5, 'j'); +	Queue<NoteInfo> jSends = new LinkedList<NoteInfo>(); +	ArrayList<Block> jLane = new ArrayList<Block>(); + +	Target kButton = new Target(Color.YELLOW, 50, 50, 5, 'k'); +	Queue<NoteInfo> kSends = new LinkedList<NoteInfo>(); +	ArrayList<Block> kLane = new ArrayList<Block>(); + +	/** +	 * Establishes what the chart for the song is going to look like +	 * @throws FileNotFoundException +	 */ +	public void loadSong(File notes) throws FileNotFoundException { +		Scanner scan = new Scanner(new File(notes.getPath()));	//file reader for reading in the notes from a notes.txt file +		try{ +			while (scan.hasNext()) { +				String input = scan.next(); +				if (input.charAt(0) == 'd') { +					dSends.add(new NoteInfo(Double.parseDouble(input.substring(1)))); +				} +				else if (input.charAt(0) == 'f') { +					fSends.add(new NoteInfo(Double.parseDouble(input.substring(1)))); +				} +				else if (input.charAt(0) == 's') { +					spaceSends.add(new NoteInfo(Double.parseDouble(input.substring(1)))); +				} +				else if (input.charAt(0) == 'j') { +					jSends.add(new NoteInfo(Double.parseDouble(input.substring(1)))); +				} +				else if (input.charAt(0) == 'k') { +					kSends.add(new NoteInfo(Double.parseDouble(input.substring(1)))); +				} +			} +		} catch (NumberFormatException e) { +			e.printStackTrace(); +		} +	} + +	public SongPlayer(Level lvl, Difficulty d, Pane p, ScoreController cntrl) { +		Driver.soundController.endSong(); +		song = lvl.song; + +		if (lvl.background != null) { +			Driver.setBackground(lvl.background); +		} +		bpm = d.bpm;					//Reads the song's bpm from a metadata file +		level = lvl; +		difficulty = d; +		pane = p; + +		//System.out.println(d.bpm + " " + d.numBeats); + +		songLength = d.numBeats; +		timer = new Timer(bpm);	//Sets the timer's bpm to that of the song +		scoreCounter = cntrl;			//Uses the song's designated scoreCounter + +		try { +			loadSong(d.notes);			//Calls the file loading from the song's notes.txt file +		} catch (FileNotFoundException e) { +			e.printStackTrace(); +		} + +		dButton.setColor(lvl.colors[0]);	//sets the color of the five buttons to be +		fButton.setColor(lvl.colors[1]);	//the colors outlined in the songs metadata +		sButton.setColor(lvl.colors[2]);	//file +		jButton.setColor(lvl.colors[3]); +		kButton.setColor(lvl.colors[4]); +		genButton(dButton);		//binds the size of each button to the screen, so that +		genButton(fButton);		//they are dynamically resizeable +		genButton(sButton); +		genButton(jButton); +		genButton(kButton); +		 +		Driver.primaryStage.getScene().setOnKeyPressed(e -> { +			/** +			 * The keyboard detection for the game: when a key is pressed it +			 * calls the checkNote() method for the corresponding lane +			 */ +			//System.out.println(timer.time()); +			if (super.isVisible()) +			{ +				if (e.getCode() == KeyCode.D) { +					checkNote(dLane, dButton); +				} +				if (e.getCode() == KeyCode.F) { +					checkNote(fLane, fButton); +				} +				if (e.getCode() == KeyCode.SPACE) { +					checkNote(spaceLane, sButton); +				} +				if (e.getCode() == KeyCode.J) { +					checkNote(jLane, jButton); +				} +				if (e.getCode() == KeyCode.K) { +					checkNote(kLane, kButton); +				} +				if (e.getCode() == KeyCode.Q) { +					System.out.println("" + timer.time()); +				} +			} +			//prints the user's current score and combo, for debugging purposes +			//System.out.println("Score: " + scoreCounter.getScore() + "\nCombo: " + scoreCounter.getCombo() + "\n"); +		}); + +		buttonBox.setAlignment(Pos.CENTER);		//puts the buttons in the center of the screen +		buttonBox.getChildren().addAll(dButton, fButton, sButton, jButton, kButton);	//places the buttons in the correct row order +		buttonBox.setSpacing(10); //sets the space between each button + +		place.prefWidthProperty().bind(super.widthProperty());		//Sets the height and with of the scene +		place.prefHeightProperty().bind(super.heightProperty());	//to natch the window +		place.getChildren().addAll(buttonBox);		//adds the buttonBox to the screen +		place.setAlignment(Pos.BOTTOM_CENTER);		//sets the alignment of the pane +		place.setSpacing(10);	 + +		StackPane root = new StackPane(); +		root.getChildren().addAll(place);	//aligns the components within the pane + +		super.getChildren().addAll(root);	//puts all of the combonents in the pane to be rendered +	} + +	/** +	 * Checks if a note should be sent at the current time, and sends the note if it +	 * needs to be +	 *  +	 * @param sends the queue to check +	 * @param lane  the lane to send the note to +	 */ +	public void sendNote(Queue<NoteInfo> sends, ArrayList<Block> lane, Target button) { +		if (sends.peek() != null && timer.time() > sends.peek().getTime()-(1000*(bpm/60000.0))) { +			TranslateTransition anim = new TranslateTransition(Duration.millis(TIME+105)); + +			lane.add(new Block(button.getColor(), 50, 50, 5)); +			int index = lane.size() - 1; +			sends.remove(); +			lane.get(index).setCache(true); //added by tbone to try to improve performance +			lane.get(index).setCacheHint(CacheHint.SPEED); //this too +			lane.get(index).heightProperty().bind(super.widthProperty().divide(8)); +			lane.get(index).widthProperty().bind(super.widthProperty().divide(8)); +			lane.get(index).arcHeightProperty().bind(super.widthProperty().divide(25)); +			lane.get(index).arcWidthProperty().bind(super.widthProperty().divide(25)); +			lane.get(index).setX(button.getLayoutX()); +			lane.get(index).setY(-lane.get(index).getHeight()); +			anim.setInterpolator(Interpolator.LINEAR); +			anim.setByY(super.getHeight() + lane.get(index).getHeight() + 75); +			anim.setCycleCount(1); +			anim.setAutoReverse(false); +			anim.setNode(lane.get(lane.size() - 1)); +			anim.play(); + +			anim.setOnFinished(e -> { +				if (super.getChildren().removeAll(anim.getNode())){ +					scoreCounter.miss(missMute); +					FillTransition ft = new FillTransition(Duration.millis(500), button.rect); +					ft.setFromValue(Color.RED); +					ft.setToValue(button.getFillColor()); +					ft.play(); +				} +			}); +			super.getChildren().add(lane.get(lane.size() - 1)); +		} +	} + +	/** +	 * Sets up the given button +	 *  +	 * @param button +	 */ +	private void genButton(Target button) { +		button.rect.heightProperty().bind(super.widthProperty().divide(8)); +		button.rect.widthProperty().bind(super.widthProperty().divide(8)); +		button.rect.arcHeightProperty().bind(super.widthProperty().divide(25)); +		button.rect.arcWidthProperty().bind(super.widthProperty().divide(25)); +		button.rect.strokeWidthProperty().bind(super.widthProperty().divide(120)); +	} + +	/** +	 * The background test that is run on every frame of the game +	 */ +	AnimationTimer gameLoop = new AnimationTimer() { + +		@Override +		public void handle(long arg0) { +			sendNote(dSends, dLane, dButton); +			sendNote(fSends, fLane, fButton); +			sendNote(spaceSends, spaceLane, sButton); +			sendNote(jSends, jLane, jButton); +			sendNote(kSends, kLane, kButton); +			if (timer.time() > songLength) { +				Driver.setMenu(new GameOver(level, difficulty, pane, scoreCounter.getScore())); +				cancel(); +			} +			if (!songIsPlaying && timer.time() > 0.0) { +				songIsPlaying = true; +				Driver.soundController.playSong(song); +			} +		} +	}; + +	//starts the gameLoop, a periodic backround task runner that runs the methods within it 60 times every second +	public void start()  +	{ +		gameLoop.start(); +	} + +	/** +	 * Stops the gameloop +	 * @throws LineUnavailableException +	 * @throws IOException +	 * @throws UnsupportedAudioFileException +	 */ +	public void cancel() { +		missMute = true; +		Driver.soundController.endSong(); +		Driver.soundController.playMenuSong(); +		Driver.setMenuBackground(); +		gameLoop.stop(); +	} + +	/** +	 * returns the pos in the lane array of the closest note to the goal +	 *  +	 * @param searchLane +	 * @return the position of the note +	 */ +	private int getClosestNote(ArrayList<Block> searchLane) { +		int pos = 0; + +		for (int i = 0; i < searchLane.size(); i++) { +			if (distanceToGoal(searchLane.get(i)) < distanceToGoal(searchLane.get(pos))) { +				pos = i; +			} +		} +		return pos; +	} + +	/** +	 * Returns the distance to the goal of the given note +	 *  +	 * @param note +	 * @return +	 */ +	private double distanceToGoal(Block note) { +		return Math.abs((super.getHeight() - note.getTranslateY() + note.getHeight()/2) - dButton.rect.getLayoutY()); +	} + +	/** +	 * When the player hits the key, checks the quality of the hit +	 * @param lane the lane checking for a hit +	 * @param button the button checking for a hit +	 * @return 2 for a perfect hit, 1 for a good hit, 0 for a miss, and -1 if there are no notes to hit +	 */ +	private int checkNote(ArrayList<Block> lane, Target button) { +		if (lane.size() != 0 && super.isVisible()) +		{ +			double distance = distanceToGoal(lane.get(getClosestNote(lane))); +			if (lane.size() > 0 && distance < super.getHeight() / 3) { + +				FillTransition ft = new FillTransition(Duration.millis(500), button.rect); +				ft.setToValue(button.getFillColor()); + +				super.getChildren().removeAll(lane.get(getClosestNote(lane))); +				lane.remove(lane.get(getClosestNote(lane))); +				if (distance < super.getHeight() / 12) { +					ft.setFromValue(Color.WHITE); +					ft.play(); +					scoreCounter.perfect(); +					return 2; +				} +				if (distance < super.getHeight() / 4) { +					ft.setFromValue(Color.CYAN); +					ft.play(); +					scoreCounter.good(); +					return 1; +				} +				ft.setFromValue(Color.RED); +				ft.play(); +				scoreCounter.miss(false); +				return 0; +			} +		}	 +		return -1; +	} + +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/gameplay/Target.java b/src/main/java/net/sowgro/npehero/gameplay/Target.java new file mode 100755 index 0000000..3dc8564 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gameplay/Target.java @@ -0,0 +1,50 @@ +//glowing block of color c (jfx node) + +package net.sowgro.npehero.gameplay; + +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; + +public class Target extends StackPane +{ +    private Color col; +    private Color fill; +    private Text label; +    public Rectangle rect = new Rectangle(); +    public Target(Color c, double a, double b, int r, char key) +    { +        label = new Text(key+""); +        label.getStyleClass().add("t3"); +        label.scaleXProperty().bind(super.widthProperty().divide(50)); +        label.scaleYProperty().bind(label.scaleXProperty()); +        super.getChildren().addAll(rect,label); + + +        col = c; +        fill = new Color(c.darker().getRed(), c.darker().getGreen(), c.darker().getBlue(), 0.45); +        rect.setFill(fill); +        rect.setWidth(a); +        rect.setHeight(b); +        rect.setArcHeight(r); +        rect.setArcWidth(r); +        rect.setStroke(col); +        rect.setStrokeWidth(5); +    } + +    public void setColor(Color c) { +        col = c; +        fill = new Color(c.darker().getRed(), c.darker().getGreen(), c.darker().getBlue(), 0.45); +        rect.setFill(fill); +        rect.setStroke(c); +    } + +    public Color getFillColor() { +        return fill; +    } + +    public Color getColor() { +        return col; +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/gameplay/Timer.java b/src/main/java/net/sowgro/npehero/gameplay/Timer.java new file mode 100755 index 0000000..eada237 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gameplay/Timer.java @@ -0,0 +1,28 @@ +/*Name:	Guitar Hero Project + *Description: Contains the method used to determine how long the user has been playing,  + *             used to determine when to send notes + */ +package net.sowgro.npehero.gameplay; + + +public class Timer +{ +    private long timeStart = System.currentTimeMillis(); +    private double bpm; + +    public Timer(double newBpm) { +        bpm = newBpm; +    } + +    public Timer() { +        bpm = 60000; +    } + +    public double time() { +        return ((double)(System.currentTimeMillis()-timeStart)-2000)*(bpm/60000.0); +    } + +    public String toString() {       +        return ""+((Math.round(10*(((double)(System.currentTimeMillis()-timeStart))*(bpm/60000.0))))/10.0); +    } +} diff --git a/src/main/java/net/sowgro/npehero/gui/GameOver.java b/src/main/java/net/sowgro/npehero/gui/GameOver.java new file mode 100755 index 0000000..68ce3ea --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/GameOver.java @@ -0,0 +1,124 @@ +package net.sowgro.npehero.gui; + +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.Level; + +public class GameOver extends Pane +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public GameOver(Level level, Difficulty diff, Pane lastMenu, int score2) +    { +        Text topText = new Text(); +        topText.setText("Level Complete"); +        topText.getStyleClass().add("t11"); + +        Text levelName = new Text(); +        levelName.setText(level.getTitle()); +        levelName.getStyleClass().add("t2"); + +        Text levelArtist = new Text(); +        levelArtist.setText(level.getArtist()+" - "+diff.title); +        levelArtist.getStyleClass().add("t3"); + +        VBox levelDetailsBox = new VBox(); +        levelDetailsBox.getChildren().addAll(levelName,levelArtist); +        levelDetailsBox.getStyleClass().add("box"); +        levelDetailsBox.setPadding(new Insets(5)); + + +        Text scoreLabel = new Text(); +        scoreLabel.setText("Final score"); +        scoreLabel.getStyleClass().add("t3"); + +        Text score = new Text(); +        score.setText(score2+""); +        score.getStyleClass().add("t2"); +        score.setStyle("-fx-font-size: 30;"); + +        VBox scoreBox = new VBox(); +        scoreBox.getStyleClass().add("box"); +        scoreBox.getChildren().addAll(scoreLabel,score); +        scoreBox.setPadding(new Insets(5)); + + +        Text nameLabel = new Text(); +        nameLabel.setText("Leaderboard entry"); +        nameLabel.getStyleClass().add("t3"); + +        TextField name = new TextField(); +        name.getStyleClass().remove("text-feild"); +        name.getStyleClass().add("button"); +        name.setText("name"); + +        Button save = new Button(); +        save.setText("Add"); +        save.setOnAction(new EventHandler<ActionEvent>() { //this is the same as the "e ->" thing but it allows more than one line to be added  +            @Override +            public void handle(ActionEvent event) { +                Driver.soundController.playSfx("forward"); +                save.setDisable(true); +                name.setDisable(true); +                diff.addToLeaderboard(name.getText(), score2); +            } +        }); + +        BorderPane b = new BorderPane(); +        b.setRight(save); +        b.setCenter(name); + +        VBox nameBox = new VBox(); +        nameBox.getChildren().addAll(nameLabel,b); +        nameBox.getStyleClass().add("box"); +        nameBox.setSpacing(5); +        nameBox.setPadding(new Insets(5)); + + +        Button exit = new Button(); +        exit.setText("Back"); +        exit.setOnAction(e -> { +            Driver.soundController.playSfx("backward"); +            Driver.setMenu(lastMenu); +        }); + +        Button replay = new Button(); +        replay.setText("Replay"); +        replay.setOnAction(e -> { +            Driver.soundController.playSfx("forward"); +            Driver.setMenu(new LevelSurround(level, diff, lastMenu)); +        }); + +        BorderPane buttonBox = new BorderPane(); +        buttonBox.setLeft(exit); +        buttonBox.setRight(replay); + + +        VBox centerBox = new VBox(); +        centerBox.getChildren().addAll(topText,levelDetailsBox,scoreBox,nameBox,buttonBox); +        centerBox.setSpacing(10); +        centerBox.setAlignment(Pos.CENTER); + +        HBox rootBox = new HBox(); +        rootBox.getChildren().add(centerBox); +        rootBox.setAlignment(Pos.CENTER); +        rootBox.prefWidthProperty().bind(super.prefWidthProperty());  +        rootBox.prefHeightProperty().bind(super.prefHeightProperty()); + +        super.getChildren().add(rootBox); +    } +} diff --git a/src/main/java/net/sowgro/npehero/gui/Leaderboard.java b/src/main/java/net/sowgro/npehero/gui/Leaderboard.java new file mode 100755 index 0000000..a8e54d1 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/Leaderboard.java @@ -0,0 +1,72 @@ +package net.sowgro.npehero.gui; + +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TableColumn.SortType; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.LeaderboardEntry; +import net.sowgro.npehero.main.Level; + +public class Leaderboard extends Pane +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public Leaderboard(Level level, Difficulty diff, Pane prev) +    { +        //sets up table view: requires java bean getters, setters and constructors to work +        TableView<LeaderboardEntry> scores = new TableView<LeaderboardEntry>(); + +        TableColumn<LeaderboardEntry, String> nameCol = new TableColumn<LeaderboardEntry, String>("Name"); +        TableColumn<LeaderboardEntry, String> scoreCol = new TableColumn<LeaderboardEntry, String>("Score"); +        TableColumn<LeaderboardEntry, String> dateCol = new TableColumn<LeaderboardEntry, String>("Date"); + +        scores.getColumns().add(nameCol); +        scores.getColumns().add(scoreCol); +        scores.getColumns().add(dateCol); + +        nameCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getName())); +        scoreCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getScore() + "")); +        dateCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getDate())); + +        scores.setItems(diff.getLeaderboard()); + +        scores.getStyleClass().add("unselectable"); + +        scores.prefWidthProperty().bind(super.prefWidthProperty().multiply(0.25));  +        scores.prefHeightProperty().bind(super.prefHeightProperty().multiply(0.75)); + +        scoreCol.setSortType(SortType.DESCENDING); +        scores.getSortOrder().add(scoreCol); + +        Button exit = new Button(); +        exit.setText("Back"); +        exit.setOnAction(e -> { +            Driver.soundController.playSfx("backward"); +            Driver.setMenu(prev); +        }); + +        VBox centerBox = new VBox(); +        centerBox.setAlignment(Pos.CENTER); +        centerBox.setSpacing(10); +        centerBox.getChildren().addAll(scores,exit); +        centerBox.setMinWidth(400); + +        HBox rootBox = new HBox(); +        rootBox.prefWidthProperty().bind(super.prefWidthProperty());  +        rootBox.prefHeightProperty().bind(super.prefHeightProperty()); +        rootBox.getChildren().add(centerBox); +        rootBox.setAlignment(Pos.CENTER); + +        super.getChildren().add(rootBox); +    } +} diff --git a/src/main/java/net/sowgro/npehero/gui/LevelDetails.java b/src/main/java/net/sowgro/npehero/gui/LevelDetails.java new file mode 100755 index 0000000..25d96c8 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/LevelDetails.java @@ -0,0 +1,137 @@ +package net.sowgro.npehero.gui; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.ToggleGroup; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; +import javafx.scene.text.TextFlow; +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.Level; + +public class LevelDetails extends VBox +{ +    /** +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     *  +     * @param level: the selected level on the right side +     */ +    public LevelDetails(Level level) +    { +        VBox rightBox = new VBox(); +        rightBox.prefWidthProperty().bind(super.prefWidthProperty()); +        rightBox.prefHeightProperty().bind(super.prefHeightProperty().multiply(0.75)); +        rightBox.setMinWidth(350); +        rightBox.getStyleClass().add("box"); + +        Button play = new Button(); +        play.setDisable(true); +        play.setText("Play"); + +        Button leaderboard = new Button(); +        leaderboard.setDisable(true); +        leaderboard.setText("Leaderboard"); + +        if (level == null) //if no level is selected from the list on the left +        { +            Text desc = new Text(); +            desc.setText("Select a level from the left pane"); +            desc.getStyleClass().add("t3"); +            desc.wrappingWidthProperty().bind(super.prefWidthProperty().subtract(10)); +            desc.setTextAlignment(TextAlignment.CENTER); + +            rightBox.setAlignment(Pos.CENTER); +            rightBox.getChildren().addAll(desc); +        } + +        else +        { +            VBox details = new VBox(); + +            ScrollPane detailsScroll = new ScrollPane(details); +            detailsScroll.prefHeightProperty().bind(rightBox.prefHeightProperty()); +            detailsScroll.prefWidthProperty().bind(rightBox.prefWidthProperty()); +            detailsScroll.getStyleClass().remove("scroll-pane"); + +            Text title = new Text(); +            title.setText(level.getTitle()); +            title.getStyleClass().add("t1"); + +            Text artist = new Text(); +            artist.setText(level.getArtist()); +            artist.getStyleClass().add("t2"); + +            Text desc = new Text(); +            desc.setText(level.desc); +            desc.getStyleClass().add("t3"); + +            ImageView previewView = new ImageView(); +            Image preview = level.preview; +            previewView.setImage(preview); +            previewView.fitWidthProperty().bind(super.prefWidthProperty().multiply(0.5)); +            previewView.setPreserveRatio(true); + +            FlowPane diffSelector = new FlowPane(); +            diffSelector.setAlignment(Pos.CENTER); +            ToggleGroup diffToggleGroup = new ToggleGroup(); //allows only one to be selected at a time +            for (Difficulty diff : level.getValidDiffList()) //adds a button for each diff +            { +                RadioButton temp = new RadioButton(); +                temp.getStyleClass().remove("radio-button"); //makes the buttons not look like a radio button and instead a normal button +                temp.getStyleClass().add("button"); +                temp.setText(diff.title); +                temp.setUserData(diff); //allows the data and text to be seperate +                diffToggleGroup.getToggles().add(temp); +                diffSelector.getChildren().add(temp); +            } +            play.disableProperty().bind(diffToggleGroup.selectedToggleProperty().isNull()); //disables play button when no difficulty is selected +            play.setOnAction(e -> { +                Driver.soundController.playSfx("forward"); +                Driver.setMenu(new LevelSurround(level, (Difficulty)diffToggleGroup.getSelectedToggle().getUserData(), Driver.getMenu())); +            }); + +            leaderboard.disableProperty().bind(diffToggleGroup.selectedToggleProperty().isNull()); +            leaderboard.setOnAction(e -> { +                Driver.soundController.playSfx("forward"); +                Driver.setMenu(new Leaderboard(level, (Difficulty)diffToggleGroup.getSelectedToggle().getUserData(), Driver.getMenu())); +            }); + + +            HBox diffBox = new HBox(); +            diffSelector.prefWidthProperty().bind(diffBox.widthProperty()); +            diffBox.getChildren().add(diffSelector); +             +            details.setSpacing(10); +            details.getChildren().addAll(new TextFlow(title), new TextFlow(artist), new TextFlow(desc), previewView, diffBox); +            detailsScroll.setFitToWidth(true); + +            rightBox.getChildren().add(detailsScroll); +            rightBox.setPadding(new Insets(5)); +        } + +        VBox rightSide = new VBox(); +        rightSide.setAlignment(Pos.CENTER_RIGHT); +        rightSide.setSpacing(10); + +        HBox buttonBox = new HBox(); +        buttonBox.getChildren().addAll(leaderboard,play); +        buttonBox.setSpacing(5); +        buttonBox.setAlignment(Pos.CENTER_RIGHT); + +        rightSide.getChildren().addAll(rightBox,buttonBox); + +        super.setAlignment(Pos.CENTER_RIGHT); +        super.getChildren().add(rightSide); +    } +} diff --git a/src/main/java/net/sowgro/npehero/gui/LevelSelector.java b/src/main/java/net/sowgro/npehero/gui/LevelSelector.java new file mode 100755 index 0000000..6d3442e --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/LevelSelector.java @@ -0,0 +1,98 @@ +package net.sowgro.npehero.gui; + +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.main.Level; +import net.sowgro.npehero.main.LevelController; + +public class LevelSelector extends Pane +{    +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public LevelSelector() +    { +        //sets up table view: requires special getters, setters and constructors to work +        TableView<Level> levels = new TableView<Level>(); +         +        TableColumn<Level,String> titleCol = new TableColumn<Level,String>("Title"); +        TableColumn<Level,String> artistCol = new TableColumn<Level,String>("Artist"); + +        levels.getColumns().add(titleCol); +        levels.getColumns().add(artistCol); + +        titleCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getTitle())); +        artistCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getArtist())); + +        levels.setItems(LevelController.getValidLevelList()); + +        levels.prefWidthProperty().bind(super.prefWidthProperty().multiply(0.25));  +        levels.prefHeightProperty().bind(super.prefHeightProperty().multiply(0.75)); +        levels.setMinWidth(300); + + +        Button exit = new Button(); +        exit.setText("Back"); +        exit.setOnAction(e -> { +            Driver.setMenu(new MainMenu()); +            Driver.soundController.playSfx("backward"); +        }); + +        VBox leftBox = new VBox(); +        leftBox.setAlignment(Pos.CENTER_LEFT); +        leftBox.setSpacing(10); +        leftBox.getChildren().addAll(levels,exit); + +        Pane rightBox = new Pane(); +        addDetails(rightBox, levels); + + +        HBox rootBox = new HBox(); +        rootBox.prefWidthProperty().bind(super.prefWidthProperty());  +        rootBox.prefHeightProperty().bind(super.prefHeightProperty()); +        rootBox.getChildren().addAll(leftBox, rightBox); +        rootBox.setAlignment(Pos.CENTER); +        rootBox.setSpacing(10); + +        levels.getStyleClass().remove("list-view"); +        levels.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Level>() { //listens for change in selected item of the list + +			@Override +			public void changed(ObservableValue<? extends Level> arg0, Level arg1, Level arg2) { +				addDetails(rightBox, levels); +			}	 +		}); +        super.getChildren().add(rootBox); +    } + +    /** +     * adds corresponding level details pane to the right side +     * @param rightBox +     * @param levels +     */ +    private void addDetails(Pane rightBox, TableView<Level> levels) +    { +        VBox details = new LevelDetails(levels.getSelectionModel().getSelectedItem()); +        if (! rightBox.getChildren().isEmpty()) +        { +            rightBox.getChildren().remove(0); +        } +        rightBox.getChildren().add(details); +        details.prefWidthProperty().bind(super.prefWidthProperty().multiply(0.37));  +        details.prefHeightProperty().bind(super.prefHeightProperty()); +        details.maxWidthProperty().bind(super.prefWidthProperty().multiply(0.37));  +        details.maxHeightProperty().bind(super.prefHeightProperty()); +    } + +} diff --git a/src/main/java/net/sowgro/npehero/gui/LevelSurround.java b/src/main/java/net/sowgro/npehero/gui/LevelSurround.java new file mode 100755 index 0000000..08baf9e --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/LevelSurround.java @@ -0,0 +1,150 @@ +package net.sowgro.npehero.gui; + +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.gameplay.SongPlayer; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.main.Level; +import net.sowgro.npehero.main.ScoreController; + +public class LevelSurround extends Pane +{    +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public LevelSurround(Level level, Difficulty difficulty, Pane prev) +    { +        ScoreController sc = new ScoreController(); +        SongPlayer game = new SongPlayer(level, difficulty, prev, sc); + +        Button exit = new Button(); +        exit.setText("Back"); +        exit.setOnAction(e -> { +            Driver.setMenu(prev); +            Driver.soundController.playSfx("backward"); +            game.cancel(); +        }); + +        HBox buttonBox = new HBox(); +        buttonBox.getChildren().addAll(exit); +        buttonBox.setAlignment(Pos.TOP_LEFT); +        buttonBox.setSpacing(10); + +        Text title = new Text(); +        title.setText(level.getTitle()); +        title.getStyleClass().add("t2"); + +        Text artist = new Text(); +        artist.setText(level.getArtist()+" - "+difficulty.title); +        artist.getStyleClass().add("t3"); + +        VBox titleTextBox = new VBox(); +        titleTextBox.setAlignment(Pos.TOP_RIGHT); +        titleTextBox.getChildren().addAll(title, artist); +        titleTextBox.getStyleClass().add("box"); +        titleTextBox.setPadding(new Insets(10)); + +        AnchorPane topBar = new AnchorPane(); +        topBar.getChildren().addAll(buttonBox,titleTextBox); +        topBar.setLeftAnchor(buttonBox, 0.0); +        topBar.setRightAnchor(titleTextBox, 0.0); +        topBar.setTopAnchor(buttonBox, 0.0); +        topBar.setTopAnchor(titleTextBox, 0.0); +        topBar.setPadding(new Insets(10)); +         + +        Text scoreLabel = new Text(); +        scoreLabel.setText("Score:"); +        scoreLabel.getStyleClass().add("t3"); + +        Text scoreDisplay = new Text(); +        scoreDisplay.textProperty().bind(sc.scoreProperty); +        scoreDisplay.getStyleClass().add("t1"); + +        VBox scoreTextBox = new VBox(); +        scoreTextBox.setAlignment(Pos.BOTTOM_LEFT); +        scoreTextBox.getChildren().addAll(scoreLabel,scoreDisplay); +        scoreTextBox.setPadding(new Insets(10)); +        scoreTextBox.getStyleClass().add("box"); +        scoreTextBox.minWidthProperty().bind(scoreTextBox.heightProperty()); + +        AnchorPane scoreBox = new AnchorPane(); +        scoreBox.getChildren().add(scoreTextBox); +        scoreBox.setLeftAnchor(scoreTextBox, 0.0); +        scoreBox.setBottomAnchor(scoreTextBox, 0.0); +        scoreBox.setPadding(new Insets(10)); + +        Text comboLabel = new Text(); +        comboLabel.setText("Combo:"); +        comboLabel.getStyleClass().add("t3"); + +        Text comboDisplay = new Text(); +        comboDisplay.textProperty().bind(sc.comboProperty); +        comboDisplay.getStyleClass().add("t1"); + +        VBox comboTextBox = new VBox(); +        comboTextBox.setAlignment(Pos.BOTTOM_RIGHT); +        comboTextBox.getChildren().addAll(comboLabel,comboDisplay); +        comboTextBox.setPadding(new Insets(10)); +        comboTextBox.getStyleClass().add("box"); +        comboTextBox.minWidthProperty().bind(comboTextBox.heightProperty()); + +        AnchorPane comboBox = new AnchorPane(); +        comboBox.getChildren().add(comboTextBox); +        comboBox.setRightAnchor(comboTextBox, 0.0); +        comboBox.setBottomAnchor(comboTextBox, 0.0); +        comboBox.setPadding(new Insets(10)); + +        game.minWidthProperty().bind(super.prefHeightProperty().multiply(0.66)); +        game.minHeightProperty().bind(super.prefHeightProperty()); +        game.getStyleClass().add("box"); + + +        comboBox.minWidthProperty().bind(super.prefWidthProperty().subtract(game.minWidthProperty()).divide(2)); +        scoreBox.minWidthProperty().bind(super.prefWidthProperty().subtract(game.minWidthProperty()).divide(2)); + +        HBox centerBox = new HBox(); +        centerBox.getChildren().addAll(comboBox, game, scoreBox); +        centerBox.setAlignment(Pos.BOTTOM_CENTER); + +        StackPane root = new StackPane(); +        root.getChildren().addAll(centerBox, topBar); + +        super.getChildren().add(root); +        root.prefWidthProperty().bind(super.prefWidthProperty()); +        root.prefHeightProperty().bind(super.prefHeightProperty()); + +        //for debug menu +        Button addScore = new Button(); +        addScore.setText(level.getTitle() + " addscore"); +        addScore.setOnAction(e -> sc.setScore(sc.getScore()+1)); +//        Driver.debug.addButton(addScore); + +        Button addCombo = new Button(); +        addCombo.setText(level.getTitle() + " addcombo"); +        addCombo.setOnAction(e -> sc.setCombo(sc.getCombo()+1)); +//        Driver.debug.addButton(addCombo); + +        Button printD = new Button(); +        printD.setText(level.getTitle() + " print debug"); +        printD.setOnAction(e -> sc.print()); +//        Driver.debug.addButton(printD); + +        Button testfinish = new Button(); +        testfinish.setText(level.getTitle() + "launch game end"); +        testfinish.setOnAction(e -> Driver.setMenu(new GameOver(level, difficulty, prev, sc.getScore()))); +//        Driver.debug.addButton(testfinish); + +        game.start(); +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/gui/MainMenu.java b/src/main/java/net/sowgro/npehero/gui/MainMenu.java new file mode 100755 index 0000000..bb1970f --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/MainMenu.java @@ -0,0 +1,70 @@ +package net.sowgro.npehero.gui; + +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.effect.BlurType; +import javafx.scene.effect.DropShadow; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Text; +import net.sowgro.npehero.Driver; + + +public class MainMenu extends Pane +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public MainMenu() +    { +        DropShadow dropShadow = new DropShadow(); +        dropShadow.setRadius(50.0); +        dropShadow.setColor(Color.WHITE); +        dropShadow.setBlurType(BlurType.GAUSSIAN); + +        Text title = new Text(); +        title.setText("NPE Hero"); +        title.getStyleClass().add("t0"); +        title.setEffect(dropShadow); + +        Button play = new Button(); +        play.setText("Play"); +        play.setOnAction(e -> { +            Driver.setMenu(new LevelSelector()); +            Driver.soundController.playSfx("forward"); +        }); + +        Button settings = new Button(); +        settings.setText("Settings"); +        settings.setOnAction(e -> {Driver.setMenu(new Settings()); +            Driver.soundController.playSfx("forward"); +        }); + +        Button exit = new Button(); +        exit.setText("Quit"); +        exit.setOnAction(e -> {Driver.quit(); +            Driver.soundController.playSfx("backward"); +        }); + +        VBox buttonBox = new VBox(); +        buttonBox.getChildren().addAll(play, settings, exit); +        buttonBox.setAlignment(Pos.CENTER); +        buttonBox.setSpacing(10); + +        VBox centerBox = new VBox(); +        centerBox.setAlignment(Pos.CENTER); +        centerBox.getChildren().addAll(title, buttonBox); +        centerBox.setSpacing(10); + +        VBox rootBox = new VBox(); +        rootBox.prefWidthProperty().bind(super.prefWidthProperty());  +        rootBox.prefHeightProperty().bind(super.prefHeightProperty()); +        rootBox.setAlignment(Pos.CENTER); +        rootBox.getChildren().add(centerBox); + +        super.getChildren().add(rootBox); +    } +} diff --git a/src/main/java/net/sowgro/npehero/gui/Settings.java b/src/main/java/net/sowgro/npehero/gui/Settings.java new file mode 100755 index 0000000..882eb86 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/gui/Settings.java @@ -0,0 +1,131 @@ +package net.sowgro.npehero.gui; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Slider; +import javafx.scene.control.ToggleButton; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import net.sowgro.npehero.Driver; +import net.sowgro.npehero.devmenu.LevelList; + +public class Settings extends Pane +{ +    /* +     * this class is a layout class, most of its purpose is to place UI elements like Buttons within Panes like VBoxes. +     * the creation of these UI elements are mostly not commented due to their repetitive and self explanatory nature. +     * style classes are defined in the style.css file. +     */ +    public Settings() +    { +        Text musicText = new Text(); +        musicText.setText("Music Volume"); +        musicText.getStyleClass().add("t3"); + +        Slider musicSlider = new Slider(); +        musicSlider.valueProperty().bindBidirectional(Driver.settingsController.musicVol); +        musicSlider.setMin(0.0); +        musicSlider.setMax(1.0); + +        VBox musicBox = new VBox(); +        musicBox.getChildren().addAll(musicText, musicSlider); +        musicBox.getStyleClass().add("box"); +        musicBox.setPadding(new Insets(10)); + + +        Text SFXText = new Text(); +        SFXText.setText("Sound Effects Volume"); +        SFXText.getStyleClass().add("t3"); + +        Slider SFXSlider = new Slider(); +        SFXSlider.valueProperty().bindBidirectional(Driver.settingsController.effectsVol); +        SFXSlider.setMin(0.0); +        SFXSlider.setMax(1.0); + +        VBox SFXBox = new VBox(); +        SFXBox.getChildren().addAll(SFXText, SFXSlider); +        SFXBox.getStyleClass().add("box"); +        SFXBox.setPadding(new Insets(10)); + + +        Text fullText = new Text(); +        fullText.setText("Fullscreen mode"); +        fullText.getStyleClass().add("t3"); + +        Button fullscreen = new Button(); +        fullscreen.setText("Toggle (F11)"); +        fullscreen.getStyleClass().remove("toggle-button"); +        fullscreen.getStyleClass().add("button"); +        fullscreen.setOnAction(e -> { +            Driver.soundController.playSfx("forward"); +            Driver.primaryStage.setFullScreen(!Driver.primaryStage.isFullScreen()); +        }); + +        VBox fullBox = new VBox(); +        fullBox.getChildren().addAll(fullText,fullscreen); +        fullBox.getStyleClass().add("box"); +        fullBox.setPadding(new Insets(10)); + + +        Text devLabel = new Text("Advanced"); +        devLabel.getStyleClass().add("t3"); +         +        Button levelEdit = new Button("Level Utility"); +        levelEdit.setOnAction(e -> { +            Driver.soundController.playSfx("forward"); +            new LevelList(); +        }); + +        Button devMenu = new Button(); +        devMenu.setText("Debug Menu"); +        devMenu.setOnAction(e -> { +            Driver.soundController.playSfx("forward"); +//            Driver.debug.show(); +        }); + +        VBox devBox = new VBox(); +        devBox.getStyleClass().add("box"); +        devBox.getChildren().addAll(devLabel,levelEdit,devMenu); +        devBox.setVisible(false); +        devBox.setManaged(false); +        devBox.setPadding(new Insets(10)); + +        ToggleButton advanced = new ToggleButton("Advanced"); +        advanced.getStyleClass().remove("toggle-button"); +        advanced.getStyleClass().add("button"); +        advanced.selectedProperty().bindBidirectional(devBox.managedProperty()); +        advanced.selectedProperty().bindBidirectional(devBox.visibleProperty()); + +        Button exit = new Button(); +        exit.setText("Back"); +        exit.setOnAction(e -> { +            Driver.settingsController.write(); +            Driver.soundController.playSfx("backward"); +            Driver.setMenu(new MainMenu()); +        }); + +        BorderPane buttonBox = new BorderPane(); +        buttonBox.setLeft(exit); +        buttonBox.setRight(advanced); + + +        VBox options = new VBox(); +        options.setSpacing(10); +        options.setAlignment(Pos.CENTER); +        options.getChildren().addAll(musicBox,SFXBox,fullBox,devBox,buttonBox); +        options.maxWidthProperty().bind(super.prefWidthProperty().multiply(0.25));  +        options.setMinWidth(400); +        options.prefHeightProperty().bind(super.prefHeightProperty()); + +        HBox rootBox = new HBox(); +        rootBox.prefWidthProperty().bind(super.prefWidthProperty());  +        rootBox.prefHeightProperty().bind(super.prefHeightProperty()); +        rootBox.getChildren().add(options); +        rootBox.setAlignment(Pos.CENTER); +        super.getChildren().add(rootBox); +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/Difficulty.java b/src/main/java/net/sowgro/npehero/main/Difficulty.java new file mode 100755 index 0000000..6bd565a --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/Difficulty.java @@ -0,0 +1,272 @@ +package net.sowgro.npehero.main; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDate; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +public class Difficulty implements Comparable<Difficulty> +{ +    public File thisDir; +    public String title = "Unnamed"; +    private ObservableList<LeaderboardEntry> leaderboard = FXCollections.observableArrayList(); +    public File notes; +    public Double bpm = 0.0; +    public int numBeats; +    public Level level; +    public boolean isValid = false; +    public int priority = 0; +     +    /** +     * Creates a new Difficulty and gives it a file path +     * @param newDir: The file path of the Difficulty +     */ +    public Difficulty(File newDir, Level level) +    { +        thisDir = newDir; +        this.level = level; +    } + +    public void readData() +    { +        boolean isValid1 = true; +        if (new File(thisDir, "metadata.json").exists()) +        { +            if (!parseMetadata()) +            { +                isValid1 = false; +            } +        } +        else +        { +            System.err.println(thisDir+" is missing metadata.json"); +            isValid1 = false; +        } + +        if (new File(thisDir, "leaderboard.json").exists()) +        { +            if (!parseLeaderboard()) +            { +                isValid1 = false; +            } +        } +        else +        { +            System.err.println(thisDir+" is missing leaderboard.json"); +            isValid1 = false; +        } + +        if (new File(thisDir, "notes.txt").exists()) +        { +            notes = new File(thisDir, "notes.txt"); +        } +        else +        { +            System.err.println(thisDir+" is missing notes.txt"); +            isValid1 = false; +        } + +        if (bpm == 0.0) +        { +            System.err.println(thisDir+" is missing a bpm"); +            isValid1 = false; +        } + +        if (numBeats == 0) +        { +            System.err.println(thisDir+" is missing the number of beats"); +            isValid1 = false; +        } + +        isValid = isValid1; +    } + +    /** +     * Reads in json metadata and assigns values to variables +     */ +    public boolean parseMetadata() +    { +        boolean isValid = true; +        File file = new File(thisDir, "metadata.json"); +        JSONParser jsonParser = new JSONParser(); //parser to read the file +		 +		try(FileReader reader = new FileReader(file)) +		{ +			Object obj = jsonParser.parse(reader);  +			JSONObject diffStuff = (JSONObject)(obj); //converts read object to a JSONObject +             +            if (diffStuff.containsKey("title")) +            { +                title = (String) diffStuff.get("title"); +            } +            else +            { +                System.err.println(file+" is missing properety title"); +                isValid = false; +            } + +            if (diffStuff.containsKey("bpm")) +            { +                bpm = Double.parseDouble(diffStuff.get("bpm")+""); +            } +            else +            { +                System.err.println(file+" is missing properety bpm"); +                isValid = false; +            } + +            if (diffStuff.containsKey("numBeats")) +            { +                numBeats = Integer.parseInt(diffStuff.get("numBeats")+""); +            } +            else +            { +                System.err.println(file+" is missing properety numBeats"); +                isValid = false; +            } + +            if (diffStuff.containsKey("priority")) +            { +                priority = Integer.parseInt(diffStuff.get("priority")+""); + +            } +            else +            { +                System.err.println(file+" is missing properety priority"); +                isValid = false; +            } +		} +		catch (Exception e) +        { +            e.printStackTrace(); +            isValid = false; +        } +        return isValid; +    } + +    /** +     * Writes metadata to json file +     */ +    public void writeMetadata()  +    { +        FileWriter fileWriter; +        try  +        { +            File file = new File(thisDir, "metadata.json"); +            fileWriter = new FileWriter(file); +            JSONObject obj = new JSONObject(); +            obj.put("title", title); +            obj.put("bpm", bpm); +            obj.put("numBeats", numBeats); +            obj.put("priority", priority); +            obj.writeJSONString(fileWriter); +            fileWriter.flush(); +        }  +        catch (IOException e)  +        { +            e.printStackTrace(); +        } +    } + +    /** +     * Reads in json leaderboard and assigns populates list with leaderboardEntries +     */ +    public boolean parseLeaderboard() +    { +        boolean isValid = true; +        File file = new File(thisDir, "leaderboard.json"); +        JSONParser jsonParser = new JSONParser(); //parser to read the file + +		try(FileReader reader = new FileReader(file)) +		{ +            Object obj = jsonParser.parse(reader);  +			 +			JSONArray leaderboardStuff = (JSONArray)(obj); //converts read object to a JSONArray + +            for (Object cur: leaderboardStuff) +            { +                JSONObject cur2 = (JSONObject) cur; + +                String name = (String) cur2.get("name"); +                int score = Integer.parseInt(""+cur2.get("score")); +                String date = (String) cur2.get("date"); +                leaderboard.add(new LeaderboardEntry(name, score, date)); +            } +		} +		catch (Exception e) +        { +            isValid = false; +            e.printStackTrace(); +        } +        return isValid; +    } + +    /** +     * Writes leaderboard to json file +     */ +    public void writeLeaderboard() +    { +        FileWriter fileWriter; +        try +		{ +            File file = new File(thisDir, "leaderboard.json"); +            fileWriter = new FileWriter(file); +            //write the settings JSONObject instance to the file  +            JSONArray jsonArray = new JSONArray(); +            for (LeaderboardEntry cur: leaderboard) +            { +                JSONObject obj = new JSONObject(); +                obj.put("name", cur.getName()); +                obj.put("score", cur.getScore()); +                obj.put("date",cur.getDate()); +                jsonArray.add(obj); +            } +            jsonArray.writeJSONString(fileWriter); +            fileWriter.flush(); +  +        }  +        catch (IOException e) { +            e.printStackTrace(); +        } +    } + +    /** +     * Adds new leaderboardEntry to list and updates json file +     * @param name: the players name +     * @param score the players score +     */ +    public void addToLeaderboard(String name, int score)  +    { +        leaderboard.add(new LeaderboardEntry(name, score, ""+LocalDate.now())); //do not delete this tho its not a placeholder +        writeLeaderboard(); +    } + +    public ObservableList<LeaderboardEntry> getLeaderboard()  +    { +        return leaderboard; +    } + +    public String toString() +    { +        return title; +    } + +    public boolean isValid() { +        return isValid; +    } + +    public String getTitle() { +        return title; +    } + +    @Override +    public int compareTo(Difficulty d) { +        return priority - d.priority; +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java b/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java new file mode 100755 index 0000000..18e657c --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java @@ -0,0 +1,28 @@ +package net.sowgro.npehero.main; + +public class LeaderboardEntry  +{ +    private int score; +    private String name; +    private String date; + +    public LeaderboardEntry(String name, int score, String date) +    { +        this.name = name; +        this.score = score; +        this.date = date; +    } + +    public int getScore() { +        return score; +    } + +    public String getName() { +        return name; +    } + +    public String getDate() +    { +        return date; +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/Level.java b/src/main/java/net/sowgro/npehero/main/Level.java new file mode 100755 index 0000000..ef264ff --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/Level.java @@ -0,0 +1,285 @@ +package net.sowgro.npehero.main; + +import java.io.File; +import java.util.Collections; +import java.util.Comparator; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.image.Image; +import javafx.scene.paint.Color; +import java.io.FileWriter; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +public class Level  +{ +    public File thisDir; +    private String title = "Unnamed"; +    private String artist = "Unknown"; +    private ObservableList<Difficulty> diffList; +    private ObservableList<Difficulty> validDiffList; +    private boolean isValid; + +    public Image preview; //optional +    public String desc; +    public Image background; //optional +    public Color[] colors = {Color.RED,Color.BLUE,Color.GREEN,Color.PURPLE,Color.YELLOW};//optional, have default colors +    public File song; + +    /** +     * Creates a new level and gives it a file path +     * @param newDir: The path of the Level +     */ +    public Level(File newDir) +    { +        thisDir = newDir; +    } + +    public void readData() +    { +        boolean isValid1 = true; +        if (new File(thisDir, "metadata.json").exists()) +        { +            if (!parseMetadata()) +            { +                System.err.println(new File(thisDir, "metadata.json")+" contains error(s)"); +                isValid1 = false; +            } +        } +        else +        { +            System.err.println(thisDir+" is missing metadata.json"); +            isValid1 = false; +        } + +        if (new File(thisDir, "song.wav").exists()) +        { +            song = new File(thisDir,"song.wav"); +        } +        else +        { +            System.err.println(thisDir+" is missing song.wav"); +            isValid1 = false; +        } + +        if (new File(thisDir, "background.png").exists()) +        { +            background = new Image(new File(thisDir,"background.png").toURI().toString()); +        } + +        if (new File(thisDir, "preview.png").exists()) +        { +            preview = new Image(new File(thisDir,"preview.png").toURI().toString()); +        } + +        diffList = FXCollections.observableArrayList(); +        validDiffList = FXCollections.observableArrayList(); +        for(File cur: thisDir.listFiles()) //iterates through all files/folders in /levels/LEVEL +        { +            if (cur.isDirectory()) //all subfolders within a level folder are difficulties +            { +                Difficulty diff = new Difficulty(cur,this); +                diff.readData(); +                if (diff.isValid) +                { +                    diffList.add(diff); +                    validDiffList.add(diff); +                } +                else +                { +                    diffList.add(diff); +                } +            } +        } +        if (validDiffList.size() == 0) +        { +            System.err.println(thisDir+" contains no valid difficulties"); +            isValid1 = false; +        } + +        Collections.sort(validDiffList); +        Collections.sort(diffList); +        isValid = isValid1; +    } + +    /** +     * Reads in json metadata and assigns values to variables +     */ +    public boolean parseMetadata()  +    { +        boolean isValid = true; +        JSONParser jsonParser = new JSONParser(); //parser to read the file +		File file = new File(thisDir, "metadata.json"); +		try(FileReader reader = new FileReader(file)) +		{ +			Object obj = jsonParser.parse(reader);  +			JSONObject levelStuff = new JSONObject(); +			levelStuff = (JSONObject)(obj); //converts read object to a JSONObject + +            if (levelStuff.containsKey("title")) +            { +                title = (String) levelStuff.get("title"); +            } +            else +            { +                System.err.println(file+" is missing properety title"); +                isValid = false; +            } + +            if (levelStuff.containsKey("artist")) +            { +                artist = (String)(levelStuff.get("artist")); +            } +            else +            { +                System.err.println(file+" is missing properety aritst"); +                isValid = false; +            } + +            if (levelStuff.containsKey("desc")) +            { +                desc = (String) levelStuff.get("desc"); +            } + +            if(( levelStuff).containsKey("color1")) //check for custom colors in a hexadecimal format (zach was lazy for not checking all five colors but i will forgive him) +            { +                colors = new Color[5]; + +                colors[0] = Color.web((String)(levelStuff.get("color1"))); //read in all the custom colors +                colors[1] = Color.web((String)(levelStuff.get("color2"))); +                colors[2] = Color.web((String)(levelStuff.get("color3"))); +                colors[3] = Color.web((String)(levelStuff.get("color4"))); +                colors[4] = Color.web((String)(levelStuff.get("color5"))); +            } +		} +		catch (Exception e)  +		{ +			e.printStackTrace(); +            isValid = false; +		}  +        return isValid; +    } + +    /** +     * Writes metadata to json file +     */ +    public void writeMetadata() +    { +        FileWriter fileWriter; +        try  +        { +            fileWriter = new FileWriter(new File(thisDir, "metadata.json")); +            JSONObject obj = new JSONObject(); +            obj.put("title", title); +            obj.put("artist", artist); +            if (desc != null) +            { +                obj.put("desc", desc); +            } +            obj.put("color1",colors[0].toString()); +            obj.put("color2",colors[1].toString()); +            obj.put("color3",colors[2].toString()); +            obj.put("color4",colors[3].toString()); +            obj.put("color5",colors[4].toString()); +            obj.writeJSONString(fileWriter); +            fileWriter.flush(); +        }  +        catch (IOException e) { +            e.printStackTrace(); +        } +    } + +    /** +     * Adds a difficulty by creating a directory and required files +     * @param text: the name of the directory and default title +     */ +    public void addDiff(String text)  +    { +        File diffDir = new File(thisDir, text); +        diffDir.mkdirs(); +        File metadataDir = new File(diffDir, "metadata.json"); +        File leaderboardDir = new File(diffDir, "leaderboard.json"); +        File notesDir = new File(diffDir, "notes.txt"); +        try { +            metadataDir.createNewFile(); +            leaderboardDir.createNewFile(); +            notesDir.createNewFile(); +        } catch (IOException e) { +            e.printStackTrace(); +        } +        Difficulty temp = new Difficulty(diffDir,this); +        temp.title = text; +        temp.writeMetadata(); +        temp.writeLeaderboard(); +        readData(); +    } + +    /** +     * Removes the difficaulty from the filesystem then reloads the level +     * @param diff: the difficulty to be removed +     */ +    public void removeDiff(Difficulty diff) +    { +        File hold = diff.thisDir; +        try { +            Files.walk(hold.toPath()) +            .sorted(Comparator.reverseOrder()) +            .map(Path::toFile) +            .forEach(File::delete); +        } catch (IOException e) { +            e.printStackTrace(); +        } +        readData(); +    } + +    /** +     * Copies a file into the level directory +     * @param newFile: the file to be copied +     * @param name: the new file name +     */ +    public void addFile(File newFile, String name) +    { +        try { +            Files.copy(newFile.toPath(), new File(thisDir, name).toPath(), StandardCopyOption.REPLACE_EXISTING); +        } catch (IOException e) { +            e.printStackTrace(); +        } +        readData(); +    } + +    public ObservableList<Difficulty> getDiffList() { +        return diffList; +    } + +    public ObservableList<Difficulty> getValidDiffList() { +        return validDiffList; +    } + +    public String getTitle()  +    { +        return title; +    } + +    public void setTitle(String title) { +        this.title = title; +    } + +    public String getArtist()  +    { +        return artist; +    } + +    public void setArtist(String artist) { +        this.artist = artist; +    } + +    public boolean isValid() { +        return isValid; +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/LevelController.java b/src/main/java/net/sowgro/npehero/main/LevelController.java new file mode 100755 index 0000000..68d7450 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/LevelController.java @@ -0,0 +1,95 @@ +package net.sowgro.npehero.main; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +public class LevelController +{ +    File thisDir = new File("levels"); +    private static ObservableList<Level> levelList; +    private static ObservableList<Level> validLevelList; + +    /** +     * Creates a levelController, which holds all the levels +     */ +    public LevelController() +    { +        readData(); +    } + +    /** +     * Reads contents of folder and creates cooresponding levels +     */ +    public void readData() +    { +        levelList = FXCollections.observableArrayList(); +        validLevelList = FXCollections.observableArrayList(); +        for (File cur: thisDir.listFiles()) //iterates through all files/folders in levels +        { +            Level level = new Level(cur); +            level.readData(); +            levelList.add(level); +            if (level.isValid()) +            { +                validLevelList.add(level); +            } +        } +    } + +    /** +     * Adds a level to the list by creating a directory and required files +     * @param text: the name of the directory and default title +     */ +    public void addLevel(String text)  +    { +        File levelDir = new File(thisDir,text); +        levelDir.mkdirs(); +        File metadataDir = new File(levelDir, "metadata.json"); +        try  +        { +            metadataDir.createNewFile(); +        }  +        catch (IOException e)  +        { +            e.printStackTrace(); +        } +        Level temp = new Level(levelDir); +        temp.setTitle(text); +        temp.writeMetadata(); +        readData(); +    } + +    /** +     * Removes level from the filesystem then reloads this levelController +     * @param level: the level to be removed +     */ +    public void removeLevel(Level level) +    { +        File hold = level.thisDir; +        levelList.remove(level); + +        try { +            Files.walk(hold.toPath()) +            .sorted(Comparator.reverseOrder()) +            .map(Path::toFile) +            .forEach(File::delete); +        } catch (IOException e) { +            e.printStackTrace(); +        } +        readData(); +    } + +    public static ObservableList<Level> getLevelList() { +        return levelList; +    } + +    public static ObservableList<Level> getValidLevelList() { +        return validLevelList; +    } +}
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/main/ScoreController.java b/src/main/java/net/sowgro/npehero/main/ScoreController.java new file mode 100755 index 0000000..ba43171 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/ScoreController.java @@ -0,0 +1,117 @@ +package net.sowgro.npehero.main; + +import net.sowgro.npehero.Driver; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class ScoreController{ + +    private int score = 0; +    private int combo = 0; +    private int comboMultiplier=1; +    public StringProperty scoreProperty = new SimpleStringProperty("0"); +    public StringProperty comboProperty = new SimpleStringProperty("0"); + +    /** +     * Called when the user performs a perfect hit +     */ +    public void perfect() { +        combo(); +        score += 300*comboMultiplier; +        scoreProperty.setValue(score+""); +        comboProperty.setValue(combo +""); +        // System.out.println("Perfect!"); +    } + +    /** +     * called when the user performs an okay hit +     */ +    public void good() { +        combo(); +        score += 100*comboMultiplier; +        scoreProperty.setValue(score+""); +        comboProperty.setValue(combo+""); +        // System.out.println("Good"); +    } + +    /** +     * Called when the user misses a note +     */ +    public void miss(boolean muted) { +        if (!muted) { +            Driver.soundController.playSfx("miss"); +        } +        combo = 0; +        comboMultiplier = 1; +        scoreProperty.setValue(score+""); +        comboProperty.setValue(combo+""); +        // System.out.println("Miss"); +    } + +    /* +     * Increments the combo by one +     */ +    private void combo() { +        Driver.soundController.playSfx("hit"); +        combo++; +         +        if (combo == 2) { +            comboMultiplier = 2; +        } +         +        if (combo == 4) { +            comboMultiplier = 4; +        } +         +        if (combo == 8) { +            comboMultiplier = 8; +        } +    } + +    /** +     * @return current score +     */ +    public int getScore() +    { +        return score; +    } + +    /** +     * @return current combo +     */ +    public int getCombo() +    { +        return combo; +    } + +    /** +     * @param newScore: the score to be set, only used in debug +     */ +    public void setScore(int newScore) +    { +        score = newScore; +        scoreProperty.setValue(newScore+""); +    } + +    /** +     * @param newCombo: the combo to be set, only used in debug +     */ +    public void setCombo(int newCombo) +    { +        combo = newCombo; +        comboProperty.setValue(newCombo+""); +    } + +    /** +     * prints values into console +     */ +    public void print() +    { +        System.out.println("--"); +        System.out.println(combo); +        System.out.println(score); +        System.out.println(""); +        System.out.println(scoreProperty); +        System.out.println(comboProperty); +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/SettingsController.java b/src/main/java/net/sowgro/npehero/main/SettingsController.java new file mode 100755 index 0000000..d5f3e3b --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/SettingsController.java @@ -0,0 +1,62 @@ +package net.sowgro.npehero.main;
 +
 +import java.io.FileWriter;
 +import java.io.File;
 +import java.io.FileReader;
 +import java.io.IOException;
 +import org.json.simple.JSONObject;
 +import org.json.simple.parser.JSONParser;
 +import javafx.beans.property.SimpleDoubleProperty;
 +
 +public class SettingsController 
 +{
 +	public SimpleDoubleProperty effectsVol = new SimpleDoubleProperty(1);
 +	public SimpleDoubleProperty musicVol = new SimpleDoubleProperty(1);
 +	private File file = new File("settings.json");
 +
 +	public SettingsController()
 +	{
 +		read();
 +	}
 +	
 +	/**
 +	 * reads json data from settings.json
 +	 */
 +	public void read()
 +	{
 +		JSONParser jsonParser = new JSONParser(); //parser to read the file
 +		try(FileReader reader = new FileReader(file))
 +		{
 +			Object obj = jsonParser.parse(reader); 
 +			JSONObject settings = new JSONObject();
 +			settings = (JSONObject)(obj); //converts read object to a JSONObject
 +			
 +			effectsVol.set(Double.parseDouble(settings.get("effectsVol")+""));
 +			musicVol.set(Double.parseDouble(settings.get("musicVol")+""));
 +		}
 +		catch (Exception e) 
 +		{
 +			e.printStackTrace();
 +		}		
 +	}
 +
 +	/**
 +	 * writes json data to settings.json
 +	 */
 +	public void write()
 +	{
 +		FileWriter fileWriter;
 +		try
 +		{
 +			fileWriter = new FileWriter(file);
 +			JSONObject obj = new JSONObject();
 +			obj.put("musicVol", musicVol.getValue());
 +			obj.put("effectsVol", effectsVol.getValue());
 +            obj.writeJSONString(fileWriter);
 +            fileWriter.flush();
 +        } 
 +		catch (IOException e) {
 +            e.printStackTrace();
 +        }
 +	}
 +}
 diff --git a/src/main/java/net/sowgro/npehero/main/SoundController.java b/src/main/java/net/sowgro/npehero/main/SoundController.java new file mode 100755 index 0000000..da80ab4 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/main/SoundController.java @@ -0,0 +1,97 @@ +package net.sowgro.npehero.main; + +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; + +import net.sowgro.npehero.Driver; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.util.Duration; + +public class SoundController  +{ +    public MediaPlayer songMediaPlayer; +    public MediaPlayer sfxMediaPlayer; +    private final HashMap<String,MediaPlayer> effects = new HashMap<>(); +    private final File mainMenuSong; + +    { +        try { +            mainMenuSong = new File(Driver.getResource("fairyfountain.wav").toURI()); +        } catch (URISyntaxException e) { +            throw new RuntimeException(e); +        } +    } + + +    /** +     * creates a new sound controller and starts playing the main menu music +     */ +    public SoundController()  +    { +        effects.put("hit", new MediaPlayer(new Media(Driver.getResource("hit.wav").toString()))); +        effects.put("miss", new MediaPlayer(new Media(Driver.getResource("miss.wav").toString()))); +        effects.put("forward", new MediaPlayer(new Media(Driver.getResource("forward.wav").toString()))); +        effects.put("backward", new MediaPlayer(new Media(Driver.getResource("backward.wav").toString()))); +        effects.forEach((key,value) -> { +            value.volumeProperty().bind(Driver.settingsController.effectsVol); +        }); +        playMenuSong(); +    } + +    /** +     * plays the song that is passed in. +     * @param songFile: the song +     */ +    public void playSong(File songFile) +    { +        if (songMediaPlayer != null) +        { +            songMediaPlayer.stop(); +        } +        Media song = new Media(songFile.toURI().toString()); +        songMediaPlayer = new MediaPlayer(song); +        songMediaPlayer.volumeProperty().bind(Driver.settingsController.musicVol); +        songMediaPlayer.play(); +    } + +    /** +     * plays the main menu song +     */ +    public void playMenuSong() +    { +        if (!mainMenuSong.exists()) { +            System.out.println("NOT EXIST " + mainMenuSong.getAbsolutePath()); +            return; +        } +        playSong(mainMenuSong); +        songMediaPlayer.setCycleCount(MediaPlayer.INDEFINITE); +        songMediaPlayer.play(); +    } + +    /** +     * stops the currently playing song +     */ +    public void endSong() +    { +        if (songMediaPlayer != null) +        { +            songMediaPlayer.stop(); +        } +    } + +    /** +     * plays a sound effect +     * for the volume slider to take effect each mediaplayer needs to be preloaded. +     * this rewinds and played the proper mediaplayer for the sound +     * @param preset: a string of the name of the sound. possible sounds assigned in the constructor +     */ +    public void playSfx(String preset) +    { +        effects.get(preset).seek(new Duration(0)); +        effects.get(preset).play(); +    } +}
\ No newline at end of file  | 
