Merge branch 'master' of github.com:badvision/lawless-legends

This commit is contained in:
Martin Haye 2021-05-13 09:26:31 -07:00
commit 5651578bbd
7 changed files with 206 additions and 62 deletions

View File

@ -10,26 +10,12 @@ import jace.core.Card;
import jace.core.Computer; import jace.core.Computer;
import jace.core.Motherboard; import jace.core.Motherboard;
import jace.core.Utility; import jace.core.Utility;
import jace.lawless.LawlessComputer;
import jace.lawless.LawlessHacks;
import jace.library.MediaCache; import jace.library.MediaCache;
import jace.library.MediaConsumer; import jace.library.MediaConsumer;
import jace.library.MediaConsumerParent; import jace.library.MediaConsumerParent;
import jace.library.MediaEntry; import jace.library.MediaEntry;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.FadeTransition; import javafx.animation.FadeTransition;
import javafx.animation.KeyFrame; import javafx.animation.KeyFrame;
import javafx.animation.Timeline; import javafx.animation.Timeline;
@ -44,6 +30,7 @@ import javafx.geometry.Insets;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.effect.DropShadow; import javafx.scene.effect.DropShadow;
@ -52,18 +39,21 @@ import javafx.scene.input.DragEvent;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode; import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.*;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* *
* @author blurry * @author blurry
@ -97,6 +87,9 @@ public class JaceUIController {
@FXML @FXML
private Button menuButton; private Button menuButton;
@FXML
private ComboBox musicSelection;
Computer computer; Computer computer;
private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false); private final BooleanProperty aspectRatioCorrectionEnabled = new SimpleBooleanProperty(false);
@ -166,6 +159,12 @@ public class JaceUIController {
} }
private void hideControlOverlay(MouseEvent evt) { private void hideControlOverlay(MouseEvent evt) {
if (evt == null || evt.getSource() != null && (
evt.getSource() == musicSelection ||
(evt.getSource() == rootPane && musicSelection.isFocused())
)) {
return;
}
if (menuButtonPane.isVisible()) { if (menuButtonPane.isVisible()) {
FadeTransition ft1 = new FadeTransition(Duration.millis(500), menuButtonPane); FadeTransition ft1 = new FadeTransition(Duration.millis(500), menuButtonPane);
ft1.setFromValue(1.0); ft1.setFromValue(1.0);
@ -237,6 +236,9 @@ public class JaceUIController {
// Kind of redundant but make sure speed is properly set as if the user did it // Kind of redundant but make sure speed is properly set as if the user did it
setSpeed(Emulator.logic.speedSetting); setSpeed(Emulator.logic.speedSetting);
}); });
musicSelection.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
((LawlessHacks) ((LawlessComputer) computer).activeCheatEngine).changeMusicScore(String.valueOf(newValue));
});
} }
private void connectButtons(Node n) { private void connectButtons(Node n) {

View File

@ -34,7 +34,7 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
* *
* @author blurry * @author blurry
*/ */
public class LawlessLegends extends Application { public class LawlessLegends extends Application {

View File

@ -20,25 +20,16 @@ package jace.apple2e;
import jace.LawlessLegends; import jace.LawlessLegends;
import jace.config.ConfigurableField; import jace.config.ConfigurableField;
import jace.core.Computer; import jace.core.*;
import jace.core.Device; import javafx.stage.FileChooser;
import jace.core.Motherboard;
import jace.core.RAMEvent; import javax.sound.sampled.LineUnavailableException;
import jace.core.RAMListener; import javax.sound.sampled.SourceDataLine;
import jace.core.SoundGeneratorDevice; import java.io.*;
import jace.core.SoundMixer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javafx.stage.FileChooser;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
/** /**
* Apple // Speaker Emulation Created on May 9, 2007, 9:55 PM * Apple // Speaker Emulation Created on May 9, 2007, 9:55 PM
@ -205,7 +196,9 @@ public class Speaker extends SoundGeneratorDevice {
bufferPos = 0; bufferPos = 0;
} }
secondaryBuffer = buffer; secondaryBuffer = buffer;
sdl.write(buffer, 0, len); if (sdl != null && buffer != null) {
sdl.write(buffer, 0, len);
}
} }
/** /**

View File

@ -8,8 +8,16 @@ import javafx.beans.property.DoubleProperty;
import javafx.scene.media.Media; import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer; import javafx.scene.media.MediaPlayer;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* Hacks that affect lawless legends gameplay * Hacks that affect lawless legends gameplay
@ -23,6 +31,8 @@ public class LawlessHacks extends Cheats {
public LawlessHacks(Computer computer) { public LawlessHacks(Computer computer) {
super(computer); super(computer);
readScores();
currentScore = SCORE_ORCHESTRAL;
} }
@Override @Override
@ -64,13 +74,15 @@ public class LawlessHacks extends Cheats {
} }
int currentSong; int currentSong;
boolean repeatSong = false;
Thread playbackEffect; Thread playbackEffect;
MediaPlayer currentSongPlayer; MediaPlayer currentSongPlayer;
MediaPlayer currentSfxPlayer; MediaPlayer currentSfxPlayer;
private void playSound(int soundNumber) { private void playSound(int soundNumber) {
boolean isMusic = soundNumber >= 0; boolean isMusic = soundNumber >= 0;
int track = soundNumber & 0x07f; int track = soundNumber & 0x03f;
repeatSong = (soundNumber & 0x040) > 0;
if (track == 0) { if (track == 0) {
if (isMusic) { if (isMusic) {
// System.out.println("Stop music"); // System.out.println("Stop music");
@ -88,8 +100,20 @@ public class LawlessHacks extends Cheats {
} }
} }
private Media getAudioTrack(String file) { private Media getAudioTrack(int number) {
String pathStr = "jace/data/sound/" + file; Map<Integer, String> score = scores.get(currentScore);
if (score == null) {
return null;
}
String filename = score.get(number);
if (filename == null) {
score = scores.get("common");
if (score == null || !score.containsKey(number)) {
return null;
}
filename = score.get(number);
}
String pathStr = "jace/data/sound/" + filename;
// System.out.println("looking in "+pathStr); // System.out.println("looking in "+pathStr);
URL path = getClass().getClassLoader().getResource(pathStr); URL path = getClass().getClassLoader().getResource(pathStr);
if (path == null) { if (path == null) {
@ -150,15 +174,15 @@ public class LawlessHacks extends Cheats {
int FADE_SPEED = 100; // 100ms per 5%, or 2 second duration int FADE_SPEED = 100; // 100ms per 5%, or 2 second duration
private void startNewSong(int track) { private void startNewSong(int track) {
if (track != currentSong || currentSongPlayer == null) { if (track != currentSong || currentSongPlayer == null || isMusicEnabled()) {
// If the same song is already playing don't restart it // If the same song is already playing don't restart it
Media song = getAudioTrack("BGM-" + track + ".mp3"); Media song = getAudioTrack(track);
if (song == null) { if (song == null) {
System.out.println("Unable to start song " + track + "; File not found"); System.out.println("Unable to start song " + track + "; File not found");
return; return;
} }
currentSongPlayer = new MediaPlayer(song); currentSongPlayer = new MediaPlayer(song);
currentSongPlayer.setCycleCount(MediaPlayer.INDEFINITE); currentSongPlayer.setCycleCount(repeatSong ? MediaPlayer.INDEFINITE : 1);
currentSongPlayer.setVolume(0.0); currentSongPlayer.setVolume(0.0);
currentSongPlayer.play(); currentSongPlayer.play();
currentSong = track; currentSong = track;
@ -185,12 +209,16 @@ public class LawlessHacks extends Cheats {
private void stopMusic() { private void stopMusic() {
stopSongEffect(); stopSongEffect();
fadeOutSong(null); fadeOutSong(()->{
if (!repeatSong) {
currentSong = 0;
}
});
} }
private void playSfx(int track) { private void playSfx(int track) {
new Thread(() -> { new Thread(() -> {
Media sfx = getAudioTrack("SFX-" + track + ".mp3"); Media sfx = getAudioTrack(track + 128);
if (sfx == null) { if (sfx == null) {
System.out.println("Unable to start SFX " + track + "; File not found"); System.out.println("Unable to start SFX " + track + "; File not found");
return; return;
@ -208,4 +236,63 @@ public class LawlessHacks extends Cheats {
} }
} }
public static final String SCORE_NONE = "none";
public static final String SCORE_COMMON = "common";
public static final String SCORE_ORCHESTRAL = "orchestral";
public static final String SCORE_CHIPTUNE = "chiptune";
private String currentScore = SCORE_COMMON;
public void changeMusicScore(String score) {
if (currentScore.equalsIgnoreCase(score)) {
return;
}
boolean wasStoppedPreviously = !isMusicEnabled();
currentScore = score.toLowerCase(Locale.ROOT);
if (currentScore.equalsIgnoreCase(SCORE_NONE)) {
stopMusic();
currentSong = -1;
} else if ((currentSongPlayer != null || wasStoppedPreviously) && currentSong > 0) {
int currentSongTemp = currentSong;
currentSong = Integer.MAX_VALUE;
startNewSong(currentSongTemp);
}
}
public boolean isMusicEnabled() {
return currentScore != null && !currentScore.equalsIgnoreCase(SCORE_NONE);
}
Pattern COMMENT = Pattern.compile("\\s*[-#;']+.*");
Pattern LABEL = Pattern.compile("[A-Za-z\\s\\-_]+");
Pattern ENTRY = Pattern.compile("([0-9]+)\\s+(.*)");
private Map<String, Map<Integer, String>> scores = new HashMap<>();
private void readScores() {
InputStream data = getClass().getClassLoader().getResourceAsStream("jace/data/sound/scores.txt");
readScores(data);
}
private void readScores(InputStream data) {
BufferedReader reader = new BufferedReader(new InputStreamReader(data));
reader.lines().forEach(line -> {
if (COMMENT.matcher(line).matches() || line.trim().isEmpty()) {
// System.out.println("Ignoring: "+line);
return;
} else if (LABEL.matcher(line).matches()) {
currentScore = line.toLowerCase(Locale.ROOT);
scores.put(currentScore, new HashMap<>());
// System.out.println("Score: "+ currentScore);
} else {
Matcher m = ENTRY.matcher(line);
if (m.matches()) {
int num = Integer.parseInt(m.group(1));
String file = m.group(2);
scores.get(currentScore).put(num, file);
// System.out.println("Score: " + currentScore + "; Song: " + num + "; " + file);
} else {
// System.out.println("Couldn't parse: " + line);
}
}
});
}
} }

View File

@ -1,17 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.collections.FXCollections?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.*?>
<?import javafx.scene.control.Slider?> <?import javafx.scene.image.*?>
<?import javafx.scene.image.Image?> <?import javafx.scene.layout.*?>
<?import javafx.scene.image.ImageView?> <?import java.lang.String?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.TilePane?>
<AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" style="-fx-background-color: black;" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController"> <AnchorPane id="AnchorPane" fx:id="rootPane" prefHeight="384.0" prefWidth="560.0" style="-fx-background-color: black;" stylesheets="@../styles/style.css" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jace.JaceUIController">
<children> <children>
<StackPane fx:id="stackPane" prefHeight="384.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <StackPane fx:id="stackPane" prefHeight="384.0" prefWidth="560.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
@ -83,8 +77,22 @@
</Button> </Button>
</children> </children>
</TilePane> </TilePane>
<!-- <TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS"> <TilePane alignment="TOP_RIGHT" hgap="5.0" vgap="5.0" HBox.hgrow="ALWAYS">
<children> <children>
<Label styleClass="musicLabel">Music:</Label>
<ComboBox fx:id="musicSelection">
<items>
<FXCollections fx:factory="observableArrayList">
<String fx:value="Orchestral" />
<String fx:value="Chiptune" />
<String fx:value="None" />
</FXCollections>
</items>
<value>
<String fx:value="Orchestral"/>
</value>
</ComboBox>
<!--
<Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="IDE"> <Button contentDisplay="TOP" mnemonicParsing="false" styleClass="uiActionButton" text="IDE">
<graphic> <graphic>
<ImageView> <ImageView>
@ -112,8 +120,9 @@
</ImageView> </ImageView>
</graphic> </graphic>
</Button> </Button>
-->
</children> </children>
</TilePane>--> </TilePane>
</children> </children>
</HBox> </HBox>
</top> </top>

View File

@ -0,0 +1,48 @@
---- Scores file defines all the music and SFX
---- Note: Anything that is shared for all scores is under the "common" section
---- Lines that start with dashes, hash marks, single quotes are treated as comments
Common
--- Sound effects start at number 129
129 bang.mp3
130 boom.mp3
Orchestral
1 Title.mp3
2 Miller.mp3
3 Tragedy.mp3
4 Victory.mp3
5 Grub.mp3
6 mines.mp3
7 Texas.mp3
8 Strange.mp3
9 Oro.mp3
10 Village.mp3
11 Sanctuary.mp3
12 Meriposa.mp3
13 Freemont.mp3
14 Littlecreek.mp3
15 Grove.mp3
16 Poverty.mp3
17 Killed.mp3
18 Wilderness.mp3
---- Chiptune version uses the blips and bleeps that are the voice of a generation
Chiptune
1 Title8.mp3
2 Miller8.mp3
3 Tragedy8.mp3
4 Victory8.mp3
5 Grub8.mp3
6 Mines8.mp3
7 Texas8.mp3
8 Strange8.mp3
9 Oro8.mp3
10 Village8.mp3
11 Sanctuary8.mp3
12 Mariposa8.mp3
13 Freemont8.mp3
14 Littlecreek8.mp3
15 Grove8.mp3
16 Poverty8.mp3
17 Killed8.mp3
18 Wilderness8.mp3

View File

@ -26,11 +26,16 @@
-fx-background-radius: 10px; -fx-background-radius: 10px;
} }
.menuButton, .uiActionButton, .uiSpeedSlider ImageView, .uiSpeedSlider Slider, .uiSpeedSlider AnchorPane { .menuButton, .uiActionButton, .uiSpeedSlider ImageView, .uiSpeedSlider Slider, .uiSpeedSlider AnchorPane, .musicLabel {
-fx-background-color: rgba(0, 0, 0, 0.75); -fx-background-color: rgba(0, 0, 0, 0.75);
-fx-text-fill: #a0FFa0 -fx-text-fill: #a0FFa0
} }
.musicLabel {
-fx-text-alignment: right;
-fx-font-size:18pt;
}
.uiActionButton ImageView, .uiSpeedSlider ImageView { .uiActionButton ImageView, .uiSpeedSlider ImageView {
-fx-effect: dropshadow(gaussian , rgba(64,255,64,0.25) , 2,1.0,0,0); -fx-effect: dropshadow(gaussian , rgba(64,255,64,0.25) , 2,1.0,0,0);
} }