Rogue: Savage Rats, a retro-themed dungeon crawler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rogue-savage-rats/src/mightypork/rogue/App.java

362 lines
7.5 KiB

package mightypork.rogue;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.util.Locale;
import java.util.logging.Level;
import javax.swing.JOptionPane;
import mightypork.gamecore.SlickLogRedirector;
import mightypork.gamecore.audio.SoundSystem;
import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.control.GameLoop;
import mightypork.gamecore.control.bus.EventBus;
import mightypork.gamecore.control.bus.events.*;
import mightypork.gamecore.control.interf.Destroyable;
import mightypork.gamecore.control.interf.Updateable;
import mightypork.gamecore.gui.screens.ScreenRegistry;
import mightypork.gamecore.input.InputSystem;
import mightypork.gamecore.input.KeyStroke;
import mightypork.gamecore.input.Keys;
import mightypork.gamecore.render.DisplaySystem;
import mightypork.rogue.events.ActionRequest;
import mightypork.rogue.events.ActionRequest.RequestType;
import mightypork.rogue.screens.test_bouncyboxes.ScreenTestBouncy;
import mightypork.rogue.screens.test_cat_sound.ScreenTestCat;
import mightypork.rogue.screens.test_font.ScreenTestFont;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LogInstance;
/**
* Main class
*
* @author MightyPork
*/
public class App implements AppAccess {
/** instance pointer */
private static App inst;
// modules
private InputSystem inputSystem;
private DisplaySystem displaySystem;
private static SoundSystem soundSystem;
private EventBus eventBus;
private GameLoop mainLoop;
private ScreenRegistry screens;
/**
* @param args
*/
public static void main(String[] args)
{
Config.init();
Config.save();
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
inst = new App();
try {
inst.start();
} catch (final Throwable t) {
onCrash(t);
}
}
/**
* Start the application
*/
private void start()
{
initialize();
Log.i("Starting main loop...");
// open first screen
mainLoop.start();
}
/**
* Handle a crash
*
* @param error
*/
public static void onCrash(Throwable error)
{
if (Log.ready()) {
Log.e("The game has crashed!", error);
} else {
System.err.println("The game has crashed!");
error.printStackTrace();
}
if (inst != null) inst.shutdown();
}
@Override
public void shutdown()
{
Log.i("Shutting down subsystems...");
if (bus() != null) {
bus().send(new DestroyEvent());
bus().destroy();
}
Log.i("Terminating...");
System.exit(0);
}
public void initialize()
{
// to get dot instead of comma in floats
Locale.setDefault(Locale.ENGLISH);
/*
* Lock working directory
*/
initLock();
/*
* Setup logging
*/
final LogInstance log = Log.create("runtime", Paths.LOGS, 10);
log.setFileLevel(Level.WARNING);
log.setSysoutLevel(Level.ALL);
log.enable(Config.LOGGING_ENABLED);
log.enableSysout(Config.LOG_TO_STDOUT);
org.newdawn.slick.util.Log.setLogSystem(new SlickLogRedirector(log));
Log.f1("Initializing subsystems...");
/*
* Event bus
*/
Log.f2("Initializing Event Bus...");
eventBus = new EventBus();
eventBus.detailedLogging = true;
initChannels();
/*
* Display
*/
Log.f2("Initializing Display System...");
displaySystem = new DisplaySystem(this);
displaySystem.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
displaySystem.setTargetFps(Const.FPS_RENDER);
/*
* Audio
*/
Log.f2("Initializing Sound System...");
soundSystem = new SoundSystem(this);
soundSystem.setMasterVolume(1);
/*
* Input
*/
Log.f2("Initializing Input System...");
inputSystem = new InputSystem(this);
setupGlobalKeystrokes();
/*
* Prepare main loop
*/
Log.f1("Preparing game systems...");
screens = new ScreenRegistry(this);
mainLoop = new MainLoop(this, screens);
/*
* Load resources
*/
Log.f1("Loading resources...");
Res.load(this);
/*
* Screen registry
*/
Log.f2("Initializing screens...");
initScreens();
}
private void initScreens()
{
Log.f3("Registering game screens...");
screens.add(new ScreenTestBouncy(this));
screens.add(new ScreenTestCat(this));
screens.add(new ScreenTestFont(this));
screens.showScreen("test.bouncy");
}
private void initChannels()
{
Log.f3("Registering channels...");
// framework events
bus().addChannel(DestroyEvent.class, Destroyable.class);
bus().addChannel(UpdateEvent.class, Updateable.class);
// input events
bus().addChannel(ScreenChangeEvent.class, ScreenChangeEvent.Listener.class);
bus().addChannel(KeyboardEvent.class, KeyboardEvent.Listener.class);
bus().addChannel(MouseMotionEvent.class, MouseMotionEvent.Listener.class);
bus().addChannel(MouseButtonEvent.class, MouseButtonEvent.Listener.class);
// control events
bus().addChannel(ScreenRequestEvent.class, ScreenRequestEvent.Listener.class);
bus().addChannel(ResourceLoadRequest.class, ResourceLoadRequest.Listener.class);
bus().addChannel(ActionRequest.class, ActionRequest.Listener.class);
bus().addChannel(MainLoopTaskRequest.class, MainLoopTaskRequest.Listener.class);
}
private void setupGlobalKeystrokes()
{
Log.f3("Setting up hot keys...");
// Go fullscreen
input().bindKeyStroke(new KeyStroke(Keys.KEY_F11), new Runnable() {
@Override
public void run()
{
bus().send(new ActionRequest(RequestType.FULLSCREEN));
}
});
// Take screenshot
input().bindKeyStroke(new KeyStroke(Keys.KEY_F2), new Runnable() {
@Override
public void run()
{
bus().send(new ActionRequest(RequestType.SCREENSHOT));
}
});
// Exit
input().bindKeyStroke(new KeyStroke(Keys.KEY_LCONTROL, Keys.KEY_Q), new Runnable() {
@Override
public void run()
{
bus().send(new ActionRequest(RequestType.SHUTDOWN));
}
});
}
private void initLock()
{
if (!Config.SINGLE_INSTANCE) return;
if (!lockInstance()) {
System.err.println("Working directory is locked.\nOnly one instance can run at a time.");
//@formatter:off
JOptionPane.showMessageDialog(
null,
"The game is already running.",
"Instance error",
JOptionPane.ERROR_MESSAGE
);
//@formatter:on
shutdown();
return;
}
}
private static boolean lockInstance()
{
final File lockFile = new File(Paths.WORKDIR, ".lock");
try {
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run()
{
try {
fileLock.release();
randomAccessFile.close();
lockFile.delete();
} catch (final Exception e) {
System.err.println("Unable to remove lock file.");
e.printStackTrace();
}
}
});
return true;
}
} catch (final Exception e) {
System.err.println("Unable to create and/or lock file.");
e.printStackTrace();
}
return false;
}
/**
* @return sound system of the running instance
*/
@Override
public SoundSystem snd()
{
return soundSystem;
}
/**
* @return input system of the running instance
*/
@Override
public InputSystem input()
{
return inputSystem;
}
/**
* @return display system of the running instance
*/
@Override
public DisplaySystem disp()
{
return displaySystem;
}
/**
* @return event bus
*/
@Override
public EventBus bus()
{
return eventBus;
}
}