package de.ctdo.crashtest.game; import de.ctdo.crashtest.domotics.*; import de.ctdo.crashtest.gui.*; import de.ctdo.crashtest.irc.*; import de.ctdo.crashtest.mpd.IMPDController; import de.ctdo.crashtest.mpd.MPDController; import java.util.Random; public class TheGame implements StatemachineListener, GuiEventListener, IRCEventListener { private final IGuiControl guiControl; private final IIrcClient ircClient; private final IStatemachine machine; private final IBuntiClient bunti; private final IMPDController mpdController; private final IRelaisboard relaisboard; private final IExtraSoundControl extraSoundControl; private int gamerRating = 3; private Thread discoThread; private boolean shouldStopDisco; private boolean gemActivated = false; private int gemCounter = 0; private boolean startedHurrySound = false; private char lastInput; private boolean sproing; public TheGame(IGuiControl guiControl1) { guiControl = guiControl1; ircClient = new IrcClient("crashtest", "#crashtest","irc.ctdo.de"); bunti = new BuntiClient("bunti.ctdo.de", 8080); mpdController = new MPDController("dampfradio.raum.ctdo.de"); relaisboard = new Relaisboard("/dev/ttyUSB0"); machine = new Statemachine(); extraSoundControl = new ExtraSoundControl(); guiControl.addListener(this); ircClient.addListener(this); machine.addListener(this); machine.reset(); relaisboard.open(); resetDomotics(); } /** * Event listener for state change events from statemachine * @param newState the new game state from statemachine */ @Override public void stateChanged(Statemachine.state newState) { ircClient.say("New State: " + newState); switch (newState) { case NEUTRAL: discoStop(); bunti.setPar56(255,255,255); bunti.setLampel(false,false,false); machine.stopTimer(); break; case IDLE: gamerRating = 3; mpdController.clearPlaylist(); machine.stopTimer(); startedHurrySound = false; resetDomotics(); discoStop(); guiControl.setExtra(""); guiControl.setWall(""); guiControl.showCountDown(false); playRandomStartMix(); //mpdController.setVolume(50); break; case ENTERED_ROOM: relaisboard.setRelais(7, false); // disable light barrier over relais mpdController.playSong("crashtest", "entered_room"); bunti.setLampel(false,false,false); bunti.setPar56(20,0,100); guiControl.setWall("bitte die tuer schliessen. dann kann das spiel beginnen."); guiControl.showCountDown(true); machine.startTimer(60*8); mpdController.setVolume(50); break; case TABLE_GAME_WRONG: extraSoundControl.playJingle("block"); if(!startedHurrySound) { mpdController.playSong("crashtest", "table_game_one"); } bunti.setLampel(true,false,false); sproing = true; break; case TABLE_GAME_ONE: relaisboard.setRelais(6, true); // enable third green circle extraSoundControl.playJingle("jump"); if(machine.getLastState() != Statemachine.state.TABLE_GAME_WRONG) { if(!startedHurrySound) { mpdController.playSong("crashtest", "table_game_one"); } } bunti.setLampel(false, true, false); bunti.setPar56(20,0,100); guiControl.showCountDown(true); break; case TABLE_GAME_TWO: //mpdController.playSong("crashtest", "table_game_two"); extraSoundControl.playJingle("jump"); bunti.setLampel(false,true,false); bunti.setPar56(100,0,100); guiControl.showCountDown(true); break; case TABLE_GAME_THREE: //mpdController.playSong("crashtest", "table_game_three"); extraSoundControl.playJingle("jump"); bunti.setLampel(false,true,false); bunti.setPar56(255,35,0); guiControl.showCountDown(true); break; case TABLE_GAME_FOUR: if(!startedHurrySound) { mpdController.playSong("crashtest", "table_game_four"); } extraSoundControl.playJingle("jump"); bunti.setLampel(false,true,false); bunti.setPar56(200,100,0); guiControl.showCountDown(true); break; case TABLE_GAME_FIVE: //mpdController.playSong("crashtest", "table_game_five"); extraSoundControl.playJingle("jump"); bunti.setLampel(false,true,false); bunti.setPar56(150,150,0); guiControl.showCountDown(true); break; case TABLE_GAME_SIX: //mpdController.playSong("crashtest", "table_game_six"); extraSoundControl.playJingle("jump"); bunti.setLampel(false,true,false); bunti.setPar56(100,200,0); guiControl.showCountDown(true); break; case TABLE_GAME_DONE: mpdController.playSong("crashtest", "table_game_done"); extraSoundControl.playJingle("jump"); bunti.setLampel(false,false,true); bunti.setPar56(100,255,0); guiControl.showCountDown(true); machine.pauseTimer(true); // spieler haben 8 Minuten, wenn sie es in weniger als 4 minuten schaffen // gibts +1, in weniger als 2 minuten gibts +2 if(machine.getTimerSecondsLeft() >= 6*60 ) { rate(2, "table game faster than 2 minutes"); } else if(machine.getTimerSecondsLeft() > 4*60) { rate(1, "table game faster than 4 minutes"); } if(machine.getStateChangeCounter() > 100) { rate(-1, "more than 100 tries"); } sayScore(); relaisboard.setRelais(6, false); // disable third green circle relaisboard.blinkRelais(2, 700); // hint Button break; case ROKET_STARTED: mpdController.playSong("crashtest", "roket_started"); mpdController.addToPlayList("crashtest", "roket_started2"); startedHurrySound = false; bunti.setLampel(false,true,false); bunti.setPar56(0, 255, 0); relaisboard.toggleRelais(0, 300); // r0kets toogle relaisboard.toggleRelais(3, 10000); // oven relaisboard.blinkRelaisStop(2); // stop hint button lamp relaisboard.setRelais(2, false); guiControl.setWall("Pizza ist fertig!"); guiControl.showCountDown(true); machine.startTimer(7*60); break; case ROKET_DONE: mpdController.playSong("crashtest", "roket_done"); //mpdController.setVolume(60); bunti.setLampel(false,false,true); bunti.setPar56(255, 255, 255); guiControl.setWall("willkommen im chaostreff dortmund"); guiControl.showCountDown(true); machine.pauseTimer(true); // spieler haben 7 Minuten, wenn sie es in weniger als 4 minuten schaffen // gibts +1, in weniger als 2 minuten gibts +2 if(machine.getTimerSecondsLeft() >= 5*60 ) { rate(2, "r0kets faster than 2 minutes"); } else if(machine.getTimerSecondsLeft() > 3*60) { rate(1, "r0kets faster than 4 minutes"); } sayScore(); discoStart(); break; } } /** * Event listener for timer ticks from statemachine. * @param tsecondsLeft the seconds left on the current game */ @Override public void timerTick(int tsecondsLeft) { if(gemCounter>0) gemCounter--; guiControl.setCountDown(tsecondsLeft); if(tsecondsLeft == 0) { ircClient.say("timer expired"); Statemachine.state state = machine.getCurrentState(); if( state == Statemachine.state.TABLE_GAME_ONE || state == Statemachine.state.TABLE_GAME_TWO || state == Statemachine.state.TABLE_GAME_THREE || state == Statemachine.state.TABLE_GAME_FOUR || state == Statemachine.state.TABLE_GAME_FIVE || state == Statemachine.state.TABLE_GAME_SIX || state == Statemachine.state.TABLE_GAME_WRONG || state == Statemachine.state.ROKET_STARTED ) { rate(-2, "game not done in time"); guiControl.setWall("die Zeit ist abgelaufen"); sayScore(); mpdController.playSong("crashtest","timeouted"); if(state == Statemachine.state.ROKET_STARTED) { relaisboard.toggleRelais(0, 300); // r0kets toogle (so there will probably be switched off) ircClient.say("if ready, use >reset"); } else { ircClient.say("if ready, set state with >state TABLE_GAME_DONE"); } /* if(state != Statemachine.state.ROKET_STARTED) { ircClient.say("switching to roket started"); machine.setNewState(Statemachine.state.ROKET_STARTED); } */ } } else { if(tsecondsLeft % 600 == 0) { sayScore(); } if(tsecondsLeft <= 630 && !startedHurrySound) { Statemachine.state state = machine.getCurrentState(); if( state != Statemachine.state.TABLE_GAME_DONE && state != Statemachine.state.ROKET_DONE ) { startedHurrySound = true; mpdController.playSong("crashtest", "hurry"); } } } } /** * Event listener for keyPress events from the GUI * @param key the pressed key */ @Override public void keyPress(char key) { machine.handleInput(key); Statemachine.state state = machine.getCurrentState(); if(state == Statemachine.state.ENTERED_ROOM) { if(key == Statemachine.TABLE_TWO || key == Statemachine.TABLE_THREE) { if(lastInput != key) { extraSoundControl.playJingle("block"); } } } else if(state == Statemachine.state.TABLE_GAME_WRONG) { if(key == Statemachine.TABLE_ONE || key == Statemachine.TABLE_TWO || key == Statemachine.TABLE_THREE) { if(lastInput != key && !sproing) { extraSoundControl.playJingle("block"); } sproing = false; } } lastInput = key; } /** * Event lister for window closing event from the GUI */ @Override public void windowClosing() { discoStop(); resetDomotics(); relaisboard.close(); try { Thread.sleep(1000); } catch (InterruptedException ignored) { } bunti.setPar56(0xff,0xff,0xff); ircClient.say("bye"); System.exit(0); } /** * Event listener for IRC Messages * @param message the message from the IRC user */ @Override public void handleMessage(final String message) { if(message.startsWith("help")) { handleHelpCommand(message); } else if(message.equals("reset")) { machine.reset(); } else if(message.startsWith("state")) { handleStateCommand(message); } else if(message.startsWith("timer")) { handleTimerCommand(message); } else if(message.startsWith("score")) { sayScore(); } else if(message.startsWith("wall")) { guiControl.setWall(message.substring("wall".length()).trim()); } else if(message.startsWith("extra")) { guiControl.setExtra(message.substring("extra".length()).trim()); } else if(message.startsWith("relais")) { handleRelaisCommand(message); } else if(message.startsWith("disco on")) { discoStart(); } else if(message.startsWith("disco off")) { discoStop(); } else if(message.startsWith("gem")) { handleGemCommand(); } else { ircClient.say("y u no use valid command?"); } } private void rate(int rating, String text) { gamerRating += rating; ircClient.say("rated: " + rating + " (" + gamerRating + ") " + text); if(gamerRating > 5) gamerRating = 5; if(gamerRating < 1) gamerRating = 1; } private void handleTimerCommand(final String message) { String params = message.substring("timer".length()).trim().toLowerCase(); if(params.startsWith("start")) { String timeStr = params.substring("start".length()).trim(); if(timeStr.length() > 0) { try { int time = Integer.parseInt(timeStr); guiControl.showCountDown(true); machine.startTimer(time); ircClient.say("got it, starting with " + time + " seconds"); } catch (NumberFormatException e) { ircClient.say("your number looks a bit odd..."); } } else { ircClient.say("invalid parameter"); } } else if(params.startsWith("stop")) { machine.stopTimer(); guiControl.showCountDown(false); } else if(params.startsWith("pause")) { machine.pauseTimer(true); } else if(params.startsWith("resume")) { machine.pauseTimer(false); } } private void handleStateCommand(final String message) { if(message.trim().equals("state")) { ircClient.say(machine.getCurrentState().name()); } else { String params = message.substring("state".length()).trim().toLowerCase(); Boolean ok = false; for(Statemachine.state st: Statemachine.state.values()) { if(st.name().toLowerCase().equals(params)) { ok = true; machine.setNewState(st); break; } } if(!ok) ircClient.say("ehm, impossibruu!"); } } // Yes, it makes no sense, but I want have it anyway. For more Spass am Geraet! private void handleGemCommand() { if (!gemActivated) { if(gemCounter > 1200) return; java.util.Random random = new java.util.Random(); int scry = random.nextInt(100); if (scry >= 99) { ircClient.say("Perfect Gem Activated"); } else if (scry >= 90) { ircClient.say("Moooooooo!"); } else { ircClient.say("Gem Activated"); } gemCounter+=100; } else { ircClient.say("Gem Deactivated"); } gemActivated = !gemActivated; } private void handleRelaisCommand(final String message) { String params = message.substring("relais".length()).trim().toLowerCase(); if(params.startsWith("lamp on")) { relaisboard.setRelais(2, true); } else if(params.startsWith("lamp off")) { relaisboard.setRelais(2, false); } else if(params.startsWith("oven on")) { relaisboard.setRelais(3, true); } else if(params.startsWith("oven off")) { relaisboard.setRelais(3, false); } else if(params.startsWith("rokets")) { relaisboard.toggleRelais(0, 300); } else if(params.startsWith("lamp blink")) { relaisboard.blinkRelais(2, 700); } else if(params.startsWith("lamp stop")) { relaisboard.blinkRelaisStop(2); } } private void handleHelpCommand(final String message) { if(message.equals("help")) { ircClient.say("commands: help, reset, state, timer, wall, extra, score, relais, disco, gem"); } else if(message.contains("reset")) { ircClient.say("resets the game to IDLE"); } else if(message.contains("state")) { ircClient.say("get or set game state"); ircClient.say("> state "); ircClient.say("valid states: "); int counter = 0; StringBuilder sb = new StringBuilder(); for(Statemachine.state st: Statemachine.state.values()) { sb.append(st.name()); sb.append(" "); if(++counter == 4) { counter = 0; ircClient.say(sb.toString()); sb.setLength(0); } } if(sb.length() > 0) { ircClient.say(sb.toString()); } } else if(message.contains("timer")) { ircClient.say("control timer. commands are: start, stop, pause, resume."); ircClient.say("for start give lenght in seconds as parameter"); ircClient.say("e.g.: timer start 120"); } else if(message.contains("wall")) { ircClient.say("set text message on the screen"); } else if(message.contains("extra")) { ircClient.say("set small extra message on the screen"); } else if(message.contains("score")) { ircClient.say("i will tell you the current game score"); } else if(message.contains("relais")) { ircClient.say("control the relais board"); ircClient.say("valid commands: lamp on, lamp off, lamp blink, lamp stop, oven on, oven off, rokets"); } else if(message.contains("disco")) { ircClient.say("party! use: disco {on,off}"); } else if(message.contains("gem")) { ircClient.say("The chat gem is working as intended."); } else { ircClient.say("dafuq?"); } } private void sayScore() { StringBuilder sb = new StringBuilder(); sb.append("st.ch.: "); sb.append(machine.getStateChangeCounter()); sb.append(" rating: "); sb.append(gamerRating); int secondsLeft = machine.getTimerSecondsLeft(); int seconds = machine.getTimerSeconds(); int secondsUsed = seconds - secondsLeft; int mins = seconds / 60; int minsUsed = secondsUsed / 60; int minsLeft = secondsLeft / 60; sb.append(String.format(" time:%d:%02d u:%d:%02d l:%d:%02d", mins, seconds % 60, minsUsed, secondsUsed % 60, minsLeft, secondsLeft % 60)); ircClient.say(sb.toString()); } private void resetDomotics() { relaisboard.setRelais(7, true); // enable light barrier over relais relaisboard.setRelais(6, true); // enable third green circle relaisboard.setRelais(2, false); // disable the lamp relaisboard.setRelais(3, false); // disable the oven bunti.setPar56(0, 0, 0); bunti.setLampel(false, false, false); } private void discoStop() { if(discoThread != null) { shouldStopDisco = true; discoThread.interrupt(); try { discoThread.join(500); } catch (InterruptedException ignored) { } } } private void discoStart() { discoStop(); Runnable r = new Runnable() { @Override public void run() { try { while(true) { if(shouldStopDisco) return; bunti.setPar56(0, 255, 0, 0); bunti.setPar56(1, 0, 255, 0); bunti.setPar56(2, 0, 255, 0); bunti.setPar56(3, 255, 0, 0); bunti.setLampel(true, false, false); if(shouldStopDisco) return; Thread.sleep(500); bunti.setPar56(0, 0, 0, 255); bunti.setPar56(1, 255, 255, 0); bunti.setPar56(2, 255, 255, 0); bunti.setPar56(3, 0, 0, 255); bunti.setLampel(false, true, false); if(shouldStopDisco) return; Thread.sleep(500); bunti.setPar56(0, 255, 128, 0); bunti.setPar56(1, 0, 255, 255); bunti.setPar56(2, 0, 255, 255); bunti.setPar56(3, 255, 128, 0); bunti.setLampel(false, false, true); if(shouldStopDisco) return; Thread.sleep(500); bunti.setPar56(0, 0, 255, 255); bunti.setPar56(1, 0, 255, 0); bunti.setPar56(2, 0, 255, 0); bunti.setPar56(3, 0, 255, 255); bunti.setLampel(true, true, true); if(shouldStopDisco) return; Thread.sleep(500); } } catch (InterruptedException ignored) { } } }; discoThread = new Thread(r); shouldStopDisco = false; discoThread.start(); } private void playRandomStartMix() { Random r = new Random(); int num = r.nextInt(3)+1; if(num == 1) { mpdController.playSong("crashtest", "idle"); } else if(num == 2) { mpdController.playSong("crashtest", "idle2"); } else if(num == 3) { mpdController.playSong("crashtest", "idle3"); } else if(num == 4) { mpdController.playSong("crashtest", "idle4"); } mpdController.skipRandomStart(); } }