diff options
author | sowgro <tpoke.ferrari@gmail.com> | 2024-07-08 02:41:31 -0400 |
---|---|---|
committer | sowgro <tpoke.ferrari@gmail.com> | 2024-07-08 02:41:31 -0400 |
commit | ee2229339429d50afa33e2f8b9c0ee0939766290 (patch) | |
tree | a5ee54bd23c24950e9b10815f3e87605906992d8 /src/main/java/net | |
parent | 9e1371424bdf4c31d756d686313730d4c61f7ac5 (diff) | |
download | NPEhero-ee2229339429d50afa33e2f8b9c0ee0939766290.tar.gz NPEhero-ee2229339429d50afa33e2f8b9c0ee0939766290.tar.bz2 NPEhero-ee2229339429d50afa33e2f8b9c0ee0939766290.zip |
Change project structure, embed resources into jar and remove libraries from source control
Diffstat (limited to 'src/main/java/net')
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 |