package gdi1sokoban.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.border.BevelBorder;
import gdi1sokoban.exceptions.InternalFailureException;
import gdi1sokoban.exceptions.LevelHistoryEntryNotFoundException;
import gdi1sokoban.exceptions.ParameterOutOfRangeException;
import gdi1sokoban.exceptions.ParseException;
import gdi1sokoban.game.GameEngine;
import gdi1sokoban.globals.SokobanFileExtFilter;
import gdi1sokoban.sound.SoundEngine;
/**
* SokobanWindow class
*
* extends the GameWindow class and provides
* basic functions for controlling the game
*/
public class SokobanWindow extends GameWindow {
private static final long serialVersionUID = -3022370482307739059L;
private GameEngine owner;
private final SoundEngine sndEngine = new SoundEngine();
// labels for status bar
public SokobanWindow
(final String windowTitle
)
throws InternalFailureException {
super(windowTitle);
setMenu();
setStatusBar();
}
/**
* sets the owner for the Gui-Object
* @param owner the GameEngine that owns this gui-object
*/
public void setOwner(final GameEngine owner){
this.owner = owner;
}
/**
* creates the menu for the Game
*/
private void setMenu(){
// Create Menu Bar
// Create Game Menu
// Create History Menu
// Create Skin Menu
// Group for JRadioButtonMenuItem
// Create Help Menu
// Add menus to Menubar
menuBar.add(gameMenu);
menuBar.add(historyMenu);
menuBar.add(helpMenu);
// Create New Game Item
// set shortcut key
newGameItem.setMnemonic('N');
// Add action to New Game
// Action performed when clicked
keyNewGamePressed();
}
});
// Create Load Game Item
// set shortcut key
loadGameItem.setMnemonic('L');
// Add action to Save Game
// Action performed when clicked
keyLoadGamePressed();
}
});
// Create Save Game Item
// set shortcut key
saveGameItem.setMnemonic('S');
// Add action to Save Game
// Action performed when clicked
keySaveGamePressed();
}
});
// Create Quit Game Item
// set shortcut key
quitGameItem.setMnemonic('Q');
// Add action to Quit
// Action performed when clicked
}
});
// Create Undo Item
// set shortcut key
//aboutItem.setMnemonic('A');
// Add action to Undo
// Action performed when clicked
keyUndoPressed();
}
});
// Create Redo Item
// set shortcut key
//aboutItem.setMnemonic('A');
// Add action to Redo
// Action performed when clicked
keyRedoPressed();
}
});
// Create Show Highscore Item
// set shortcut key
highscoreItem.setMnemonic('H');
// Add action to Show Highscore
// Action performed when clicked
showHighscores();
}
});
// Create About Item
// set shortcut key
aboutItem.setMnemonic('A');
// Add action to Show Highscore
// Action performed when clicked
showAbout();
}
});
// Entries for skin-submenu
// When Game is started Big Skin is selected
bigSkinItem.setSelected(true);
// Add action to switch skin
// Action performed when clicked
loadSkin(0);
}
});
// Action performed when clicked
loadSkin(1);
}
});
// Action performed when clicked
loadSkin(2);
}
});
// Action performed when clicked
loadSkin(3);
}
});
// Add entries to group
skinGroup.add(standardSkinItem);
skinGroup.add(bigSkinItem);
skinGroup.add(doomSkinItem);
skinGroup.add(crazyPaulSkinItem);
// Add Items to Menu
gameMenu.add(newGameItem);
gameMenu.add(loadGameItem);
gameMenu.add(saveGameItem);
gameMenu.add(skinMenu);
gameMenu.add(quitGameItem);
historyMenu.add(undoItem);
historyMenu.add(redoItem);
historyMenu.add(highscoreItem);
helpMenu.add(aboutItem);
// Add Items to Skin-Submenu
skinMenu.add(standardSkinItem);
skinMenu.add(bigSkinItem);
skinMenu.add(doomSkinItem);
skinMenu.add(crazyPaulSkinItem);
// Add Menu Bar to Window
this.setJMenuBar(menuBar);
}
/**
* adds a statusbar to the gameWindow
*/
public void setStatusBar(){
coords.
setFont(new Font("Arial",
Font.
BOLD,
10));
coords.
setHorizontalAlignment(JLabel.
CENTER);
// time label
time.
setFont(new Font("Arial",
Font.
BOLD,
10));
time.
setHorizontalAlignment(JLabel.
CENTER);
// step-counter label
steps.
setFont(new Font("Arial",
Font.
BOLD,
10));
steps.
setHorizontalAlignment(JLabel.
CENTER);
// infos label
infos.
setFont(new Font("Arial",
Font.
BOLD,
10));
infos.
setHorizontalAlignment(JLabel.
CENTER);
// add labels to statusbar
statusBar.add(time);
statusBar.add(coords);
statusBar.add(steps);
statusBar.add(infos);
statusBar.
setPreferredSize(new Dimension(getWidth
(),
20));
// add statusbar to gamewindow
}
/**
* sets the info field in the Statusbar
* @param info text that shall be shown in the statusbar
*/
public void setStatusBarInfo
(final String info
){
infoText = info;
updateStatusBar();
}
protected SokobanPanel createGamePanel() {
final SokobanPanel panel = new SokobanPanel(this);
add(panel);
return panel;
}
/**
* Events for other keys
*/
protected void keyOtherPressed
(final KeyEvent key
) {
try {
owner.checkCheat(key.getKeyChar());
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Fehler: Ungültige Levelkoordinaten!");
}
}
/**
* Redo actions
*/
protected void keyRedoPressed() {
try {
sndEngine.playSound("redoSound", "sounds/redo.mp3", SoundEngine.SND_ASYNC);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
try {
owner.redo();
owner.roundEnd(true);
} catch (final LevelHistoryEntryNotFoundException e) {
System.
err.
println("Redo nicht möglich: Keine gültigen Schritte verfügbar");
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Fehler: Ungültige Levelkoordinaten!");
}
}
/**
* Plays a sound when a player is perfoming a move
* @param validStep was this move a valid step?
*/
private void playMoveSound(final boolean validStep) {
if (validStep)
try {
sndEngine.playSound("moveSound", "sounds/walk.mp3", SoundEngine.SND_ASYNC);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
else
try {
sndEngine.playSound("wallSound", "sounds/wall.mp3", SoundEngine.SND_ASYNC);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
}
/**
* plays a sound when user types in a cheat
*/
public void playCheatSound() {
try {
sndEngine.playSound("cheatSound", "sounds/cheat.mp3", SoundEngine.SND_ASYNC);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
}
/**
* shows a warning when a deadlock state is reached
*/
public void showDeadlockMessage() {
JOptionPane.
showMessageDialog(null,
"Das Level befindet sich in einem Deadlockzustand.\n\nBenutzen Sie die Undo-Funktion, um die Schritte,\ndie zu diesem Zustand geführt haben, rückgängig zu machen.",
"Deadlock-Zustand",
JOptionPane.
WARNING_MESSAGE);
setStatusBarInfo("Deadlock");
}
/**
* shows an error message when level syntax is incorrect
*/
public void showParseError() {
JOptionPane.
showMessageDialog(null,
"Das Level konnte nicht geladen werden.\nDie Datei ist fehlerhaft.",
"Leveldatei fehlerhaft",
JOptionPane.
ERROR_MESSAGE);
setStatusBarInfo("Error");
}
/**
* Plays a sound when there is a deadlock situation
*/
public void playDeadlockSound() {
try {
sndEngine.stopSound("backgroundSound");
sndEngine.playSound("deadlock", "sounds/stuck.mp3", SoundEngine.SND_ASYNC + SoundEngine.SND_LOOP);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
}
/**
* Plays the background sound
*/
public void playBackgroundSound() {
try {
setStatusBarInfo("");
sndEngine.stopSound("deadlock");
sndEngine.playSound("backgroundSound", "sounds/bg.mp3", SoundEngine.SND_ASYNC + SoundEngine.SND_LOOP);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
}
/**
* Moves the player
* @param dir directory
*/
private void movePlayer(final char dir) {
boolean validMove;
try {
validMove = owner.movePlayer(dir);
playMoveSound(validMove);
owner.roundEnd(validMove);
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Fehler: Ungültige Levelkoordinaten!");
}
}
/**
* updates information on the status panel
*/
private final void updateStatusBar() {
if((owner != null) && (owner.getGameLevel() != null)) {
try {
coords.setText("Lvl: " + owner.getCurrentLevelNr() + " [" + owner.getGameLevel().getPlayerPos().left + ", " + owner.getGameLevel().getPlayerPos().top + "]");
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Fehler: Ungültige Levelkoordinaten!");
} // show player coordinates
steps.setText("Schritte: " + owner.getGameLevel().getSteps()); // show number of made steps
time.setText("Zeit: " + owner.getPlayingTime(0) + " Sek.");
infos.setText(infoText); // show info
}
}
protected void keyDownPressed() {
movePlayer('D');
}
protected void keyLeftPressed() {
movePlayer('L');
}
protected void keyRightPressed() {
movePlayer('R');
}
protected void keyUpPressed() {
movePlayer('U');
}
/**
* Undo event
*/
protected void keyUndoPressed() {
try {
sndEngine.playSound("undoSound", "sounds/undo.mp3", SoundEngine.SND_ASYNC);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Abspielen eines Sounds");
}
try {
owner.undo();
owner.roundEnd(true);
} catch (final LevelHistoryEntryNotFoundException e) {
System.
err.
println("Undo nicht möglich: Keine vorherigen Schritte verfügbar.");
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Fehler: Ungültige Levelkoordinaten!");
}
}
/**
* Method is called when panel is resized.
* Updates the size of the Window
*/
public void panelResized(){
// Set Window's size to size of panel
setSize(getWidth(),getHeight());
setVisible( true );
}
/**
* Shows the highscore list
*/
public void showHighscores() {
showHighscores(owner.getCurrentLevelNr());
}
public void showHighscores(final int levelNo) {
hsForm.setBounds(0, 0, 600, 370);
final String [] hsTableHeader =
{"Platz",
"Spielername",
"Benötigte Schritte",
"Benötigte Zeit (in Sek.)" };
final JTable hsTable =
new JTable(owner.
getHighscores().
toStringArray(levelNo
), hsTableHeader
);
final String [] hsLevels = owner.
getHighscores().
getLevels();
hsLevelList.
setSelectedItem(String.
valueOf(levelNo
));
// Action performed when clicked
hsForm.setVisible(false);
showHighscores
(Integer.
valueOf((String)hsLevelList.
getSelectedItem()));
}
});
hsTable.
setPreferredScrollableViewportSize(new Dimension(520,
220));
hsTable.setFillsViewportHeight(true);
hsTable.setShowGrid(false);
hsTable.setShowHorizontalLines(true);
hsTable.setRowHeight(22);
hsTable.
setFont(new Font(null,
Font.
PLAIN,
20));
hsTable.setEnabled(false);
// Create the scroll pane and add the table to it.
hsForm.setResizable(false);
hsForm.setTitle("Highscores - Level " + levelNo);
final int top = (screenSize.height - hsForm.getHeight()) / 2;
final int left = (screenSize.width - hsForm.getWidth()) / 2;
hsForm.setLocation(left, top);
// Action performed when clicked
if (JOptionPane.
showConfirmDialog(null,
"/!\\ GEFAHR /!\\\n\n=======================================\n\nALLE HIGHSCORES WERDEN UNWIDERBRINGLICH VERNICHTET.\n\n=======================================\n\nFortfahren?",
"/!\\ GEFAHR /!\\",
JOptionPane.
YES_NO_OPTION,
JOptionPane.
WARNING_MESSAGE) ==
JOptionPane.
YES_OPTION) {
owner.getHighscores().resetHighscores();
showHighscores(levelNo);
hsForm.setVisible(false);
}
}
});
// Action performed when clicked
hsForm.setVisible(false);
}
});
hsPanel.add(hsLevelLabel);
hsPanel.add(hsLevelList);
hsPanel.add(hsScrollPane);
hsPanel.add(hsResetButton);
hsPanel.add(hsCloseButton);
hsForm.add(hsPanel);
hsForm.getRootPane().setDefaultButton(hsCloseButton);
hsForm.setModal(true);
hsForm.setVisible(true);
}
/**
* Shows the about box
*/
public void showAbout(){
"<h1><b><font color=\"#000099\">Sokoban</b></h1>" +
"<H3><I>developed by:</I> Yocto Software Solutions</H3>" +
"<ul><small>"+
"Super Samuel Kniesz"+
"<br>Victorious Victor-Philipp Negoescu"+
"<br>Perfect Paul Ruchniewicz"+
"<br>Heroic Holger Thies</small></ul>" +
"<H3><I>version:</I></H3>" +
"<ul><small>1.0</small></ul>" +
"<H3><I>special thanks to:</I></H3>" +
"<small><ul>the whole ics 1 team (for support)<br>our loved ones (for understanding)<br>the coffee-machine</small></ul>"+
"</html>", "Über Sokoban",
}
/**
* Loads a skin
* @param skinId predefined skin id
*/
public void loadSkin(final int skinId){
getGamePanel().loadSkin(skinId);
try {
getGamePanel().redraw();
} catch (final InternalFailureException e) {
System.
err.
println("Fehler: Fehler beim laden des Skin-Sets");
}
}
/**
* New game event
*/
protected void keyNewGamePressed() {
try {
owner.newGame(owner.getCurrentLevelNr());
System.
err.
println("Interner Fehler: I/O Fehler");
e.printStackTrace();
} catch (final ParameterOutOfRangeException e) {
System.
err.
println("Interner Fehler: Ungültiger Parameter!");
e.printStackTrace();
} catch (final InternalFailureException e) {
System.
err.
println("Interner Fehler: Es ist ein interner Fehler aufgetreten.");
e.printStackTrace();
System.
err.
println("Fehler: Fehlerhafter Dateiname");
System.
err.
println("Fehler: Leveldatei fehlerhaft");
}
}
/**
* Load game event
*/
protected void keyLoadGamePressed() {
try {
// show File dialog
final SokobanFileExtFilter filter = new SokobanFileExtFilter();
chooser.setFileFilter(filter);
// setzt das Verzeichnis das angezeigt werden soll (also das LEVEL
// -Verzeichnis)
chooser.
setCurrentDirectory(new File(new URI(owner.
getLevelDir())));
final int returnVal = chooser.showOpenDialog(chooser);
// Springt aus der funktion wenn abgebrochen wird
return;
.getResource(
"levels" +
File.
separatorChar
+ chooser.getSelectedFile().getName())
.toURI());
owner.loadGame(GamesSaveFile);
System.
err.
println("Fehler: Fehlerhafter Dateiname");
}
}
/**
* Save game event
*/
protected void keySaveGamePressed() {
try {
String playerName = owner.
getPlayerName();
if (playerName.isEmpty())
playerName =
JOptionPane.
showInputDialog("Bitte geben Sie Ihren Namen ein:");
// Springt aus der funktion wenn abgebrochen wird
if (playerName == null)
return;
owner.setPlayerName(playerName);
// Filename is date and player name
// replace illegal characters within the player name
final Pattern charPattern =
Pattern.
compile("[^a-zA-Z0-9äöüÃÃÃ]");
final Matcher charMatcher = charPattern.
matcher(playerName
);
playerName = charMatcher.replaceAll("-"); // replace all illegal characters with "-"
// build filename and save the file
final String fileName =
"Save_" + dt +
"_" + playerName +
".sok";
final File saveFile =
new File(new URI(owner.
getLevelDir() + fileName
));
if (owner.saveGame(saveFile))
JOptionPane.
showMessageDialog(null,
"Spielstand erfolgreich gespeichert!\nDateiname: " + fileName,
"Savegame gespeichert",
JOptionPane.
INFORMATION_MESSAGE);
System.
err.
println("Fehler: Datei konnte nicht gefunden werden!");
}
}
/**
* Dialog for getting the max. time to solve this level
*/
public int showSolveTimeDialog(){
int searchTime = 0;
try{
searchTime =
Integer.
parseInt(JOptionPane.
showInputDialog("Bitte geben Sie die maximale Suchdauer in Sekunden für die Lösung des Levels ein:"));
return searchTime;
return 0;
}
}
/**
* redraws the game panel when the round has ended
*/
public void roundEnd
(final boolean hasMoved,
final String playerName
){
// redraw game panel
try {
getGamePanel().redraw();
} catch (final InternalFailureException e) {
System.
err.
println("Interner Fehler: Fehler beim Zeichnen des Spielfelds");
}
}
/**
* level loader event
*/
public void notifyLevelLoaded(final int width, final int height) throws ParameterOutOfRangeException, InternalFailureException{
super.notifyLevelLoaded(width, height);
playBackgroundSound(); // start playing background music
// start thread for updating StatusBar
infoText = ""; // reset info text
public void run() {
while(!owner.getGameLevel().isSolved()){
updateStatusBar(); // update status bar as long as level is not solved
try {
Thread.
sleep(200); // update every 0.3 seconds
System.
err.
println("Interner Fehler: Thread konnte nicht manipuliert werden, da er bereits beendet wurde.");
}
}
}
}.start();
}
/**
* let the user type his name to insert a new highscore
* @param place winning place
*/
public void newHighscore(final int place) {
try {
sndEngine.stopSound("backgroundSound");
sndEngine.playSound("levelCompleted", "sounds/finish.mp3", SoundEngine.SND_ASYNC);
// System.out.println("Herzlichen Glückwunsch! Ein neuer Highscore! Sie belegen Platz " + place);
if ((owner.getPlayerName() == null) || (owner.getPlayerName().isEmpty()))
owner.
setPlayerName(JOptionPane.
showInputDialog("Herzlichen Glückwunsch - ein neuer Highscore!\n\nSie haben Platz " +
String.
valueOf(place
) +
" der Highscoreliste belegt!\nBitte geben Sie Ihren Namen ein:"));
else
JOptionPane.
showMessageDialog(null,
"Herzlichen Glückwunsch - ein neuer Highscore!\n\n" + owner.
getPlayerName() +
", Sie haben Platz " +
String.
valueOf(place
) +
" der Highscoreliste belegt!",
"Ein neuer Highscore",
JOptionPane.
INFORMATION_MESSAGE);
} catch (final InternalFailureException e1) {
System.
err.
println("Interner Fehler: Fehler beim Eintragen des Highscores");
}
}
/**
* resize the window when the panel is resized
* @param width new width of panel
* @param height new height of panel
*/
public void panelResized(final int width, int height){
// Add height of menubar and statusbar to panel's size
height += this.getJMenuBar().getHeight() + statusBar.getHeight();
// resize window
this.setSize(width, height);
}
}