package ocelot.desktop.node import ocelot.desktop.audio._ import ocelot.desktop.graphics.{Graphics, IconSource} import ocelot.desktop.inventory.SyncedInventory import ocelot.desktop.node.ComputerAwareNode._ import ocelot.desktop.node.Node.Size import ocelot.desktop.ui.UiHandler import ocelot.desktop.ui.event.BrainEvent import ocelot.desktop.ui.event.handlers.DiskActivityHandler import ocelot.desktop.ui.particle.Particle import ocelot.desktop.util.Messages import ocelot.desktop.{ColorScheme, Settings => DesktopSettings} import totoro.ocelot.brain.Settings import totoro.ocelot.brain.entity.traits.{Entity, Environment, WorkspaceAware} import totoro.ocelot.brain.event._ import java.util.Calendar abstract class ComputerAwareNode(entity: Entity with Environment with WorkspaceAware) extends EntityNode(entity) with SyncedInventory with DiskActivityHandler with OcelotLogParticleNode with BoomCardFxHandler with ShiftClickNode { private lazy val soundCardSounds: (SoundStream, SoundSource) = Audio.newStream(SoundCategory.Records) private def soundCardStream: SoundStream = soundCardSounds._1 private def soundCardSource: SoundSource = soundCardSounds._2 eventHandlers += { case BrainEvent(event: MachineCrashEvent) => val message = Messages.lift(event.message) match { case Some(message) => logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message code ${event.message}: $message") message case None => logger.info(s"[EVENT] Machine crash (address = ${event.address})! Message: ${event.message}") event.message } UiHandler.root.workspaceView.particleSystem.add(new ErrorMessageParticle(message)) case BrainEvent(event: BeepEvent) if !Audio.isDisabled => BeepGenerator.newBeep(".", event.frequency, event.duration).play() case BrainEvent(event: BeepPatternEvent) if !Audio.isDisabled => BeepGenerator.newBeep(event.pattern, 1000, 200).play() case BrainEvent(event: SoundCardAudioEvent) if !Audio.isDisabled => val samples = SoundSamples(event.data, Settings.get.soundCardSampleRate, SoundSamples.Format.Mono8) soundCardStream.enqueue(samples) soundCardSource.volume = event.volume } protected def drawOverlay(g: Graphics): Unit = HolidayIcon match { case Some(icon) if DesktopSettings.get.enableFestiveDecorations => val holidayOverlaySize = Size * 1.6f val offset = (holidayOverlaySize - Size) / 2 g.sprite( icon, position.x - offset, position.y - offset, holidayOverlaySize, holidayOverlaySize, ) case _ => } override def draw(g: Graphics): Unit = { super.draw(g) drawOverlay(g) } private class ErrorMessageParticle(message: String) extends Particle(speed = ErrorMessageMoveSpeed) { private val offsetX = size.width / 2 - message.length * 4 private val offsetY = -8 override def draw(g: Graphics): Unit = { g.setSmallFont() g.foreground = ColorScheme("ErrorMessage").withAlpha(1 - (2 * time - 1).min(1).max(0)) g.text(position.x + offsetX, position.y + offsetY - MaxErrorMessageDistance * time, message) g.setNormalFont() } } } object ComputerAwareNode { private val MaxErrorMessageDistance: Float = 50f private val ErrorMessageMoveSpeed: Float = 0.5f private val HolidayIcon: Option[IconSource] = { var dayOfYear = Calendar.getInstance().get(Calendar.DAY_OF_YEAR) val maxDuration = 5 if (dayOfYear >= 365 - maxDuration) { dayOfYear -= 365 } val holidays: Array[(Int, IconSource)] = Array( (1, IconSource.Nodes.Holidays.Christmas), (45, IconSource.Nodes.Holidays.Valentines), (305, IconSource.Nodes.Holidays.Halloween), ) holidays .find(holiday => dayOfYear >= holiday._1 - maxDuration && dayOfYear <= holiday._1 + maxDuration) .map(holiday => holiday._2) } }