diff --git a/lib/ocelot-brain b/lib/ocelot-brain index 62e45f6..5d6dee4 160000 --- a/lib/ocelot-brain +++ b/lib/ocelot-brain @@ -1 +1 @@ -Subproject commit 62e45f6c5dd19ddfbdbc0d4239529b91a011059b +Subproject commit 5d6dee430d6cd04f466d876f10f109b3a3d1057b diff --git a/sprites/nodes/IronNoteBlock.png b/sprites/nodes/IronNoteBlock.png new file mode 100644 index 0000000..a01b7a6 Binary files /dev/null and b/sprites/nodes/IronNoteBlock.png differ diff --git a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png index b59268c..23faa56 100644 Binary files a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png and b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.png differ diff --git a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt index 9cc4a92..7716500 100644 --- a/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt +++ b/src/main/resources/ocelot/desktop/images/spritesheet/spritesheet.txt @@ -1,5 +1,5 @@ BackgroundPattern 0 0 304 304 -BarSegment 476 105 16 4 +BarSegment 493 105 16 4 ComputerMotherboard 305 0 79 70 Empty 9 316 1 1 EmptySlot 441 147 18 18 @@ -8,7 +8,7 @@ KnobCenter 436 0 50 50 KnobLimits 305 71 50 50 ShadowBorder 0 305 1 24 ShadowCorner 424 122 24 24 -TabArrow 441 105 8 14 +TabArrow 458 105 8 14 buttons/BottomDrawerClose 460 147 18 18 buttons/BottomDrawerOpen 479 147 18 18 buttons/PowerOff 424 180 18 18 @@ -102,11 +102,12 @@ nodes/ComputerOnOverlay 441 88 16 16 nodes/DiskDrive 458 88 16 16 nodes/DiskDriveActivity 475 88 16 16 nodes/DiskDriveFloppy 492 88 16 16 -nodes/NewNode 356 105 16 16 -nodes/NoteBlock 373 105 16 16 -nodes/Relay 390 105 16 16 -nodes/Screen 407 105 16 16 -nodes/ScreenOnOverlay 424 105 16 16 +nodes/IronNoteBlock 356 105 16 16 +nodes/NewNode 373 105 16 16 +nodes/NoteBlock 390 105 16 16 +nodes/Relay 407 105 16 16 +nodes/Screen 424 105 16 16 +nodes/ScreenOnOverlay 441 105 16 16 panel/BorderB 8 305 4 4 panel/BorderL 63 305 4 2 panel/BorderR 13 305 4 4 @@ -116,13 +117,13 @@ panel/CornerBR 28 305 4 4 panel/CornerTL 33 305 4 4 panel/CornerTR 38 305 4 4 panel/Fill 6 316 2 2 -particles/Note 468 105 7 10 +particles/Note 485 105 7 10 screen/BorderB 5 305 2 8 screen/BorderT 2 305 2 10 screen/CornerBL 365 236 8 8 screen/CornerBR 374 236 8 8 -screen/CornerTL 450 105 8 10 -screen/CornerTR 459 105 8 10 +screen/CornerTL 467 105 8 10 +screen/CornerTR 476 105 8 10 window/BorderDark 2 316 1 4 window/BorderLight 4 316 1 4 window/CloseButton 383 236 7 6 diff --git a/src/main/scala/ocelot/desktop/OcelotDesktop.scala b/src/main/scala/ocelot/desktop/OcelotDesktop.scala index fcefb1b..b3c4100 100644 --- a/src/main/scala/ocelot/desktop/OcelotDesktop.scala +++ b/src/main/scala/ocelot/desktop/OcelotDesktop.scala @@ -3,7 +3,7 @@ package ocelot.desktop import buildinfo.BuildInfo import li.flor.nativejfilechooser.NativeJFileChooser import ocelot.desktop.audio._ -import ocelot.desktop.node.nodes.NoteBlockNode +import ocelot.desktop.node.nodes.{IronNoteBlockNode, NoteBlockNode} import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.swing.SplashScreen import ocelot.desktop.ui.widget._ @@ -384,10 +384,14 @@ object OcelotDesktop extends Logging { } EventBus.subscribe[NoteBlockTriggerEvent] { event => - SoundSource.fromBuffer(SoundBuffers.NoteBlock(event.instrument), SoundCategory.Beep, pitch = NoteBlockNode.pitches(event.pitch)).play() - UiHandler.root.workspaceView.nodes.find(_.entity.node.address == event.address).get - .asInstanceOf[NoteBlockNode] - .addParticle(event.pitch) + SoundSource.fromBuffer(SoundBuffers.NoteBlock(event.instrument), SoundCategory.Beep, + pitch = NoteBlockNode.Pitches(event.pitch), volume = event.volume.toFloat.min(1f).max(0f)).play() + val node = UiHandler.root.workspaceView.nodes.find(_.entity.node.address == event.address).get + node match { + case nb: NoteBlockNode => nb.addParticle(event.pitch) + case nb: IronNoteBlockNode => nb.addParticle(event.pitch) + case _ => + } } val soundFloppyAccess = SoundBuffers.MachineFloppyAccess.map(buffer => SoundSource.fromBuffer(buffer, SoundCategory.Environment)) diff --git a/src/main/scala/ocelot/desktop/audio/Audio.scala b/src/main/scala/ocelot/desktop/audio/Audio.scala index 8b5573d..7ce3b6a 100644 --- a/src/main/scala/ocelot/desktop/audio/Audio.scala +++ b/src/main/scala/ocelot/desktop/audio/Audio.scala @@ -78,7 +78,7 @@ object Audio extends Logging { AL10.alSourcef(sourceId, AL10.AL_PITCH, source.pitch) AL10.alSource3f(sourceId, AL10.AL_POSITION, 0f, 0f, 0f) AL10.alSourcei(sourceId, AL10.AL_LOOPING, if (source.looping) AL10.AL_TRUE else AL10.AL_FALSE) - AL10.alSourcef(sourceId, AL10.AL_GAIN, SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster) + AL10.alSourcef(sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster) AL10.alSourcePlay(sourceId) val state = SourceState(sourceId, associatedBufferId = if (isAssociated) bufferId else -1, SoundSource.Status.Playing) @@ -105,7 +105,7 @@ object Audio extends Logging { if (isDisabled) return sources.filterInPlace { case (source, state) => - AL10.alSourcef(state.sourceId, AL10.AL_GAIN, SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster) + AL10.alSourcef(state.sourceId, AL10.AL_GAIN, source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster) AL10.alGetSourcei(state.sourceId, AL10.AL_SOURCE_STATE) match { case AL10.AL_PLAYING => state.status = SoundSource.Status.Playing diff --git a/src/main/scala/ocelot/desktop/audio/SoundSource.scala b/src/main/scala/ocelot/desktop/audio/SoundSource.scala index 159c9f9..3931ace 100644 --- a/src/main/scala/ocelot/desktop/audio/SoundSource.scala +++ b/src/main/scala/ocelot/desktop/audio/SoundSource.scala @@ -5,7 +5,8 @@ import java.time.Duration class SoundSource(val kind: SoundSource.Kind, val soundCategory: SoundCategory.Value, val looping: Boolean, - val pitch: Float) { + val pitch: Float, + val volume: Float) { def duration: Duration = { val seconds = kind match { @@ -55,13 +56,13 @@ object SoundSource { case class KindSoundSamples(samples: SoundSamples) extends Kind def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value, - looping: Boolean = false, pitch: Float = 1f): SoundSource = { - new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch) + looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = { + new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume) } def fromSamples(samples: SoundSamples, soundCategory: SoundCategory.Value, - looping: Boolean = false, pitch: Float = 1f): SoundSource = { - new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch) + looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = { + new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume) } object Status extends Enumeration { diff --git a/src/main/scala/ocelot/desktop/node/NodeRegistry.scala b/src/main/scala/ocelot/desktop/node/NodeRegistry.scala index db517d7..eb4d2e9 100644 --- a/src/main/scala/ocelot/desktop/node/NodeRegistry.scala +++ b/src/main/scala/ocelot/desktop/node/NodeRegistry.scala @@ -1,7 +1,7 @@ package ocelot.desktop.node import ocelot.desktop.node.nodes._ -import totoro.ocelot.brain.entity.{Cable, Case, FloppyDiskDrive, NoteBlock, Relay, Screen} +import totoro.ocelot.brain.entity.{Cable, Case, FloppyDiskDrive, IronNoteBlock, NoteBlock, Relay, Screen} import scala.collection.mutable @@ -39,4 +39,8 @@ object NodeRegistry { register(NodeType("NoteBlock", "nodes/NoteBlock", -1, () => { new NoteBlockNode(new NoteBlock) })) + + register(NodeType("IronNoteBlock", "nodes/IronNoteBlock", -1, () => { + new IronNoteBlockNode(new IronNoteBlock) + })) } diff --git a/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala b/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala new file mode 100644 index 0000000..48862e7 --- /dev/null +++ b/src/main/scala/ocelot/desktop/node/nodes/IronNoteBlockNode.scala @@ -0,0 +1,7 @@ +package ocelot.desktop.node.nodes + +import totoro.ocelot.brain.entity.IronNoteBlock + +class IronNoteBlockNode(val ironNoteBlock: IronNoteBlock) extends NoteBlockNodeBase(ironNoteBlock) { + override def icon: String = "nodes/IronNoteBlock" +} diff --git a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala index 999127f..1290050 100644 --- a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala +++ b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNode.scala @@ -1,25 +1,16 @@ package ocelot.desktop.node.nodes -import ocelot.desktop.geometry.{Size2D, Vector2D} -import ocelot.desktop.graphics.Graphics -import ocelot.desktop.node.Node -import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry, ContextMenuSubmenu} -import ocelot.desktop.{ColorScheme, OcelotDesktop} import totoro.ocelot.brain.entity.NoteBlock -import totoro.ocelot.brain.entity.traits.Environment -import totoro.ocelot.brain.nbt.NBTTagCompound -import scala.collection.mutable - -class NoteBlockNode(val noteBlock: NoteBlock) extends Node(noteBlock) { +class NoteBlockNode(val noteBlock: NoteBlock) extends NoteBlockNodeBase(noteBlock) { override def icon: String = "nodes/NoteBlock" override def setupContextMenu(menu: ContextMenu): Unit = { menu.addEntry(new ContextMenuSubmenu("Instrument") { { - val maxLen = NoteBlockNode.instruments.map(_._2.length).max - for ((instrument, name) <- NoteBlockNode.instruments) { + val maxLen = NoteBlockNode.Instruments.map(_._2.length).max + for ((instrument, name) <- NoteBlockNode.Instruments) { val dot = if (noteBlock.instrument == instrument) '•' else ' ' addEntry(new ContextMenuEntry(name.padTo(maxLen, ' ') + dot, () => noteBlock.instrument = instrument)) } @@ -29,31 +20,14 @@ class NoteBlockNode(val noteBlock: NoteBlock) extends Node(noteBlock) { menu.addSeparator() super.setupContextMenu(menu) } - - private val particles = mutable.ArrayBuffer[(Float, Int)]() - - def addParticle(pitch: Int): Unit = { - synchronized { - particles += ((0f, pitch)) - } - } - - override def drawParticles(g: Graphics): Unit = synchronized { - for ((time, pitch) <- particles.reverseIterator) { - val col = ColorScheme("Note" + pitch).withAlpha(1 - (2 * time - 1).min(1).max(0)) - g.sprite("particles/Note", position + Vector2D(pitch / 24f * 40f + 5, height / 2 - 10 - 100 * time), Size2D(14, 20), col) - } - particles.mapInPlace { case (t, p) => (t + 1.2f * UiHandler.dt, p) } - particles.filterInPlace(_._1 <= 1f) - } } object NoteBlockNode { - val pitches = List( + val Pitches: Seq[Float] = List( 0.500000f, 0.529732f, 0.561231f, 0.594604f, 0.629961f, 0.667420f, 0.707107f, 0.749154f, 0.793701f, 0.840896f, 0.890899f, 0.943874f, 1.059463f, 1.122462f, 1.189207f, 1.259921f, 1.334840f, 1.414214f, 1.498307f, 1.587401f, 1.681793f, 1.781797f, 1.887749f, 2.000000f) - val instruments = List( + private val Instruments = List( ("bass", "Bass (Wood)"), ("snare", "Snare Drum (Sand)"), ("hat", "Hi-hat (Glass)"), diff --git a/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala new file mode 100644 index 0000000..9b67318 --- /dev/null +++ b/src/main/scala/ocelot/desktop/node/nodes/NoteBlockNodeBase.scala @@ -0,0 +1,29 @@ +package ocelot.desktop.node.nodes + +import ocelot.desktop.ColorScheme +import ocelot.desktop.geometry.{Size2D, Vector2D} +import ocelot.desktop.graphics.Graphics +import ocelot.desktop.node.Node +import ocelot.desktop.ui.UiHandler +import totoro.ocelot.brain.entity.traits.{Entity, Environment} + +import scala.collection.mutable + +abstract class NoteBlockNodeBase(entity: Entity with Environment) extends Node(entity) { + private val particles = mutable.ArrayBuffer[(Float, Int)]() + + def addParticle(pitch: Int): Unit = { + synchronized { + particles += ((0f, pitch)) + } + } + + override def drawParticles(g: Graphics): Unit = synchronized { + for ((time, pitch) <- particles.reverseIterator) { + val col = ColorScheme("Note" + pitch).withAlpha(1 - (2 * time - 1).min(1).max(0)) + g.sprite("particles/Note", position + Vector2D(pitch / 24f * 40f + 5, height / 2 - 10 - 100 * time), Size2D(14, 20), col) + } + particles.mapInPlace { case (t, p) => (t + 1.2f * UiHandler.dt, p) } + particles.filterInPlace(_._1 <= 1f) + } +}