diff --git a/Classic.mtp b/Classic.mtp new file mode 100644 index 0000000..8d7f9c6 Binary files /dev/null and b/Classic.mtp differ diff --git a/Convertor/src/turtle/editor/TMXTestLevel.java b/Convertor/src/turtle/editor/TMXTestLevel.java index c8ad9de..0c0f96f 100644 --- a/Convertor/src/turtle/editor/TMXTestLevel.java +++ b/Convertor/src/turtle/editor/TMXTestLevel.java @@ -7,6 +7,7 @@ import turtle.ui.MainApp; import java.io.File; +import java.util.UUID; /** * @author Henry Wang @@ -36,10 +37,11 @@ public static void main(String args[]) throws Exception { System.setProperty("user.dir", lvlFile.getParent()); - Launcher.testing = new LevelPack("Testing"); + LevelPack pack = Launcher.testing = new LevelPack("Testing"); + pack.setLevelPackID(new UUID(0, 0)); Level lvl = TMXToMTP.loadLevel(lvlFile); - Launcher.testing.addLevel(lvl); + pack.addLevel(lvl); Application.launch(Launcher.class); } diff --git a/Main/Main.iml b/Main/Main.iml index e5d2daf..4201de2 100644 --- a/Main/Main.iml +++ b/Main/Main.iml @@ -5,6 +5,7 @@ + diff --git a/Main/res/turtle/comp/blowing.wav b/Main/res/turtle/comp/blowing.wav new file mode 100644 index 0000000..081d75f Binary files /dev/null and b/Main/res/turtle/comp/blowing.wav differ diff --git a/Main/res/turtle/comp/click.wav b/Main/res/turtle/comp/click.wav new file mode 100644 index 0000000..2592c25 Binary files /dev/null and b/Main/res/turtle/comp/click.wav differ diff --git a/Main/res/turtle/comp/explosion.wav b/Main/res/turtle/comp/explosion.wav new file mode 100644 index 0000000..665d649 Binary files /dev/null and b/Main/res/turtle/comp/explosion.wav differ diff --git a/Main/res/turtle/comp/grass.wav b/Main/res/turtle/comp/grass.wav new file mode 100644 index 0000000..f584003 Binary files /dev/null and b/Main/res/turtle/comp/grass.wav differ diff --git a/Main/res/turtle/comp/splash.wav b/Main/res/turtle/comp/splash.wav new file mode 100644 index 0000000..fc38737 Binary files /dev/null and b/Main/res/turtle/comp/splash.wav differ diff --git a/Main/res/turtle/comp/steam.wav b/Main/res/turtle/comp/steam.wav new file mode 100644 index 0000000..46fe2cc Binary files /dev/null and b/Main/res/turtle/comp/steam.wav differ diff --git a/Main/res/turtle/comp/tap.wav b/Main/res/turtle/comp/tap.wav new file mode 100644 index 0000000..f686807 Binary files /dev/null and b/Main/res/turtle/comp/tap.wav differ diff --git a/Main/res/turtle/comp/unlock.wav b/Main/res/turtle/comp/unlock.wav new file mode 100644 index 0000000..90d2fd4 Binary files /dev/null and b/Main/res/turtle/comp/unlock.wav differ diff --git a/Main/res/turtle/comp/whip.wav b/Main/res/turtle/comp/whip.wav new file mode 100644 index 0000000..e89d2cc Binary files /dev/null and b/Main/res/turtle/comp/whip.wav differ diff --git a/Main/src/turtle/ui/styles.css b/Main/res/turtle/ui/styles.css similarity index 100% rename from Main/src/turtle/ui/styles.css rename to Main/res/turtle/ui/styles.css diff --git a/Main/src/turtle/comp/Bucket.java b/Main/src/turtle/comp/Bucket.java index 9ac69d6..5994bc7 100644 --- a/Main/src/turtle/comp/Bucket.java +++ b/Main/src/turtle/comp/Bucket.java @@ -45,6 +45,7 @@ public class Bucket extends Mover { public boolean die(Component attacker) { if (attacker instanceof Water) { if (!isFilled()) { + playSound(Sounds.SPLASH); setFilled(true); ((Water) attacker).transformToSand(); } diff --git a/Main/src/turtle/comp/Button.java b/Main/src/turtle/comp/Button.java index 06a8f78..dc88cb9 100644 --- a/Main/src/turtle/comp/Button.java +++ b/Main/src/turtle/comp/Button.java @@ -66,6 +66,7 @@ public void setLinked(Location linkedLocation) { @Override public boolean interact(Actor other) { Grid parent = getParentGrid(); + playSound(Sounds.CLICK); if (parent != null && parent.isValidLocation(linkedLocation)) { Cell factory = parent.getCellAt(linkedLocation); if (factory instanceof Factory) { diff --git a/Main/src/turtle/comp/Cannon.java b/Main/src/turtle/comp/Cannon.java index 1ea0cd4..2d80f8e 100644 --- a/Main/src/turtle/comp/Cannon.java +++ b/Main/src/turtle/comp/Cannon.java @@ -54,6 +54,8 @@ private void shoot() { return; } + playSound(Sounds.EXPLOSION); + Direction heading = getHeading(); Location loc = new Location(getHeadLocation()); heading.traverse(loc); diff --git a/Main/src/turtle/comp/Door.java b/Main/src/turtle/comp/Door.java index bb713a6..6490fe3 100644 --- a/Main/src/turtle/comp/Door.java +++ b/Main/src/turtle/comp/Door.java @@ -69,6 +69,7 @@ public boolean checkInteract(Actor other) { if (other instanceof Player) { for (Item itm : ((Player) other).getPocket()) { if (itm instanceof Key && ((Key) itm).getColor() == getColor()) { + playSound(Sounds.UNLOCK); die(this); return true; } diff --git a/Main/src/turtle/comp/Exit.java b/Main/src/turtle/comp/Exit.java index f54f65c..2d19009 100644 --- a/Main/src/turtle/comp/Exit.java +++ b/Main/src/turtle/comp/Exit.java @@ -44,6 +44,7 @@ public void updateFrame(long frame) { super.updateFrame(frame); if (winner != null && winner.getTrailingLocation().equals( getHeadLocation())) { + //TODO: sound winner.win(); } } diff --git a/Main/src/turtle/comp/Factory.java b/Main/src/turtle/comp/Factory.java index 775b296..acc9d1e 100644 --- a/Main/src/turtle/comp/Factory.java +++ b/Main/src/turtle/comp/Factory.java @@ -22,7 +22,7 @@ public class Factory extends Cell { */ public static final int DEFAULT_IMAGE = 70; private static final int FACTORY_OFFSET_IMAGE = DEFAULT_IMAGE; - private static final double RATIO_CLONE_IMG = .8; + private static final double RATIO_CLONE_IMG = .7; private static final long serialVersionUID = -8544196441607182765L; /** diff --git a/Main/src/turtle/comp/Fire.java b/Main/src/turtle/comp/Fire.java index 0060345..e7720db 100644 --- a/Main/src/turtle/comp/Fire.java +++ b/Main/src/turtle/comp/Fire.java @@ -69,7 +69,10 @@ public boolean pass(Actor visitor) { if (smoking) { return true; } - visitor.die(this); + if (visitor.die(this)) { + playSound(Sounds.STEAM); + Sounds.GRASS.play(); + } return true; } diff --git a/Main/src/turtle/comp/Food.java b/Main/src/turtle/comp/Food.java index c2aa02d..7a30a17 100644 --- a/Main/src/turtle/comp/Food.java +++ b/Main/src/turtle/comp/Food.java @@ -35,7 +35,11 @@ public boolean checkInteract(Actor other) { */ @Override public boolean interact(Actor other) { - return other instanceof Player && super.interact(other); + if (other instanceof Player) { + //TODO: sound + return super.interact(other); + } + return false; } /** diff --git a/Main/src/turtle/comp/Grass.java b/Main/src/turtle/comp/Grass.java index 20d3471..85a74c9 100644 --- a/Main/src/turtle/comp/Grass.java +++ b/Main/src/turtle/comp/Grass.java @@ -124,6 +124,8 @@ public void updateFrame(long frame) { if (dr + dc <= 1) { fading = 0; animateFrames(TRANSFORM_FRAMES, false); + if (!Sounds.GRASS.isPlaying()) + playSound(Sounds.GRASS); } } } diff --git a/Main/src/turtle/comp/Item.java b/Main/src/turtle/comp/Item.java index 19ba54b..98bae1e 100644 --- a/Main/src/turtle/comp/Item.java +++ b/Main/src/turtle/comp/Item.java @@ -23,6 +23,8 @@ public abstract class Item extends Actor { @Override public boolean interact(Actor other) { if (other instanceof Player) { + //if (getCurrentClip() == null) + playSound(Sounds.TAP); Player p = (Player) other; if (p.collectItem(this)) { getParentGrid().removeActor(this); diff --git a/Main/src/turtle/comp/Sounds.java b/Main/src/turtle/comp/Sounds.java new file mode 100644 index 0000000..5f266cd --- /dev/null +++ b/Main/src/turtle/comp/Sounds.java @@ -0,0 +1,32 @@ +package turtle.comp; + +import javafx.scene.media.AudioClip; + + +import static java.lang.ClassLoader.getSystemResource; + +/** + * Contains all the default sounds that can be used for various interactions + * @author Henry Wang + */ +public interface Sounds { + AudioClip BLOWING = loadClip("turtle/comp/blowing.wav"); + AudioClip CLICK = loadClip("turtle/comp/click.wav"); + AudioClip EXPLOSION = loadClip("turtle/comp/explosion.wav"); + AudioClip GRASS = loadClip("turtle/comp/grass.wav"); + AudioClip SPLASH = loadClip("turtle/comp/splash.wav"); + AudioClip STEAM = loadClip("turtle/comp/steam.wav"); + AudioClip TAP = loadClip("turtle/comp/tap.wav"); + AudioClip UNLOCK = loadClip("turtle/comp/unlock.wav"); + AudioClip WHIP = loadClip("turtle/comp/whip.wav"); + + /** + * Loads an audio clip from the classpath + * @param res the classpath of the audio clip + * @return the loaded audio clip + */ + static AudioClip loadClip(String res) { + + return new AudioClip(getSystemResource(res).toExternalForm()); + } +} diff --git a/Main/src/turtle/comp/Trap.java b/Main/src/turtle/comp/Trap.java index f03c9a7..769aebf 100644 --- a/Main/src/turtle/comp/Trap.java +++ b/Main/src/turtle/comp/Trap.java @@ -40,7 +40,7 @@ public class Trap extends Actor { */ @Override public boolean checkInteract(Actor other) { - return true; + return !(other instanceof Trap); } /** @@ -53,6 +53,7 @@ public boolean checkInteract(Actor other) { @Override public boolean interact(Actor other) { if (other.die(this)) { + playSound(Sounds.WHIP); die(this); } return true; @@ -67,7 +68,7 @@ public boolean interact(Actor other) { */ @Override public DominanceLevel dominanceLevelFor(Actor other) { - return FIXTURE; + return FIXTURE_TRAP; } /** diff --git a/Main/src/turtle/comp/Water.java b/Main/src/turtle/comp/Water.java index 5e761e4..2d6ab19 100644 --- a/Main/src/turtle/comp/Water.java +++ b/Main/src/turtle/comp/Water.java @@ -1,6 +1,8 @@ package turtle.comp; +import javafx.animation.Transition; import javafx.scene.image.ImageView; +import javafx.util.Duration; import turtle.core.Actor; import turtle.core.Cell; import turtle.core.TileSet; @@ -26,6 +28,7 @@ public class Water extends Cell { private static final int MAX_TRANSFORM = TRANSFORM_ANIMATION_FRAME.length * DEF_ANIMATION_FRAME_CHANGE; private static final long serialVersionUID = 991189208764206004L; + private static final Duration SPLASH_DELAY = Duration.millis(100); /** * Determines the associated attributes with a tile if the tile is related to this object. @@ -63,7 +66,9 @@ public Water() { */ @Override public boolean pass(Actor visitor) { - visitor.die(this); + if (visitor.die(this)) { + playSound(Sounds.SPLASH); + } return true; } diff --git a/Main/src/turtle/core/Actor.java b/Main/src/turtle/core/Actor.java index 8c1b064..30951f7 100644 --- a/Main/src/turtle/core/Actor.java +++ b/Main/src/turtle/core/Actor.java @@ -27,6 +27,7 @@ public abstract class Actor extends Component { public static final DominanceLevel ENEMY = new DominanceLevel("Enemy", 100); public static final DominanceLevel MOVER = new DominanceLevel("Mover", 200); public static final DominanceLevel ITEM = new DominanceLevel("Item", 300); + public static final DominanceLevel FIXTURE_TRAP = new DominanceLevel("Fixture_Trap", 350); public static final DominanceLevel FIXTURE = new DominanceLevel("Fixture", 400); private static final long serialVersionUID = -8229684437846026366L; diff --git a/Main/src/turtle/core/Component.java b/Main/src/turtle/core/Component.java index 4e83c18..1966367 100644 --- a/Main/src/turtle/core/Component.java +++ b/Main/src/turtle/core/Component.java @@ -1,13 +1,16 @@ package turtle.core; +import javafx.application.Platform; import javafx.geometry.HPos; import javafx.geometry.VPos; import javafx.scene.Node; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; +import javafx.scene.media.AudioClip; import turtle.attributes.Attributable; import turtle.attributes.AttributeSet; import turtle.attributes.NotAttribute; +import turtle.comp.Sounds; import java.io.IOException; import java.io.ObjectInputStream; @@ -26,6 +29,33 @@ */ public abstract class Component extends Pane implements Attributable, Serializable { + public static final int DEFAULT_IMAGE = -1; + + public static final int DEF_ANIMATION_FRAME_CHANGE = 4; + public static final TileSet DEFAULT_SET = new TileSet(); + public static final int BIG_FRAME = 10; + + private static final int INVALID_IMAGE_FRAME = -2; + private static final long serialVersionUID = -65657197093045828L; + + private static final int SHUFFLE = 50; + private static AudioClip currentClip = null; + + static { + //Initialize all the sounds + for (Field fld : Sounds.class.getFields()) { + try { + Object val = fld.get(null); + if (val instanceof AudioClip) { + ((AudioClip) val).play(0); + ((AudioClip) val).stop(); + } + } catch (IllegalAccessException e) { + throw new Error(e); + } + } + } + /** * Obtains the default image of this class. If this class does not define * a default image, this will search up the hierarchy until the field is @@ -55,9 +85,9 @@ public static int getDefaultImage(Class comp) { return DEFAULT_IMAGE; } - public static final int DEFAULT_IMAGE = -1; - private static final int INVALID_IMAGE_FRAME = -2; - private static final long serialVersionUID = -65657197093045828L; + public static AudioClip getCurrentClip() { + return (currentClip != null && currentClip.isPlaying()) ? currentClip : null; + } /** * Utility method used to shuffle an array. @@ -91,11 +121,6 @@ public static void shuffle(Object[] arr, Random rng) { } } - public static final int DEF_ANIMATION_FRAME_CHANGE = 4; - public static final TileSet DEFAULT_SET = new TileSet(); - public static final int BIG_FRAME = 10; - private static final int SHUFFLE = 50; - private final Location headLoc; private final Location trailLoc; private final AttributeSet attributes; @@ -128,19 +153,6 @@ protected Component() { attributes = new AttributeSet<>(this); } - /** - * Initializes the tile-set and image-view. This should ONLY be called - * exactly once in the component's lifetime. - * - * @param ts the tileset to initialize to. - */ - private void initTileSet(TileSet ts) { - this.ts = ts; - currentImage = INVALID_IMAGE_FRAME; - img = new ImageView(ts.getImageSet()); - this.getChildren().add(img); - } - /** * Animates component through a series of frames at the default * change rate (change once per 4 frames). @@ -291,6 +303,45 @@ protected void layoutChildren() { VPos.CENTER); } + /** + * Plays an audio clip sound, stopping the previous sound effect. + * @param sound the sound clip to play + */ + protected void playSound(AudioClip sound) { + if (!Platform.isFxApplicationThread()) { + throw new IllegalStateException("Must be on Fx thread"); + } + + if (getParentGrid() == null || !getParentGrid().isPlaying()) { + return; + } + + //if (currentClip != null && currentClip.isPlaying()) { + // currentClip.stop(); + //} + if (sound.isPlaying()) { + sound.stop(); + } + + //TODO: configure volume. + + sound.play(); + currentClip = sound; + } + + /** + * Initializes the tile-set and image-view. This should ONLY be called + * exactly once in the component's lifetime. + * + * @param ts the tileset to initialize to. + */ + private void initTileSet(TileSet ts) { + this.ts = ts; + currentImage = INVALID_IMAGE_FRAME; + img = new ImageView(ts.getImageSet()); + this.getChildren().add(img); + } + /** * Obtains next step of incrementing a value * diff --git a/Main/src/turtle/core/Grid.java b/Main/src/turtle/core/Grid.java index 478019c..801975b 100644 --- a/Main/src/turtle/core/Grid.java +++ b/Main/src/turtle/core/Grid.java @@ -33,6 +33,7 @@ public class Grid extends Pane implements Serializable { private int timeLeft; private Direction lastMove; private final Recording recording; + private boolean playing; /** @@ -181,6 +182,14 @@ public int getTimeLeft() { return timeLeft; } + public boolean isPlaying() { + return playing; + } + + public void setPlaying(boolean playing) { + this.playing = playing; + } + /** * Setter method for remaining time * diff --git a/Main/src/turtle/ui/GameUI.java b/Main/src/turtle/ui/GameUI.java index 87c44ab..b2a2157 100644 --- a/Main/src/turtle/ui/GameUI.java +++ b/Main/src/turtle/ui/GameUI.java @@ -374,13 +374,8 @@ private void handleLevelDialog(int response) { * occurred. */ private void handleKey(KeyEvent event) { - if (state == HALTED) { - return; - } - if (event.getEventType() == KeyEvent.KEY_TYPED) { - return; - } - if (!mappedKeys.containsKey(event.getCode())) { + if (state == HALTED || event.getEventType() == KeyEvent.KEY_TYPED || + !mappedKeys.containsKey(event.getCode())) { return; } @@ -738,6 +733,7 @@ private void stopGame() { runner.stop(); if (view.getGrid() != null) { Recording r = view.getGrid().getRecording(); + view.getGrid().setPlaying(false); if (r != null) { r.stop(); } @@ -756,6 +752,7 @@ private void pauseGame() { return; } state = PAUSED; + view.getGrid().setPlaying(false); runner.pause(); for (int i = 0; i < moving.length; i++) @@ -771,6 +768,7 @@ private void resumeGame() { return; } state = RUNNING; + view.getGrid().setPlaying(true); runner.start(); } @@ -781,6 +779,7 @@ private void resumeGame() { private void startGame() { if (state == STOPPED) { runner.start(); + view.getGrid().setPlaying(true); state = RUNNING; } } diff --git a/Main/src/turtle/ui/MainApp.java b/Main/src/turtle/ui/MainApp.java index e68fac0..ad13aa5 100644 --- a/Main/src/turtle/ui/MainApp.java +++ b/Main/src/turtle/ui/MainApp.java @@ -422,8 +422,7 @@ private void loadLevelPacks() { } } if (!success) { - showDialog(new DialogBoxUI("Unable to load some level packs.", - "OK")); + showDialog(new DialogBoxUI("Unable to load some level packs.", "OK")); } } diff --git a/test.mtp b/test.mtp deleted file mode 100644 index c30f4f7..0000000 Binary files a/test.mtp and /dev/null differ