diff options
| author | sowgro <tpoke.ferrari@gmail.com> | 2024-07-28 01:07:41 -0400 | 
|---|---|---|
| committer | sowgro <tpoke.ferrari@gmail.com> | 2024-07-28 01:07:41 -0400 | 
| commit | 0ce09f72f4af26412356b9699d402b52dbcfc94f (patch) | |
| tree | b01b94b1b80d1f3fc5aea559b3718024b79cfe91 /src | |
| parent | d04c277edff957d14b6261dd38da43c18b7ba189 (diff) | |
| download | NPEhero-0ce09f72f4af26412356b9699d402b52dbcfc94f.tar.gz NPEhero-0ce09f72f4af26412356b9699d402b52dbcfc94f.tar.bz2 NPEhero-0ce09f72f4af26412356b9699d402b52dbcfc94f.zip | |
Finalize level API and new Json library
Diffstat (limited to 'src')
36 files changed, 819 insertions, 750 deletions
| diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 09cc9e7..02dcac8 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,7 +3,7 @@ module net.sowgro.npehero {      requires javafx.fxml;      requires javafx.media;      requires java.desktop; -    requires com.fasterxml.jackson.databind; +    requires com.google.gson;      exports net.sowgro.npehero;  }
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/Driver.java b/src/main/java/net/sowgro/npehero/Driver.java index 3e1bd6e..2845cf4 100755 --- a/src/main/java/net/sowgro/npehero/Driver.java +++ b/src/main/java/net/sowgro/npehero/Driver.java @@ -13,12 +13,12 @@ import javafx.scene.layout.*;  import javafx.stage.Stage;  import javafx.util.Duration;  import net.sowgro.npehero.editor.ErrorDisplay; +import net.sowgro.npehero.levelapi.Levels;  import net.sowgro.npehero.main.*;  import net.sowgro.npehero.gui.MainMenu; -import java.io.FileNotFoundException; +import java.io.IOException;  import java.net.URL; -import java.util.List;  import java.util.Stack; @@ -81,21 +81,30 @@ public class Driver extends Application          primaryStage.show();          Stack<String> errors = new Stack<>(); +        System.out.println("Loading settings...");          try {              Settings.read(); +            System.out.println("Settings loaded");          } catch (Exception e) { +            e.printStackTrace();              errors.push("Failed to load settings from file\n"+e);          } -        try { -            Levels.readData(); -        } catch (FileNotFoundException e) { -            errors.push("Failed to load levels: Level folder is missing\n"); -        } +        System.out.println("Loading controls...");          try {              Control.readFromFile(); +            System.out.println("Controls loaded");          } catch (Exception e) { +            e.printStackTrace();              errors.push("Failed to load controls from file\n"+e);          } +        System.out.println("Loading levels..."); +        try { +            Levels.readData(); +            System.out.println("Loaded " + Levels.list.size() + " levels (" + Levels.getValidList().size() + " valid)"); +        } catch (IOException e) { +            e.printStackTrace(); +            errors.push("Failed to load levels\n"); +        }          Page last = new MainMenu();          while (!errors.empty()) {              last = new ErrorDisplay(errors.pop(), last); diff --git a/src/main/java/net/sowgro/npehero/editor/DiffEditor.java b/src/main/java/net/sowgro/npehero/editor/DiffEditor.java index 6940485..0eb4968 100755 --- a/src/main/java/net/sowgro/npehero/editor/DiffEditor.java +++ b/src/main/java/net/sowgro/npehero/editor/DiffEditor.java @@ -11,8 +11,12 @@ import net.sowgro.npehero.gameplay.Block;  import net.sowgro.npehero.gui.LevelSurround;  import javafx.scene.layout.VBox;  import javafx.scene.text.Text; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Note;  import net.sowgro.npehero.main.*; +import java.io.IOException; +  public class DiffEditor extends Page  {      Difficulty diff; @@ -37,13 +41,15 @@ public class DiffEditor extends Page          editNotes.setOnAction(_ -> {              if (diff.level.song == null) {                  Driver.setMenu(new ErrorDisplay("You must import a song file before editing the notes!", this)); +                return;              }              if (diff.bpm != 0.0) {                  Driver.setMenu(new ErrorDisplay( -                        "Note:\nThe new notes editor does not support bpm and beat based songs. If you continue the beats will be converted to seconds.", +                        "Note:\nThe new notes editor does not support bpm and beat based songs. If you continue, the notes will be converted.",                          this,                          new NotesEditor2(diff, this)                  )); +                return;              }              Driver.setMenu(new NotesEditor2(diff, this));          }); @@ -57,7 +63,15 @@ public class DiffEditor extends Page          Label scoresLable = new Label("Scores");          Button editScores = new Button("Clear leaderboard"); -        editScores.setOnAction(_ -> diff.leaderboard.entries.clear()); +        editScores.setOnAction(_ -> { +            diff.leaderboard.entries.clear(); +            try { +                diff.leaderboard.save(); +            } catch (IOException e) { +                e.printStackTrace(); +                Driver.setMenu(new ErrorDisplay("Failed to clear the leaderboard:\n"+e, this)); +            } +        });          Button playLevel = new Button("Play level");          playLevel.setOnAction(_ -> { @@ -74,7 +88,13 @@ public class DiffEditor extends Page              diff.title = title.getText();  //            diff.bpm = Double.parseDouble(bpm.getText());  //            diff.numBeats = Integer.parseInt(numBeats.getText()); -            diff.write(); +            try { +                diff.writeMetadata(); +            } catch (IOException e) { +                e.printStackTrace(); +                //TODO +                throw new RuntimeException(e); +            }          });          HBox scrollContent = new HBox(); diff --git a/src/main/java/net/sowgro/npehero/editor/DiffList.java b/src/main/java/net/sowgro/npehero/editor/DiffList.java index 7017c32..9e2df9b 100755 --- a/src/main/java/net/sowgro/npehero/editor/DiffList.java +++ b/src/main/java/net/sowgro/npehero/editor/DiffList.java @@ -9,12 +9,13 @@ 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.Level; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound;  import java.io.IOException; +import java.nio.file.FileAlreadyExistsException;  import java.util.Collections;  public class DiffList extends Page @@ -68,6 +69,7 @@ public class DiffList extends Page              try {                  level.difficulties.remove(diffs.getSelectionModel().getSelectedItem());              } catch (IOException ex) { +                ex.printStackTrace();                  Driver.setMenu(new ErrorDisplay("Failed to remove difficulty\n"+e, this));              }          }); @@ -76,7 +78,11 @@ public class DiffList extends Page          Button refresh = new Button("Refresh");          refresh.setOnAction(e -> { -//            level.readData(); +            try { +                level.difficulties.read(); +            } catch (IOException ex) { +                // TODO +            }  //            diffs.setItems(level.difficulties.list.sorted());              diffs.refresh();          }); @@ -93,7 +99,12 @@ public class DiffList extends Page                  return;              }              Collections.swap(diffList, oldIndex, oldIndex-1); -            level.difficulties.saveOrder(); +            try { +                level.difficulties.saveOrder(); +            } catch (IOException e) { +                e.printStackTrace(); +                Driver.setMenu(new ErrorDisplay("Failed to move difficulty\n"+e,this)); +            }          });          Button moveDown = new Button("Move Down"); @@ -106,7 +117,12 @@ public class DiffList extends Page                  return;              }              Collections.swap(diffList, oldIndex, oldIndex+1); -            level.difficulties.saveOrder(); +            try { +                level.difficulties.saveOrder(); +            } catch (IOException e) { +                e.printStackTrace(); +                Driver.setMenu(new ErrorDisplay("Failed to move difficulty\n"+e,this)); +            }          });          VBox buttons = new VBox(); @@ -148,8 +164,11 @@ public class DiffList extends Page          newLevelButton.setOnAction(_ -> {              try {                  level.difficulties.add(newLevelEntry.getText()); +            } catch (FileAlreadyExistsException e) { +                Driver.setMenu(new ErrorDisplay("Failed to add level\nA difficulty already exists with the folder name " + e.getFile(), this));              } catch (IOException e) {                  Driver.setMenu(new ErrorDisplay("Failed to add level\n"+e, this)); +                e.printStackTrace();              }              newLevelEntry.clear();              refresh.fire(); diff --git a/src/main/java/net/sowgro/npehero/editor/ErrorDisplay.java b/src/main/java/net/sowgro/npehero/editor/ErrorDisplay.java index b3ec751..c07d12f 100644 --- a/src/main/java/net/sowgro/npehero/editor/ErrorDisplay.java +++ b/src/main/java/net/sowgro/npehero/editor/ErrorDisplay.java @@ -23,9 +23,11 @@ public class ErrorDisplay extends Page {      public ErrorDisplay(String message, Page prev) {          Label main = new Label(message);          main.getStyleClass().add("box"); +        main.setPadding(new Insets(10)); +        main.setWrapText(true);          Button exit = new Button(); -        exit.setText("Back"); +        exit.setText("Ok");          exit.setOnAction(e -> {              Sound.playSfx(Sound.BACKWARD);              Driver.setMenu(prev); @@ -50,6 +52,7 @@ public class ErrorDisplay extends Page {          Label main = new Label(message);          main.getStyleClass().add("box");          main.setPadding(new Insets(10)); +        main.setWrapText(true);          Button exit = new Button();          exit.setText("Cancel"); diff --git a/src/main/java/net/sowgro/npehero/editor/LevelEditor.java b/src/main/java/net/sowgro/npehero/editor/LevelEditor.java index b3ae2b8..ae56d44 100755 --- a/src/main/java/net/sowgro/npehero/editor/LevelEditor.java +++ b/src/main/java/net/sowgro/npehero/editor/LevelEditor.java @@ -1,6 +1,7 @@  package net.sowgro.npehero.editor;  import java.io.File; +import java.io.IOException;  import javafx.beans.property.ReadOnlyStringWrapper;  import javafx.geometry.Pos; @@ -10,10 +11,16 @@ import javafx.scene.text.Text;  import javafx.stage.FileChooser;  import javafx.stage.FileChooser.ExtensionFilter;  import net.sowgro.npehero.Driver; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.main.*;  public class LevelEditor extends Page  { +    private final ValidIndicator songValid = new ValidIndicator(); +    private final ValidIndicator diffsInvalid = new ValidIndicator(); +    Level level; +      private HBox content = new HBox();      private File selectedSong = null; @@ -22,6 +29,7 @@ public class LevelEditor extends Page      public LevelEditor(Level level, Page prev)      { +        this.level = level;          Text folderNameLabel = new Text("Folder name");          TextField folderName = new TextField();          if (level.dir != null) { @@ -55,10 +63,6 @@ public class LevelEditor extends Page          HBox colorPickerBox = new HBox();          colorPickerBox.getChildren().addAll(colorsPickers); -        ValidIndicator songValid = new ValidIndicator(); -        if (level.song == null) { -            songValid.setInvalid("Missing song file!"); -        }          HBox filesLabel = new HBox(new Text("Files"), songValid);          FileChooser backgroundChooser = new FileChooser(); @@ -77,10 +81,6 @@ public class LevelEditor extends Page          songButton.setOnAction(_ -> selectedSong = songChooser.showOpenDialog(Driver.primaryStage)); -        ValidIndicator diffsInvalid = new ValidIndicator(); -        if (level.difficulties.getValidList().isEmpty()) { -            diffsInvalid.setInvalid("This level contains no valid difficulties!"); -        }          HBox diffLabel = new HBox(new Text("Difficulties"), diffsInvalid);          diffLabel.setSpacing(5); @@ -134,18 +134,25 @@ public class LevelEditor extends Page              try {                  if (selectedBackground != null && selectedBackground.exists()) { -                    level.addFile(selectedBackground, "background." + getFileExtension(selectedBackground)); +                    level.addFile(selectedBackground, "background");                  }                  if (selectedPreview != null && selectedPreview.exists()) { -                    level.addFile(selectedPreview, "preview." + getFileExtension(selectedPreview)); +                    level.addFile(selectedPreview, "preview");                  }                  if (selectedSong != null) { -                    level.addFile(selectedSong, "song." + getFileExtension(selectedSong)); +                    level.addFile(selectedSong, "song");                  } -            } catch (Exception _) { +            } catch (Exception ex) { +                // TODO +                ex.printStackTrace(); +            } +            try { +                level.writeMetadata(); +            } catch (IOException ex) {                  // TODO +                ex.printStackTrace();              } -            level.writeMetadata(); +            validate();          });          VBox left = new VBox(filesLabel, songButton, previewButton, backgroundButton, colorsLabel, colorPickerBox); @@ -189,12 +196,19 @@ public class LevelEditor extends Page          return content;      } -    /** -     * Get the extension of a file. -     * @param file The file to return the extension of -     * @return The extension of the file in the format "*.ext" -     */ -    public String getFileExtension(File file) { -        return file.getName().substring(file.getName().lastIndexOf('.') + 1); +    @Override +    public void onView() { +        validate(); +    } + +    public void validate() { +        if (level.difficulties.getValidList().isEmpty()) { +            diffsInvalid.setInvalid("This level contains no valid difficulties!"); +        } else { +            diffsInvalid.setValid(); +        } +        if (level.song == null) { +            songValid.setInvalid("Missing song file!"); +        }      }  }
\ No newline at end of file diff --git a/src/main/java/net/sowgro/npehero/editor/LevelList.java b/src/main/java/net/sowgro/npehero/editor/LevelList.java index 1bc85dd..86c65b5 100755 --- a/src/main/java/net/sowgro/npehero/editor/LevelList.java +++ b/src/main/java/net/sowgro/npehero/editor/LevelList.java @@ -9,13 +9,14 @@ import net.sowgro.npehero.Driver;  import javafx.scene.layout.HBox;  import javafx.scene.layout.VBox;  import net.sowgro.npehero.gui.MainMenu; -import net.sowgro.npehero.main.Level; -import net.sowgro.npehero.main.Levels; +import net.sowgro.npehero.levelapi.Level; +import net.sowgro.npehero.levelapi.Levels;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound;  import java.io.FileNotFoundException;  import java.io.IOException; +import java.nio.file.FileAlreadyExistsException;  public class LevelList extends Page  { @@ -71,6 +72,7 @@ public class LevelList extends Page                  Levels.remove(levels.getSelectionModel().getSelectedItem());              } catch (IOException ex) {                  Driver.setMenu(new ErrorDisplay("Failed to remove this level\n"+e.toString(), this)); +                ex.printStackTrace();              }          });          remove.setDisable(true); @@ -80,8 +82,9 @@ public class LevelList extends Page          refresh.setOnAction(e -> {              try {                  Levels.readData(); -            } catch (FileNotFoundException ex) { +            } catch (IOException ex) {                  Driver.setMenu(new ErrorDisplay("Failed to load levels: Level folder is missing\n"+e.toString(), this)); +                ex.printStackTrace();              }              levels.setItems(Levels.list);          }); @@ -135,8 +138,12 @@ public class LevelList extends Page          newLevelButton.setOnAction(_ -> {              try {                  Levels.add(newLevelEntry.getText()); +            } catch (FileAlreadyExistsException e) { +                Driver.setMenu(new ErrorDisplay("Failed to add level\nA level already exists with the folder name " + e.getFile(), this)); +                e.printStackTrace();              } catch (IOException e) {                  Driver.setMenu(new ErrorDisplay("Failed to create this level\n"+e.toString(), this)); +                e.printStackTrace();              }              newLevelEntry.clear();              refresh.fire(); diff --git a/src/main/java/net/sowgro/npehero/editor/NotesEditor.java b/src/main/java/net/sowgro/npehero/editor/NotesEditor.java index 133330f..da5af33 100755 --- a/src/main/java/net/sowgro/npehero/editor/NotesEditor.java +++ b/src/main/java/net/sowgro/npehero/editor/NotesEditor.java @@ -16,7 +16,7 @@ import javafx.scene.control.Button;  import javafx.scene.layout.VBox;  import javafx.scene.text.Text;  import net.sowgro.npehero.main.Control; -import net.sowgro.npehero.main.Difficulty; +import net.sowgro.npehero.levelapi.Difficulty;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound; @@ -64,11 +64,11 @@ public class NotesEditor extends Page          content.getChildren().add(centerBox);          content.setAlignment(Pos.CENTER); -        try { -            writer = new PrintWriter(diff.notes.getFile(), "UTF-8"); -        } catch (FileNotFoundException | UnsupportedEncodingException e) { -            throw new RuntimeException(e); -        } +//        try { +//            writer = new PrintWriter(diff.notes.getFile(), "UTF-8"); +//        } catch (FileNotFoundException | UnsupportedEncodingException e) { +//            throw new RuntimeException(e); +//        }          Scene scene = Driver.primaryStage.getScene();          scene.addEventFilter(KeyEvent.KEY_PRESSED, e -> { diff --git a/src/main/java/net/sowgro/npehero/editor/NotesEditor2.java b/src/main/java/net/sowgro/npehero/editor/NotesEditor2.java index 8d17900..b6810d8 100644 --- a/src/main/java/net/sowgro/npehero/editor/NotesEditor2.java +++ b/src/main/java/net/sowgro/npehero/editor/NotesEditor2.java @@ -16,7 +16,6 @@ import javafx.scene.layout.HBox;  import javafx.scene.layout.Pane;  import javafx.scene.layout.StackPane;  import javafx.scene.layout.VBox; -import javafx.scene.media.Media;  import javafx.scene.media.MediaPlayer;  import javafx.scene.paint.Color;  import javafx.scene.shape.Line; @@ -25,9 +24,12 @@ import javafx.util.Duration;  import net.sowgro.npehero.Driver;  import net.sowgro.npehero.gameplay.Block;  import net.sowgro.npehero.gameplay.Target; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Note;  import net.sowgro.npehero.main.*;  import net.sowgro.npehero.main.Control; +import java.io.IOException;  import java.util.concurrent.atomic.AtomicInteger;  public class NotesEditor2 extends Page { @@ -164,7 +166,12 @@ public class NotesEditor2 extends Page {          save.setText("Save");          save.setOnAction(_ -> {              diff.notes.list = noteList; -            diff.notes.writeFile(); +            try { +                diff.notes.writeFile(); +            } catch (IOException e) { +                // TODO +                throw new RuntimeException(e); +            }              diff.endTime = newEndTime.get();              Sound.playSfx(Sound.BACKWARD);              Driver.setMenu(new DiffEditor(diff, prev.prev)); diff --git a/src/main/java/net/sowgro/npehero/gameplay/Block.java b/src/main/java/net/sowgro/npehero/gameplay/Block.java index c55a7bf..9d68ab6 100755 --- a/src/main/java/net/sowgro/npehero/gameplay/Block.java +++ b/src/main/java/net/sowgro/npehero/gameplay/Block.java @@ -6,7 +6,7 @@ import javafx.scene.effect.BlurType;  import javafx.scene.effect.DropShadow;  import javafx.scene.paint.Color;  import javafx.scene.shape.Rectangle; -import net.sowgro.npehero.main.Note; +import net.sowgro.npehero.levelapi.Note;  /**   * A block is a visual representation of a note on the screen. This is used both in the editor and in during the game play. diff --git a/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java b/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java index 36f706c..33f9a51 100755 --- a/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java +++ b/src/main/java/net/sowgro/npehero/gameplay/SongPlayer.java @@ -1,6 +1,5 @@  package net.sowgro.npehero.gameplay; -import java.io.File;  import java.io.FileNotFoundException;  import java.io.IOException;  import java.util.ArrayList; @@ -14,6 +13,8 @@ import javafx.event.EventHandler;  import javafx.scene.input.KeyEvent;  import javafx.scene.media.Media;  import net.sowgro.npehero.Driver; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.main.*;  import net.sowgro.npehero.gui.GameOver;  import javafx.geometry.Pos; diff --git a/src/main/java/net/sowgro/npehero/gui/ControlEditor.java b/src/main/java/net/sowgro/npehero/gui/ControlEditor.java index 90161e0..7fd288b 100644 --- a/src/main/java/net/sowgro/npehero/gui/ControlEditor.java +++ b/src/main/java/net/sowgro/npehero/gui/ControlEditor.java @@ -76,6 +76,7 @@ public class ControlEditor extends Page {                          Control.writeToFile();                      } catch (IOException e) {                          Driver.setMenu(new ErrorDisplay("An error occured while saving your controls\n"+e, this)); +                        e.printStackTrace();                      }                  });                  controlButton.setOnAction(_ -> { diff --git a/src/main/java/net/sowgro/npehero/gui/GameOver.java b/src/main/java/net/sowgro/npehero/gui/GameOver.java index dd23f5f..b66d0c6 100755 --- a/src/main/java/net/sowgro/npehero/gui/GameOver.java +++ b/src/main/java/net/sowgro/npehero/gui/GameOver.java @@ -10,11 +10,14 @@ 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; +import net.sowgro.npehero.editor.ErrorDisplay; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound; +import java.io.IOException; +  public class GameOver extends Page  {      HBox content = new HBox(); @@ -67,7 +70,12 @@ public class GameOver extends Page              Sound.playSfx(Sound.FORWARD);              save.setDisable(true);              name.setDisable(true); -            diff.leaderboard.add(name.getText(), score2); +            try { +                diff.leaderboard.add(name.getText(), score2); +            } catch (IOException e) { +                e.printStackTrace(); +                Driver.setMenu(new ErrorDisplay("Failed to save score to leaderboard:\n"+e, this)); +            }          });          BorderPane b = new BorderPane(); diff --git a/src/main/java/net/sowgro/npehero/gui/LeaderboardView.java b/src/main/java/net/sowgro/npehero/gui/LeaderboardView.java index 46ce22f..1a87e27 100755 --- a/src/main/java/net/sowgro/npehero/gui/LeaderboardView.java +++ b/src/main/java/net/sowgro/npehero/gui/LeaderboardView.java @@ -10,6 +10,8 @@ import javafx.scene.layout.HBox;  import javafx.scene.layout.Pane;  import javafx.scene.layout.VBox;  import net.sowgro.npehero.Driver; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.LeaderboardEntry;  import net.sowgro.npehero.main.*;  public class LeaderboardView extends Page @@ -29,9 +31,9 @@ public class LeaderboardView extends Page          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())); +        nameCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().name)); +        scoreCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().score + "")); +        dateCol.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().date));          scores.setItems(diff.leaderboard.entries); diff --git a/src/main/java/net/sowgro/npehero/gui/LevelDetails.java b/src/main/java/net/sowgro/npehero/gui/LevelDetails.java index 4bd1e09..55c45bd 100755 --- a/src/main/java/net/sowgro/npehero/gui/LevelDetails.java +++ b/src/main/java/net/sowgro/npehero/gui/LevelDetails.java @@ -15,8 +15,8 @@ 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; +import net.sowgro.npehero.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.main.Sound;  public class LevelDetails extends VBox diff --git a/src/main/java/net/sowgro/npehero/gui/LevelSelector.java b/src/main/java/net/sowgro/npehero/gui/LevelSelector.java index 3c8f25f..b2c028b 100755 --- a/src/main/java/net/sowgro/npehero/gui/LevelSelector.java +++ b/src/main/java/net/sowgro/npehero/gui/LevelSelector.java @@ -9,8 +9,8 @@ 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.Levels; +import net.sowgro.npehero.levelapi.Level; +import net.sowgro.npehero.levelapi.Levels;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound; diff --git a/src/main/java/net/sowgro/npehero/gui/LevelSurround.java b/src/main/java/net/sowgro/npehero/gui/LevelSurround.java index ea1eb30..8ab722d 100755 --- a/src/main/java/net/sowgro/npehero/gui/LevelSurround.java +++ b/src/main/java/net/sowgro/npehero/gui/LevelSurround.java @@ -11,8 +11,8 @@ 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.levelapi.Difficulty; +import net.sowgro.npehero.levelapi.Level;  import net.sowgro.npehero.gameplay.ScoreController;  import net.sowgro.npehero.main.Page;  import net.sowgro.npehero.main.Sound; @@ -28,7 +28,7 @@ public class LevelSurround extends Page          Button exit = new Button();          exit.setText("Back"); -        exit.setOnAction(e -> { +        exit.setOnAction(_ -> {              Driver.setMenu(prev);              Sound.playSfx(Sound.BACKWARD);              game.cancel(); diff --git a/src/main/java/net/sowgro/npehero/gui/MainMenu.java b/src/main/java/net/sowgro/npehero/gui/MainMenu.java index 3b8c8db..6160245 100755 --- a/src/main/java/net/sowgro/npehero/gui/MainMenu.java +++ b/src/main/java/net/sowgro/npehero/gui/MainMenu.java @@ -1,5 +1,6 @@  package net.sowgro.npehero.gui; +import javafx.application.Platform;  import javafx.geometry.Pos;  import javafx.scene.control.Button;  import javafx.scene.effect.BlurType; @@ -55,8 +56,8 @@ public class MainMenu extends Page {          exit.setOnAction(_ -> {              Sound.playSfx(Sound.BACKWARD);  //            Driver.quit(); -//            Platform.exit(); -            System.exit(0); +            Platform.exit(); +//            System.exit(0);          });          VBox buttonBox = new VBox(); diff --git a/src/main/java/net/sowgro/npehero/gui/SettingsEditor.java b/src/main/java/net/sowgro/npehero/gui/SettingsEditor.java index 12555ed..efd136a 100755 --- a/src/main/java/net/sowgro/npehero/gui/SettingsEditor.java +++ b/src/main/java/net/sowgro/npehero/gui/SettingsEditor.java @@ -95,7 +95,8 @@ public class SettingsEditor extends Page              try {                  Settings.save();              } catch (IOException ex) { -                Driver.setMenu(new ErrorDisplay("Failed to save settings"+e, this)); +                ex.printStackTrace(); +                Driver.setMenu(new ErrorDisplay("Failed to save settings"+ex, this));              }              Sound.playSfx(Sound.BACKWARD);              Driver.setMenu(new MainMenu()); diff --git a/src/main/java/net/sowgro/npehero/levelapi/Difficulties.java b/src/main/java/net/sowgro/npehero/levelapi/Difficulties.java new file mode 100644 index 0000000..cffd95e --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/Difficulties.java @@ -0,0 +1,127 @@ +package net.sowgro.npehero.levelapi; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +/** + * Responsible for the list of difficulties in a level + */ +public class Difficulties { + +    public final ObservableList<Difficulty> list = FXCollections.observableArrayList(); +    public final HashMap<String, Exception> problems = new HashMap<>(); + +    private final Level level; + +    /** +     * Creates a new Difficulties object +     * @param level the file path of the level +     * @throws IOException If there is a problem reading in the difficulties +     */ +    public Difficulties(Level level) throws IOException { +        this.level = level; +        read(); +    } + +    /** +     * Loads difficulties +     * <p> +     * Creates difficulty objects out of each subfolder in the level and adds it to the list. +     * @throws IOException If there is a problem reading in the difficulties +     */ +    public void read() throws IOException { +        list.clear(); +        File[] fileList = level.dir.listFiles(); +        if (fileList == null) { +            throw new FileNotFoundException(); +        } +        for(File cur: fileList) { +            if (cur.isDirectory()) { +                try { +                    Difficulty diff = new Difficulty(cur, level); +                    list.add(diff); +                } catch (IOException e) { +                    problems.put("", e); +                    e.printStackTrace(); +                } +            } +        } +        list.sort(Comparator.naturalOrder()); +    } + +    /** +     * Removes a difficulty +     * <p> +     * Recursively deletes the folder and removes it from the list +     * @param diff: The difficulty to remove. +     * @throws IOException If there is a problem removing the difficulty. +     */ +    public void remove(Difficulty diff) throws IOException { +        File hold = diff.thisDir; +        Files.walk(hold.toPath()) +                .sorted(Comparator.reverseOrder()) +                .map(Path::toFile) +                .forEach(File::delete); +        list.remove(diff); +    } + +    /** +     * Adds a difficulty +     * <p> +     * Creates the directory and required files +     * @param text The name of the directory +     * @throws IOException If there is a problem adding the level +     */ +    public void add(String text) throws IOException { +        File diffDir = new File(level.dir, text.toLowerCase().replaceAll("\\W+", "-")); +        if (diffDir.exists()) { +            throw new FileAlreadyExistsException(diffDir.getName()); +        } +        if (diffDir.mkdirs()) { +            Difficulty temp = new Difficulty(diffDir, level); +            temp.title = text; +            list.add(temp); +            list.sort(Comparator.naturalOrder()); +        } +        else { +            throw new IOException(); +        } +    } + +    /** +     * Saves the order of the difficulties in the list +     * <p> +     * Updates the order variable of each difficulty in the list to match their index in the list +     * @throws IOException If there is a problem saving the difficulty's metadata file +     */ +    public void saveOrder() throws IOException { +        for (Difficulty d : list) { +            d.order = list.indexOf(d); +            d.writeMetadata(); +        } +    } + +    /** +     * Get a list of only the valid difficulties in the level. +     * @return A list of the valid difficulties. +     */ +    public List<Difficulty> getValidList() { +        ObservableList<Difficulty> validList = FXCollections.observableArrayList(); +        for (Difficulty difficulty : list) { +            if (difficulty.isValid()) { +                validList.add(difficulty); +            } +        } +        return validList; +    } +} diff --git a/src/main/java/net/sowgro/npehero/levelapi/Difficulty.java b/src/main/java/net/sowgro/npehero/levelapi/Difficulty.java new file mode 100755 index 0000000..2e99a7a --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/Difficulty.java @@ -0,0 +1,95 @@ +package net.sowgro.npehero.levelapi; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.ToNumberPolicy; + +import java.io.*; +import java.util.Map; + +/** + * Represents a difficulty + * Responsible for the data in metadata.yml + */ +public class Difficulty implements Comparable<Difficulty> +{ +    public final File thisDir; +    public final Level level; + +    public String title = "Unnamed"; +    public Double bpm = 0.0; +    public double endTime = 0; +    public int order = 0; + +    public final Leaderboard leaderboard; +    public final Notes notes; + +    private final Gson jsonParser = new GsonBuilder().serializeNulls().setPrettyPrinting().setNumberToNumberStrategy(ToNumberPolicy.DOUBLE).create(); +    private final File jsonFile; + +    /** +     * Creates a new Difficulty +     * @param newDir: The file path of the Difficulty +     * @throws IOException If there are any problems reading the metadata or leaderboard files +     */ +    public Difficulty(File newDir, Level level) throws IOException { +        thisDir = newDir; +        this.level = level; +        jsonFile = new File(thisDir, "metadata.json"); +        readMetadata(); +        notes = new Notes(new File(thisDir, "notes.txt"), this); // needs metadata first +        leaderboard = new Leaderboard(new File(thisDir, "leaderboard.json")); +    } + +    /** +     * Read in the data from metadata.json +     * @throws IOException If there are any problems loading the file. +     */ +    public void readMetadata() throws IOException { +        if (!jsonFile.exists()) { +            return; +        } +        Map<String, Object> data = jsonParser.fromJson(new FileReader(jsonFile), Map.class); + +        title = (String) data.getOrDefault("title", title); +        bpm = (Double) data.getOrDefault("bpm", bpm); +        endTime = (double) data.getOrDefault("endTime", endTime); +        if (endTime == 0) { +            int tmp = (int) (double) data.getOrDefault("numBeats", 0.0); +            if (tmp != 0) { +                endTime = Notes.beatToSecond(tmp, bpm); +            } +        } +        order = (int) (double) data.getOrDefault("priority", (double) order); +    } + +    /** +     * Checks the validity of the difficulty +     * <p> +     * A valid difficulty has at least one note +     * @return True if the difficulty is valid +     */ +    public boolean isValid() { +        return !notes.list.isEmpty(); +    } + +    /** +     * Writes metadata to json file +     * @throws IOException If there is a problem writing to the file +     */ +    public void writeMetadata() throws IOException { +        jsonFile.createNewFile(); +        Map<String, Object> data = jsonParser.fromJson(new FileReader(jsonFile), Map.class); // start with previous values +        data.put("title", title); +        data.put("endTime", endTime); +        data.put("priority", order); +        FileWriter fileWriter = new FileWriter(jsonFile); +        jsonParser.toJson(data, fileWriter); +        fileWriter.close(); +    } + +    @Override +    public int compareTo(Difficulty d) { +        return order - d.order; +    } +} diff --git a/src/main/java/net/sowgro/npehero/levelapi/Leaderboard.java b/src/main/java/net/sowgro/npehero/levelapi/Leaderboard.java new file mode 100644 index 0000000..bb1f30c --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/Leaderboard.java @@ -0,0 +1,81 @@ +package net.sowgro.npehero.levelapi; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Leaderboard { + +    public final ObservableList<LeaderboardEntry> entries = FXCollections.observableArrayList(); +    private final Gson json = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); +    private final File file; + +    public Leaderboard(File file) throws IOException{ +        this.file = file; +        read(); +    } + +    /** +     * Adds new leaderboardEntry to list and updates json file +     * @param name: The players name +     * @param score The players score +     * @throws IOException If there is a problem updating the leaderboard file. +     */ +    public void add(String name, int score) throws IOException { +        entries.add(new LeaderboardEntry(name, score, LocalDate.now().toString())); +        save(); +    } + +    /** +     * Writes leaderboard to json file +     * @throws IOException If there are problems writing to the file. +     */ +    public void save() throws IOException { +        file.createNewFile(); +        List<Map<String, Object>> data = json.fromJson(new FileReader(file), List.class); +        for (LeaderboardEntry cur : entries) { +            Map<String, Object> obj = new HashMap<>(); +            obj.put("name", cur.name); +            obj.put("score", cur.score); +            obj.put("date", cur.date); +            data.add(obj); +        } +        FileWriter fileWriter = new FileWriter(file); +        json.toJson(data, fileWriter); +        fileWriter.close(); +    } + +    /** +     * Reads in json leaderboard and assigns populates list with leaderboardEntries +     * @throws IOException If there are problems reading the file +     */ +    public void read() throws IOException { +        if (!file.exists()) { +            return; +        } +        List<Map<String, Object>> data = json.fromJson(new FileReader(file), List.class); +        if (data == null) { +            return; +        } +        for (Map<String, Object> cur: data) { +            String name = (String) cur.getOrDefault("name", null); +            int score = (int) (double) cur.getOrDefault("score", -1); +            String date = (String) cur.getOrDefault("date", null); +            if (name == null || score == -1 || date == null) { +                System.out.println("dbg: bad entry skipped"); +                continue; // discard invalid entries +            } +            entries.add(new LeaderboardEntry(name, score, date)); +        } +    } +} diff --git a/src/main/java/net/sowgro/npehero/levelapi/LeaderboardEntry.java b/src/main/java/net/sowgro/npehero/levelapi/LeaderboardEntry.java new file mode 100755 index 0000000..2b98a29 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/LeaderboardEntry.java @@ -0,0 +1,24 @@ +package net.sowgro.npehero.levelapi; + +/** + * Represents one players score in the leaderboard + */ +public class LeaderboardEntry +{ +    public final int score; +    public final String name; +    public final String date; + +    /** +     * Create a new LeaderboardEntry +     * @param name The name the player input after completing the level +     * @param score The score the player earned +     * @param date The date the player earned this score +     */ +    public LeaderboardEntry(String name, int score, String date) +    { +        this.name = name; +        this.score = score; +        this.date = date; +    } +} diff --git a/src/main/java/net/sowgro/npehero/levelapi/Level.java b/src/main/java/net/sowgro/npehero/levelapi/Level.java new file mode 100755 index 0000000..218779f --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/Level.java @@ -0,0 +1,152 @@ +package net.sowgro.npehero.levelapi; + +import java.io.*; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import javafx.scene.image.Image; +import javafx.scene.media.Media; +import javafx.scene.paint.Color; + +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Map; + +public class Level implements Comparable<Level>{ + +    public final File dir; + +    public String title = "Unnamed"; +    public String artist = "Unknown"; +    public String desc; +    public Color[] colors = {Color.RED,Color.BLUE,Color.GREEN,Color.PURPLE,Color.YELLOW}; +    public Image preview; +    public Image background; +    public Media song; + +    public Difficulties difficulties; + +    private final File jsonFile; +    private final Gson jsonParser = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); + +    /** +     * Creates a new level +     * @param newDir The path of the Level +     * @throws IOException If there is a problem reading the metadata file or loading the difficulties +     */ +    public Level(File newDir) throws IOException +    { +        dir = newDir; +        jsonFile = new File(dir, "metadata.json"); +        readFiles(); +        readMetadata(); +        difficulties = new Difficulties(this); +    } + +    /** +     * Check for a song file, background file and preview image file +     */ +    public void readFiles() { + +        File[] fileList = dir.listFiles(); +        if (fileList == null) { +            return; +        } +        for (File file : fileList) { +            String fileName = file.getName(); +            if (fileName.contains("song")) { +                song = new Media(file.toURI().toString()); +            } +            else if (fileName.contains("background")) { +                background = new Image(file.toURI().toString()); +            } +            else if (fileName.contains("preview")) { +                preview = new Image(file.toURI().toString()); +            } +        } + +    } + +    /** +     * Read in metadata file +     * @throws IOException If there is a problem reading the file +     */ +    public void readMetadata() throws IOException { +        if (!jsonFile.exists()) { +            return; +        } +        Map<String, Object> data = jsonParser.fromJson(new FileReader(jsonFile), Map.class); +        title = (String) data.getOrDefault("title", title); +        artist = (String) data.getOrDefault("artist", artist); +        desc = (String) data.getOrDefault("desc", desc); +        colors[0] = Color.web((String) data.getOrDefault("color1", colors[0].toString())); +        colors[1] = Color.web((String) data.getOrDefault("color2", colors[1].toString())); +        colors[2] = Color.web((String) data.getOrDefault("color3", colors[2].toString())); +        colors[3] = Color.web((String) data.getOrDefault("color4", colors[3].toString())); +        colors[4] = Color.web((String) data.getOrDefault("color5", colors[4].toString())); +    } + +    /** +     * Checks if the level is valid. +     * <p> +     * A valid level has a song file and 1 or more valid difficulties +     * @return True if the level is valid +     */ +    public boolean isValid() { +        if (song == null) { +            return false; +        } + +        if (difficulties.getValidList().isEmpty()) { +            return false; +        } +        return true; +    } + +    /** +     * Writes metadata to json file +     * @throws IOException If there is a problem writing to the file. +     */ +    public void writeMetadata() throws IOException { +        jsonFile.createNewFile(); +        Map<String, Object> data = jsonParser.fromJson(new FileReader(jsonFile), Map.class); +        data.put("title", title); +        data.put("artist", artist); +        data.put("desc", desc); +        data.put("color1",colors[0].toString()); +        data.put("color2",colors[1].toString()); +        data.put("color3",colors[2].toString()); +        data.put("color4",colors[3].toString()); +        data.put("color5",colors[4].toString()); +        FileWriter fileWriter = new FileWriter(jsonFile); +        jsonParser.toJson(data, fileWriter); +        fileWriter.close(); +    } + + +    /** +     * Copies a file into the level directory with the name provided. The extension will be inherited from the source file +     * @param source: the file to be copied +     * @param name: the new file name EXCLUDING the extension. +     * @throws IOException If there is a problem adding the file +     */ +    public void addFile(File source, String name) throws IOException { +        name = name + "." + getFileExtension(source); +        Files.copy(source.toPath(), new File(dir, name).toPath(), StandardCopyOption.REPLACE_EXISTING); +        readFiles(); +    } + +    @Override +    public int compareTo(Level other) { +        return title.compareTo(other.title); +    } + +    /** +     * Get the extension of a file. +     * @param file The file to return the extension of +     * @return The extension of the file in the format "*.ext" +     */ +    public String getFileExtension(File file) { +        return file.getName().substring(file.getName().lastIndexOf('.') + 1); +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/Levels.java b/src/main/java/net/sowgro/npehero/levelapi/Levels.java index ce720c6..84ffe51 100755 --- a/src/main/java/net/sowgro/npehero/main/Levels.java +++ b/src/main/java/net/sowgro/npehero/levelapi/Levels.java @@ -1,39 +1,48 @@ -package net.sowgro.npehero.main; +package net.sowgro.npehero.levelapi;  import java.io.File;  import java.io.FileNotFoundException;  import java.io.IOException; +import java.nio.file.FileAlreadyExistsException;  import java.nio.file.Files;  import java.nio.file.Path;  import java.util.Comparator; -import java.util.List; +import java.util.HashMap;  import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener;  import javafx.collections.ObservableList; -public class Levels -{ +/** + * Stores a list of all the levels + */ +public class Levels { + +    public static final ObservableList<Level> list = FXCollections.observableArrayList(); +    public static final HashMap<String, Exception> problems = new HashMap<>(); +      private static final File dir = new File("levels"); -    public static ObservableList<Level> list = FXCollections.observableArrayList();      /**       * Reads contents of the levels folder and creates a level form each subfolder +     * <p>       * All subfolders in the levels folder are assumed to be levels -     * @throws FileNotFoundException when the levels folder is not present +     * @throws FileNotFoundException If the levels folder is missing. +     * @throws IOException If there is a problem reading in the levels.       */ -    public static void readData() throws FileNotFoundException -    { +    public static void readData() throws IOException {          list.clear();          File[] fileList = dir.listFiles();          if (fileList == null) {              throw new FileNotFoundException();          } -        for (File file: fileList) -        { -            Level level = new Level(file); -            level.readData(); -            list.add(level); +        for (File file: fileList) { +            try { +                Level level = new Level(file); +                list.add(level); +            } catch (IOException e) { +                problems.put("", e); +                e.printStackTrace(); +            }          }          list.sort(Comparator.naturalOrder());      } @@ -43,9 +52,11 @@ public class Levels       * @param text: the name of the directory and default title       * @throws IOException if there was an error adding the level       */ -    public static void add(String text) throws IOException -    { -        File levelDir = new File(dir,text); +    public static void add(String text) throws IOException { +        File levelDir = new File(dir, text.toLowerCase().replaceAll("\\W+", "-")); +        if (levelDir.exists()) { +            throw new FileAlreadyExistsException(levelDir.getName()); +        }          if (levelDir.mkdirs()) {              Level temp = new Level(levelDir);              temp.title = text; @@ -59,23 +70,21 @@ public class Levels      /**       * Removes level from the filesystem then reloads this levelController       * @param level: the level to be removed -     * @throws IOException if there was an error deleting the level +     * @throws IOException If there is a problem deleting the level       */ -    public static void remove(Level level) throws IOException -    { +    public static void remove(Level level) throws IOException {          File hold = level.dir; -        list.remove(level); - -        // delete files recursively -        // TODO clean this up          Files.walk(hold.toPath())              .sorted(Comparator.reverseOrder())              .map(Path::toFile)              .forEach(File::delete); -          list.remove(level);      } +    /** +     * Gets a list of only the valid levels. +     * @return A list of the valid levels. +     */      public static ObservableList<Level> getValidList() {          ObservableList<Level> validList = FXCollections.observableArrayList();          for (Level level : list) { diff --git a/src/main/java/net/sowgro/npehero/main/Note.java b/src/main/java/net/sowgro/npehero/levelapi/Note.java index c2c9015..ab93885 100644 --- a/src/main/java/net/sowgro/npehero/main/Note.java +++ b/src/main/java/net/sowgro/npehero/levelapi/Note.java @@ -1,17 +1,23 @@ -package net.sowgro.npehero.main; +package net.sowgro.npehero.levelapi;  import javafx.beans.property.DoubleProperty;  import javafx.beans.property.SimpleDoubleProperty;  /**   * A note represents a moment in the song when the player should hit a key + * <p>   * The key corresponding to the lane the note is in should be pressed   */  public class Note { -    public DoubleProperty time = new SimpleDoubleProperty(); -    public int lane; +    public final DoubleProperty time = new SimpleDoubleProperty(); +    public final int lane; +    /** +     * Creates a new note +     * @param time The time the player should hit the note. +     * @param lane The lane the note belongs to. +     */      public Note(double time, int lane) {          this.time.set(time);          this.lane = lane; diff --git a/src/main/java/net/sowgro/npehero/levelapi/Notes.java b/src/main/java/net/sowgro/npehero/levelapi/Notes.java new file mode 100644 index 0000000..1df0248 --- /dev/null +++ b/src/main/java/net/sowgro/npehero/levelapi/Notes.java @@ -0,0 +1,107 @@ +package net.sowgro.npehero.levelapi; + +import javafx.beans.property.ListProperty; +import javafx.beans.property.SimpleListProperty; +import javafx.collections.FXCollections; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Scanner; + +/** + * Stores all the notes for a difficulty. + */ +public class Notes { + +    private final File file; +    private final Difficulty diff; + +    public ListProperty<Note> list = new SimpleListProperty<>(FXCollections.observableArrayList()); + +    /** +     * Create a new Notes object +     * @param file The notes.txt file +     * @param diff The difficulty these notes belong to +     * @throws IOException If there is a problem reading the notes file +     */ +    public Notes(File file, Difficulty diff) throws IOException { +        this.file = file; +        this.diff = diff; +        readFile(); +    } + +    /** +     * Read notes.txt and add the notes to the list +     * @throws IOException if there is a problem reading the file. +     */ +    public void readFile() throws IOException { +        if (!file.exists()) { +            return; +        } +        Scanner scan = new Scanner(file); +        while (scan.hasNext()) { +            String input = scan.next(); +            int lane = switch (input.charAt(0)) { +                case 'd' -> 0; +                case 'f' -> 1; +                case 's' -> 2; +                case 'j' -> 3; +                case 'k' -> 4; +                default -> -1; +            }; +            if (lane == -1) { +                continue; +            } +            double time = Double.parseDouble(input.substring(1)); + +            if (diff.bpm != 0.0) { +                time = beatToSecond(time, diff.bpm); +            } +            list.add(new Note(time, lane)); +        } +    } + +    /** +     * Writes the notes to notes.txt +     * @throws IOException If there is a problem writing to the file. +     */ +    public void writeFile() throws IOException{ +        var _ = file.createNewFile(); +        PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8); +        for (Note note : list) { +            Character lane = switch (note.lane) { +                case 0 -> 'd'; +                case 1 -> 'f'; +                case 2 -> 's'; +                case 3 -> 'j'; +                case 4 -> 'k'; +                default -> null; +            }; +            if (lane == null) { +                continue; +            } +            writer.println(lane + "" + note.time.get()); +        } +        writer.close(); +    } + +    /** +     * Converts a beat to a second using the levels bpm +     * @param beat The beat to convert to seconds +     * @param bpm The beats per minute to use for conversion +     * @return The time in seconds the beat was at +     */ +    public static double beatToSecond(double beat, double bpm) { +        return beat/(bpm/60); +    } + +    /** +     * Performs a deep copy of the notes list. +     * @return a new list of notes with the same notes. +     */ +    public ListProperty<Note> deepCopyList() { +        ListProperty<Note> ret = new SimpleListProperty<>(FXCollections.observableArrayList()); +        list.forEach(e -> ret.add(new Note(e))); +        return ret; +    } +} diff --git a/src/main/java/net/sowgro/npehero/main/Control.java b/src/main/java/net/sowgro/npehero/main/Control.java index 6e608ec..ece88ac 100644 --- a/src/main/java/net/sowgro/npehero/main/Control.java +++ b/src/main/java/net/sowgro/npehero/main/Control.java @@ -1,5 +1,7 @@  package net.sowgro.npehero.main; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder;  import javafx.beans.property.ObjectProperty;  import javafx.beans.property.SimpleObjectProperty;  import javafx.scene.input.KeyCode; @@ -36,7 +38,8 @@ public enum Control {                  entry("Legacy Editor", List.of(LEGACY_PRINT, LEGACY_STOP))              ); -    private static final JSONFile jsonFile = new JSONFile(new File("controls.json")); +    private static final File file = new File("controls.json"); +    private static final Gson json = new GsonBuilder().serializeNulls().setPrettyPrinting().create();      Control(String label, KeyCode key) {          this.label = label; @@ -80,19 +83,21 @@ public enum Control {      }      public static void writeToFile() throws IOException { +        Map<String, Object> data = new HashMap<>();          for (Control control : Control.values()) { -            jsonFile.set(control.toString(), control.getKey().toString()); +            data.put(control.toString(), control.getKey().toString());          } -        jsonFile.write(); +        FileWriter fileWriter = new FileWriter(file); +        json.toJson(data, fileWriter); +        fileWriter.close();      }      public static void readFromFile() throws Exception { -        jsonFile.read(); +        Map<String, Object> data = json.fromJson(new FileReader(file), Map.class);          for (Control control : Control.values()) { -            if (jsonFile.containsKey(control.toString())) { -                control.setKey(KeyCode.valueOf(jsonFile.getString(control.toString(), null))); +            if (data.containsKey(control.toString())) { +                control.setKey(KeyCode.valueOf((String) data.getOrDefault(control.toString(), null)));              }          }      } -  } diff --git a/src/main/java/net/sowgro/npehero/main/Difficulties.java b/src/main/java/net/sowgro/npehero/main/Difficulties.java deleted file mode 100644 index 9549a5a..0000000 --- a/src/main/java/net/sowgro/npehero/main/Difficulties.java +++ /dev/null @@ -1,86 +0,0 @@ -package net.sowgro.npehero.main; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.List; - -public class Difficulties { - -    public ObservableList<Difficulty> list = FXCollections.observableArrayList(); -    Level level; - -    public Difficulties(Level level) { -        this.level = level; -    } - -    public void read() { -        list.clear(); -        File[] fileList = level.dir.listFiles(); -        if (fileList == null) { -            return; -        } -        for(File cur: fileList) //iterates through all files/folders in /levels/LEVEL -        { -            if (cur.isDirectory()) //all subfolders within a level folder are difficulties -            { -                Difficulty diff = new Difficulty(cur,level); -                list.add(diff); -            } -        } -        list.sort(Comparator.naturalOrder()); -    } - -    /** -     * Removes the difficaulty from the filesystem then reloads the level -     * @param diff: the difficulty to be removed -     */ -    public void remove(Difficulty diff) throws IOException -    { -        File hold = diff.thisDir; -        Files.walk(hold.toPath()) -                .sorted(Comparator.reverseOrder()) -                .map(Path::toFile) -                .forEach(File::delete); -        list.remove(diff); -    } - -    /** -     * Adds a difficulty by creating a directory and required files -     * @param text: the name of the directory and default title -     */ -    public void add(String text) throws IOException { -        File diffDir = new File(level.dir, text); -        if (diffDir.mkdirs()) { -            Difficulty temp = new Difficulty(diffDir, level); -            temp.title = text; -            list.add(temp); -            list.sort(Comparator.naturalOrder()); -        } -        else { -            throw new IOException(); -        } -    } - -    public void saveOrder() { -        for (Difficulty d : list) { -            d.order = list.indexOf(d); -            d.write(); -        } -    } - -    public List<Difficulty> getValidList() { -        ObservableList<Difficulty> validList = FXCollections.observableArrayList(); -        for (Difficulty difficulty : list) { -            if (difficulty.isValid()) { -                validList.add(difficulty); -            } -        } -        return validList; -    } -} diff --git a/src/main/java/net/sowgro/npehero/main/Difficulty.java b/src/main/java/net/sowgro/npehero/main/Difficulty.java deleted file mode 100755 index 0308fac..0000000 --- a/src/main/java/net/sowgro/npehero/main/Difficulty.java +++ /dev/null @@ -1,87 +0,0 @@ -package net.sowgro.npehero.main; - -import javafx.scene.media.Media; - -import java.io.File; -import java.io.IOException; - -public class Difficulty implements Comparable<Difficulty> -{ -    public File thisDir; -    public Level level; -    private final JSONFile metadataYaml; - -    public String title = "Unnamed"; -    public Double bpm = 0.0; -    public double endTime = 0; -    public int order = 0; - -    public Leaderboard leaderboard; -    public Notes notes; - -    /** -     * 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; -        metadataYaml = new JSONFile(new File(thisDir, "metadata.json")); -        notes = new Notes(new File(thisDir, "notes.txt"), this); -        leaderboard = new Leaderboard(new File(thisDir, "leaderboard.json")); - -        readData(); -    } - -    public void readData() { -        try { -            metadataYaml.read(); -        } -        catch (Exception e) { -            System.err.println(level.title + "/" + title + ": Failed to read metadata.json"); -        } - -        title = metadataYaml.getString("title", title); -        bpm = metadataYaml.getDouble("bpm", bpm); -        endTime = metadataYaml.getDouble("endTime", endTime); -        if (endTime == 0) { -            int tmp = metadataYaml.getInt("numBeats", 0); -            if (tmp != 0) { -                endTime = beatToSecond(tmp); -            } -        } -        order = metadataYaml.getInt("priority", order); - -    } - -    public boolean isValid() { -        return !notes.list.isEmpty(); -    } - -    /** -     * Writes metadata to json file -     */ -    public void write() { -        metadataYaml.set("title", title); -        metadataYaml.set("endTime", endTime); -        metadataYaml.set("priority", order); - -        try { -            metadataYaml.write(); -        }  -        catch (IOException e) { -            System.err.println(level.title + "/" + title + ": Failed to write metadata.json"); -        } -    } - -    @Override -    public int compareTo(Difficulty d) { -        return order - d.order; -    } - -    private double beatToSecond(double beat) { -        return beat/(bpm/60); -    } - - -} diff --git a/src/main/java/net/sowgro/npehero/main/JSONFile.java b/src/main/java/net/sowgro/npehero/main/JSONFile.java deleted file mode 100644 index d06ca1f..0000000 --- a/src/main/java/net/sowgro/npehero/main/JSONFile.java +++ /dev/null @@ -1,108 +0,0 @@ -package net.sowgro.npehero.main; - - - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import java.io.*; - -/** - * An ergonomic JSON API wrapper inspired by the Bukkit YAML API - */ -public class JSONFile { - -    private final File file; -    ObjectMapper objectMapper = new ObjectMapper(); -    ObjectNode writeNode = objectMapper.createObjectNode(); -    JsonNode readNode; - -    public JSONFile(File file) { -        try { -            file.createNewFile(); -        } catch (IOException e) { -            throw new RuntimeException(e); -        } -        this.file = file; -    } - -    public String getString(String key, String def) { -        if (!readNode.has(key)) { -            return def; -        } -        return readNode.get(key).asText(); -    } - -    public int getInt(String key, int def) { -        if (!readNode.has(key)) { -            return def; -        } -        try { -            return Integer.parseInt(readNode.get(key).asText()); -        } -        catch (NumberFormatException e) { -            return def; -        } -    } - -    public double getDouble(String key, double def) { -        if (!readNode.has(key)) { -            return def; -        } -        try { -            return Double.parseDouble(readNode.get(key).asText()); -        } -        catch (NumberFormatException e) { -            return def; -        } -    } - -    public boolean getBoolean(String key, boolean def) { -        if (!readNode.has(key)) { -            return def; -        } -        try { -            return Boolean.parseBoolean(readNode.get(key).asText()); -        } -        catch (NumberFormatException e) { -            return def; -        } -    } - -    public void set(String key, String value) { -        if (value == null) { -            return; -        } -        writeNode.put(key, value); -    } - -    public void set(String key, int value) { -        writeNode.put(key, value); -    } - -    public void set(String key, double value) { -        writeNode.put(key, value); -    } - -    public void set(String key, boolean value) { -        writeNode.put(key, value); -    } - -    public boolean containsKey(String key) { -        return writeNode.has(key); -    } - -    public void read() throws Exception { -        if (file.length() == 0) { -            readNode = objectMapper.createObjectNode(); -            return; -        } -        readNode = objectMapper.readTree(file); -    } - -    public void write() throws IOException { -        objectMapper.writeValue(file, writeNode); -    } - -} diff --git a/src/main/java/net/sowgro/npehero/main/Leaderboard.java b/src/main/java/net/sowgro/npehero/main/Leaderboard.java deleted file mode 100644 index eb4e5f5..0000000 --- a/src/main/java/net/sowgro/npehero/main/Leaderboard.java +++ /dev/null @@ -1,94 +0,0 @@ -package net.sowgro.npehero.main; - -import com.fasterxml.jackson.databind.JsonNode; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.time.LocalDate; - -public class Leaderboard { - -    public ObservableList<LeaderboardEntry> entries = FXCollections.observableArrayList(); -//    private final JSONFile jsonFile; -    private File file; - -    public Leaderboard(File file) { -//        jsonFile = new JSONFile(file); -        this.file = file; -    } - -    /** -     * Adds new leaderboardEntry to list and updates json file -     * @param name: The players name -     * @param score The players score -     */ -    public void add(String name, int score) { -        new LeaderboardEntry(name, score, ""+ LocalDate.now()); -    } - -    /** -     * Writes leaderboard to json file -     */ -//    public void save() -//    { -//        FileWriter fileWriter; -//        try -//        { -//            fileWriter = new FileWriter(file); -//            //write the settings JSONObject instance to the file -//            JSONArray jsonArray = new JSONArray(); -//            for (LeaderboardEntry cur: entries) -//            { -//                JsonNode obj = new ; -//                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(); -//        } -//    } -// -//    /** -//     * Reads in json leaderboard and assigns populates list with leaderboardEntries -//     */ -//    public boolean parseLeaderboard() -//    { -//        boolean isValid = true; -//        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"); -//                entries.add(new LeaderboardEntry(name, score, date)); -//            } -//        } -//        catch (Exception e) -//        { -//            isValid = false; -//            e.printStackTrace(); -//        } -//        return isValid; -//    } - - -} diff --git a/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java b/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java deleted file mode 100755 index 18e657c..0000000 --- a/src/main/java/net/sowgro/npehero/main/LeaderboardEntry.java +++ /dev/null @@ -1,28 +0,0 @@ -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 deleted file mode 100755 index 6f66893..0000000 --- a/src/main/java/net/sowgro/npehero/main/Level.java +++ /dev/null @@ -1,137 +0,0 @@ -package net.sowgro.npehero.main; - -import java.io.File; - -import javafx.scene.image.Image; -import javafx.scene.media.Media; -import javafx.scene.paint.Color; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -public class Level implements Comparable<Level>{ - -    public File dir; - -    public String title = "Unnamed"; -    public String artist = "Unknown"; -    public String desc; -    public Color[] colors = {Color.RED,Color.BLUE,Color.GREEN,Color.PURPLE,Color.YELLOW};//optional, have default colors - -    public Image preview; //optional -    public Image background; //optional -    public Media song; - -    public Difficulties difficulties; - -    private JSONFile metadataJson; - -    /** -     * Creates a new level -     * @param newDir: The path of the Level -     */ -    public Level(File newDir) -    { -        dir = newDir; -        metadataJson = new JSONFile(new File(dir, "metadata.json")); -        difficulties = new Difficulties(this); -        difficulties.read(); -        readData(); -    } - -    /** -     * Check for a song file, background file and preview image file -     * Parse metadata.json -     */ -    public void readData() { - -        var fileList = dir.listFiles(); -        if (fileList == null) { -            return; -        } -        for (File file : fileList) { -            String fileName = file.getName(); -            if (fileName.contains("song")) { -                song = new Media(file.toURI().toString()); -            } -            else if (fileName.contains("background")) { -                background = new Image(file.toURI().toString()); -            } -            else if (fileName.contains("preview")) { -                preview = new Image(file.toURI().toString()); -            } -        } - -        try { -            metadataJson.read(); -        } -        catch (Exception e) { -            // TODO -        } -        title = metadataJson.getString("title", title); -        artist = metadataJson.getString("artist", artist); -        desc = metadataJson.getString("desc", desc); -        colors[0] = Color.web(metadataJson.getString("color1", colors[0].toString())); -        colors[1] = Color.web(metadataJson.getString("color2", colors[1].toString())); -        colors[2] = Color.web(metadataJson.getString("color3", colors[2].toString())); -        colors[3] = Color.web(metadataJson.getString("color4", colors[3].toString())); -        colors[4] = Color.web(metadataJson.getString("color5", colors[4].toString())); -    } - -    /** -     * Checks if the level is valid. -     * A valid level has a song file and 1 or more valid difficulties -     * @return true if the level is valid -     */ -    public boolean isValid() { -        if (song == null) { -//            System.out.println(dir +" is missing song file"); -            return false; -        } - -        if (difficulties.getValidList().isEmpty()) { -//            System.out.println(dir +" contains no valid difficulties"); -            return false; -        } -        return true; -    } - -    /** -     * Writes metadata to json file -     */ -    public void writeMetadata() -    { -        metadataJson.set("title", title); -        metadataJson.set("artist", artist); -        metadataJson.set("desc", desc); -        metadataJson.set("color1",colors[0].toString()); -        metadataJson.set("color2",colors[1].toString()); -        metadataJson.set("color3",colors[2].toString()); -        metadataJson.set("color4",colors[3].toString()); -        metadataJson.set("color5",colors[4].toString()); -        try { -            metadataJson.write(); -        } -        catch (IOException e) { -            // TODO -        } -    } - - -    /** -     * Copies a file into the level directory -     * @param newFile: the file to be copied -     * @param name: the new file name -     * @throws IOException if there was an error adding the file -     */ -    public void addFile(File newFile, String name) throws IOException { -        Files.copy(newFile.toPath(), new File(dir, name).toPath(), StandardCopyOption.REPLACE_EXISTING); -        readData(); -    } - -    @Override -    public int compareTo(Level other) { -        return title.compareTo(other.title); -    } -} diff --git a/src/main/java/net/sowgro/npehero/main/Notes.java b/src/main/java/net/sowgro/npehero/main/Notes.java deleted file mode 100644 index 62a602e..0000000 --- a/src/main/java/net/sowgro/npehero/main/Notes.java +++ /dev/null @@ -1,100 +0,0 @@ -package net.sowgro.npehero.main; - -import javafx.beans.property.ListProperty; -import javafx.beans.property.SimpleListProperty; -import javafx.collections.FXCollections; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.util.Scanner; - -public class Notes { -    private File file; -    private Difficulty diff; - -    public ListProperty<Note> list = new SimpleListProperty<>(FXCollections.observableArrayList()); - -    public Notes(File f, Difficulty diff) { -        try { -            f.createNewFile(); -        } catch (IOException e) { -            throw new RuntimeException(e); -        } -        this.file = f; -        this.diff = diff; -        readFile(); -    } - -    public void readFile() { -        Scanner scan = null;	//file reader for reading in the notes from a notes.txt file -        try { -            scan = new Scanner(file); -        } -        catch (FileNotFoundException e) { -            // TODO handle error -            throw new RuntimeException(e); -        } - -        while (scan.hasNext()) { -            String input = scan.next(); - -            int lane = switch (input.charAt(0)) { -                case 'd' -> 0; -                case 'f' -> 1; -                case 's' -> 2; -                case 'j' -> 3; -                case 'k' -> 4; -                default  -> -1; -            }; - -            if (lane == -1) { -                // TODO handle error -                continue; -            } - -            double time = Double.parseDouble(input.substring(1)); -            if (diff.bpm != 0.0) { -                time = beatToSecond(time, diff.bpm); -            } -            list.add(new Note(time, lane)); -        } -    } - -    public void writeFile() { -        try (PrintWriter writer = new PrintWriter(diff.notes.getFile(), StandardCharsets.UTF_8)) { -            for (Note note : list) { -                char lane = switch (note.lane) { -                    case 0 -> 'd'; -                    case 1 -> 'f'; -                    case 2 -> 's'; -                    case 3 -> 'j'; -                    case 4 -> 'k'; -                    default -> 'e'; -                }; - -                if (lane == 'e') { -                    // TODO handle error -                } - -                writer.println(lane + "" + note.time.get()); -            } -        } -        catch (IOException e) { -            throw new RuntimeException(e); -        } -    } - -    private double beatToSecond(double beat, double bpm) { -        return beat/(bpm/60); -    } - -    public File getFile() { -        return file; -    } - -    public ListProperty<Note> deepCopyList() { -        ListProperty<Note> ret = new SimpleListProperty<>(FXCollections.observableArrayList()); -        list.forEach(e -> ret.add(new Note(e))); -        return ret; -    } -} diff --git a/src/main/java/net/sowgro/npehero/main/Settings.java b/src/main/java/net/sowgro/npehero/main/Settings.java index 8cba8f7..43dce5a 100755 --- a/src/main/java/net/sowgro/npehero/main/Settings.java +++ b/src/main/java/net/sowgro/npehero/main/Settings.java @@ -1,8 +1,14 @@  package net.sowgro.npehero.main;  import java.io.File; +import java.io.FileReader; +import java.io.FileWriter;  import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder;  import javafx.beans.property.SimpleBooleanProperty;  import javafx.beans.property.SimpleDoubleProperty; @@ -12,25 +18,29 @@ public class Settings  	public static SimpleDoubleProperty musicVol = new SimpleDoubleProperty(1);  	public static SimpleBooleanProperty enableMenuMusic = new SimpleBooleanProperty(true); -	private static final JSONFile jsonFile = new JSONFile(new File("settings.json")); +	private static final Gson jsonParser = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); +	private static final File jsonFile = new File("settings.json");  	/**  	 * Reads json data from settings.json  	 */  	public static void read() throws Exception { -        jsonFile.read(); -        effectsVol.set(jsonFile.getDouble("effectsVol", 1)); -		musicVol.set(jsonFile.getDouble("musicVol", 1)); -		enableMenuMusic.set(jsonFile.getBoolean("enableMenuMusic", true)); +		Map<String, Object> data = jsonParser.fromJson(new FileReader(jsonFile), Map.class); +        effectsVol.set((Double) data.getOrDefault("effectsVol", 1.0)); +		musicVol.set((Double) data.getOrDefault("musicVol", 1.0)); +		enableMenuMusic.set((Boolean) data.getOrDefault("enableMenuMusic", true));  	}  	/**  	 * Writes json data to settings.json  	 */  	public static void save() throws IOException { -		jsonFile.set("effectsVol", effectsVol.get()); -		jsonFile.set("musicVol", musicVol.get()); -		jsonFile.set("enableMenuMusic", enableMenuMusic.get()); -		jsonFile.write(); +		Map<String, Object> data = new HashMap<>(); +		data.put("effectsVol", effectsVol.get()); +		data.put("musicVol", musicVol.get()); +		data.put("enableMenuMusic", enableMenuMusic.get()); +		FileWriter fileWriter = new FileWriter(jsonFile); +		jsonParser.toJson(data, fileWriter); +		fileWriter.close();  	}  } | 
