/**************************************************************************************
* Mythos.java
* Runs the game
*
* Primary game "container" is the GameMap.
GameMap contains the layout(Cells), Terrain objects (used by all cells in the map), Actor objects, Field(reactive but not active) objects.
When a new Game is started, the all GameMaps are created as "shell" maps - they are given basic terrain info, exit data and assigned any fixed, custom content, but have no layout, monsters, or items.
The Vault stores static base information for generating Actors, Items, etc. The vault performs random generation and is static (information taken from data files)
GameWorld is the top container that is serialized/unserialized for save/load actions. It is the state model that encapsulates everything.
GameWorld contains:
-All GameMap objects
-All global "world" data - Quest states, the player object, etc
-Exit data. Exits are paired up by a unqiue (for the pair) LinkID number so that when something takes an exit, the GameWorld can take the exit's GameMap and LinkID and determine both the GameMap to load as well as the cell to place them on.
When changing maps, adjacent actors may come with the player, and nearby followers are also coming through. Actors remaining on the level receive full healing/curing and either return to their fixed location or are randomly placed.
Actors switching maps join an entrance queue. The player is placed immediately, and as many creatures from the entrance queue will be placed as will fit in the adjacent cells to the player. Remaining creatures will be placed as these spaces become available.
The game architecture is as follows:
-Mythos: Starts and updates the GUI, decides what to do with player input
-GameWorld: the current game state
-GameWindow: the GUI. Has drawFoo() functions that are called by the main thread to update the screen with the current map, player data, target data, message log, etc. The output side of the GUI is passive, not changing until told to do so. The input side is active, passing player commands to the handler function.
-RNG, the RNG.
Handler Functions: These methods of the main class determine what to do with the response from the user. There is a state variable that determines which handler is currently in use, and a parent handler which is always called by the GUI that uses this state variable to determine which handler reacts. Handlers include HandleAction (used when it is the Player's turn to act), HandleMenu (when there is a menu or text display open), HandleCursor (for looking, targeting)
DISPLAYING TEXT
-Panel to draw on (fullPanel to do the whole window, playPanel to just do the play area window, still showing player stats, logs, etc.
-Title
-Array of paragraphs (ColoredString[])
-first line displayed (int)
-last possible first line (int: array size - number of displayed lines on screen, min 1)
-When player goes up/down move screen up down by one, with pageup/space & pagedown shift first line and redraw the screen
DISPLAYING MENU
-Panel to draw on (fullPanel to do the whole window, playPanel to just do the play area window, still showing player stats, logs, etc.
-Title
-Instructions/general remarks at top of screen
-Array of choices to be shown (MenuItem[])
-whether to show a details panel (boolean)
-current choice (if showing details).
Details is basically a menu itself - it has title, remarks, array of MenuChoices
-on cancel, on choose commands (ints, used to navigate back/forwards, if there is a submenu that is forward command, if a target is needed
GENERAL PLAY
-Use keys to move/melee/switch. Shift+keys does forced action instead of moving (force attack1, close doors, etc)
-Player has ability slots bound to the number keys,-,=,`. These are shown, along with current pools, attack1/defense, etc.
-Speed has five settings, and mostly only applies to moving. Everything else takes one turn.
-Towns/bases are used to train skills / guild promotions.
-Player finds items and
*************************************************************************************/
package creid.mythos.engine;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JFileChooser;
import creid.mythos.ui.GameUI;
public class Mythos implements KeyListener
{
//Constants/////////////////////////////////////////////////////////////////////////
//Title
public static final String VERSION = "0.5.7.6";
public static final String TITLE = "Mythos " + VERSION;
//Dev or release
public static final boolean RELEASE = false;
//Debug settings
public static final boolean DEBUG_MAP = false;
public static final boolean DEBUG_NO_MOB_GEN = false;
public static final boolean DEBUG_AUTO_CHARGEN = false;
//Help context
public static final int HELP_PLAYER_TURN = 0;
//Commands
public static final int NEW_GAME = KeyEvent.VK_N;
public static final int LOAD_GAME = KeyEvent.VK_R;
public static final int EXIT_QUIT = KeyEvent.VK_Q;
public static final int EXIT_ESCAPE = KeyEvent.VK_ESCAPE;
public static final int ANY_KEY = 0;
public static final int MAIN_MENU = 1;
public static final int PLAYER_ACTION = 2;
public static final int CONFIRM = 3;
public static final int TEXT_ENTRY = 4;
public static final int MENU = 5;
public static final int DIRECTION = 6;
//Move commands
public static final int MOVE_N = KeyEvent.VK_K;
public static final int MOVE_N2 = KeyEvent.VK_UP;
public static final int MOVE_N3 = KeyEvent.VK_NUMPAD8;
public static final int MOVE_S = KeyEvent.VK_J;
public static final int MOVE_S2 = KeyEvent.VK_DOWN;
public static final int MOVE_S3 = KeyEvent.VK_NUMPAD2;
public static final int MOVE_E = KeyEvent.VK_L;
public static final int MOVE_E2 = KeyEvent.VK_RIGHT;
public static final int MOVE_E3 = KeyEvent.VK_NUMPAD6;
public static final int MOVE_W = KeyEvent.VK_H;
public static final int MOVE_W2 = KeyEvent.VK_LEFT;
public static final int MOVE_W3 = KeyEvent.VK_NUMPAD4;
public static final int MOVE_NW = KeyEvent.VK_Y;
public static final int MOVE_NW2 = KeyEvent.VK_NUMPAD7;
public static final int MOVE_NE = KeyEvent.VK_U;
public static final int MOVE_NE2 = KeyEvent.VK_NUMPAD9;
public static final int MOVE_SW = KeyEvent.VK_B;
public static final int MOVE_SW2 = KeyEvent.VK_NUMPAD1;
public static final int MOVE_SE = KeyEvent.VK_N;
public static final int MOVE_SE2 = KeyEvent.VK_NUMPAD3;
public static final int WAIT = KeyEvent.VK_NUMPAD5;
public static final int WAIT2 = KeyEvent.VK_PERIOD;
public static final int OPERATE = KeyEvent.VK_O;
public static final int GROUND_ACT = KeyEvent.VK_G;
public static final int SWAP_WEAPONS = KeyEvent.VK_S;
public static final int THROW = KeyEvent.VK_T;
public static final int FIRE = KeyEvent.VK_F;
public static final int RELOAD = KeyEvent.VK_R;
//Misc Action commands
public static final int EQUIPMENT = KeyEvent.VK_E;
public static final int INVENTORY = KeyEvent.VK_I;
public static final int NEXT_TARGET = KeyEvent.VK_TAB;
//Meta/UI commands
public static final int LOOK = KeyEvent.VK_X;
public static final int SET_TARGET = KeyEvent.VK_T;
public static final int SHOW_HELP = KeyEvent.VK_F1;
public static final int SHOW_HELP2 = KeyEvent.VK_SLASH; //? when shifted
public static final int SHOW_OLD_MESSAGES = KeyEvent.VK_P; //? when shifted
public static final int CONFIRM_YES = KeyEvent.VK_Y;
public static final int CONFIRM_NO = KeyEvent.VK_N;
public static final int CONFIRM_DEFAULT = KeyEvent.VK_ENTER;
public static final int CONFIRM_DEFAULT2 = KeyEvent.VK_SPACE;
public static final int CANCEL = KeyEvent.VK_ESCAPE;
public static final int BACKSPACE = KeyEvent.VK_BACK_SPACE;
public static final int BACKSPACE2 = KeyEvent.VK_DELETE;
public static final int SAVE_AND_QUIT = KeyEvent.VK_S;
public static final int QUIT_SUICIDE = KeyEvent.VK_Q;
public static final int ANIMATION_DELAY = 150; //Delay in milliseconds between animation steps
//Menu constants
public static final int MENU_CANCELLED = -1;
public static final int MENU_DETAIL_MULT = 1000;
public static final boolean NUMBERED_MENU = true;
public static final boolean LETTERED_MENU = false;
public static final String MENU_HEADER_SEPARATOR = "============================================================================";
public static final String MENU_INSTRUCTIONS = "Press the letter in parentheses to select, or Escape to cancel";
public static final String MENU_INSTRUCTIONS_NUM = "Press the number in parentheses to select, or Escape to cancel";
public static final String MENU_INSTRUCTIONS_CONFIRM = "Press Enter to confirm, any other key to cancel";
public static final int[] SIMPLE_CONFIRM = { CONFIRM_YES, CONFIRM_NO };
public static final String BIRTH_MENU_HEADER = "Choose a background. Your background determines starting attributes and equipment";
public static final String INVENTORY_HEADER = "Select an item to view, use, or drop it.";
public static final String EQUIPMENT_HEADER = "Select a slot to wear something or to take off the currently worn item";
public static final String PICK_EQUIP_HEADER = "Select an item to wield or wear";
public static final String THROW_HEADER = "Select an item to throw";
public static final int INV_DROP = KeyEvent.VK_D;
public static final int INV_USE = KeyEvent.VK_U;
public static final String LOOK_INSTRUCTIONS = "Use arrow/WASD/numpad keys to move cursor, 't' to target, ESC to quit";
//Attributes////////////////////////////////////////////////////////////////////////
private static Random randomizer;
public static MessageBuffer logger;
//Game World
private GameWorld game;
//static Entity lists built from Data files:
public static Actor[] Backgrounds;
public static Cell[] TerrainTypes;
public static Actor[] CreatureTypes;
public static Item[] ItemTypes;
public static MapSpecial[] MapSpecials;
//GUI
private GameUI gui;
//Input stuff
private KeyEvent input;
private boolean shifted;
private JFileChooser chooser;
//Marks the end of a gaming session. Can't just use isDead(), because we can save and quit.
boolean quitting;
public static boolean winner;
//Constructors//////////////////////////////////////////////////////////////////////
public Mythos()
{
winner = false;
randomizer = new Random();
logger = new MessageBuffer();
//Initialize Display
gui = new GameUI(TITLE);
gui.setFocusable(true);
if(!gui.hasFocus())
gui.requestFocus();
gui.addKeyListener(this);
new Thread(gui).start();
game = null; //Will be defined later
input = null;
chooser = new JFileChooser(System.getProperty("user.home") + File.separator + "MythosSavedGames");
//Initialize entity type arrays
TerrainTypes = FileHandler.loadTerrainTypes();
ItemTypes = FileHandler.loadItems();
Backgrounds = FileHandler.loadBackgrounds();
CreatureTypes = FileHandler.loadActors();
MapSpecials = FileHandler.loadSpecialRooms();
quitting = true;
}
//Methods///////////////////////////////////////////////////////////////////////////
public GameWorld getGame()
{
return game;
}
/**
* Get the title page
* @return title text
*/
private String[] getTitleText()
{
String[] text = FileHandler.loadTextFromFile(FileHandler.TITLE_PAGE_FILE, false);
//Add in the version number
for (int i = 0; i < text.length; i++)
if (text[i].contains("%VERSION%"))
text[i] = text[i].replace("%VERSION%", VERSION);
return text;
}
/**
* get help text from file
* @param context - what help file to show
* @return text
*/
private String[] getHelpText(int context)
{
//TODO: Load different help files in different situations
String[] text = FileHandler.loadTextFromFile(FileHandler.MAIN_HELP_PAGE_FILE, false);
//Add in the version number
for (int i = 0; i < text.length; i++)
if (text[i].contains("%VERSION%"))
text[i] = text[i].replace("%VERSION%", VERSION);
return text;
}
/**
* Start a game or exit the program
* @return game state
*/
public void mainMenu()
{
int choice = 0;
boolean loop = true;
while (loop)
{
logger.clearBuffer();
gui.setQuittingTime(false);
gui.hideStatus();
gui.showTitle(getTitleText());
//Wait for player input
choice = getInputCode(MAIN_MENU);
switch(choice)
{
case NEW_GAME:
newGame();
break;
case LOAD_GAME:
loadGame();
break;
case EXIT_ESCAPE:
case EXIT_QUIT:
loop = false;
break;
}
//Quit all the way if asked to
if (gui.isQuittingTime())
loop = false;
}
}
static String getSaveDirectoryPath()
{
//Warning: This property may not work consistently in older Windows systems
//TODO: At least add check for consistency in Windows XP. Maybe just use a C:\mythossavedgames folder there?
return System.getProperty("user.home") + File.separator + "MythosSavedGames";
}
private String getSaveFileName()
{
return getSaveDirectoryPath() + File.separator + game.getPC().getName(Entity.INDEFINITE) + "." + VERSION + ".save";
}
/**
* removeSave
* delete the save file
*/
private void removeSave()
{
File save = new File(getSaveFileName());
if (save.exists())
{
if(!save.delete())
{
Mythos.logger.errorLog("Error handling save file!");
}
}
}
/**
* Save the current game state
*/
public boolean saveGame()
{
File saveDirectory = new File(getSaveDirectoryPath());
if (!saveDirectory.exists() && !saveDirectory.mkdir())
{
logger.logMessage("Failed to find/create savegame directory! Save aborted!");
return false;
}
File save = new File(getSaveFileName());
//Create the file if it doesn't exist
if (!save.exists())
{
try
{
save.createNewFile();
} catch (IOException e)
{
logger.logMessage("Failed to create save file! Save aborted!");
e.printStackTrace();
return false;
}
}
//Open the file for writing
ObjectOutputStream out;
try
{
out = new ObjectOutputStream(new FileOutputStream(save));
out.writeObject(game);
out.close();
} catch (IOException e)
{
logger.logMessage("Failure saving game! Save aborted!");
e.printStackTrace();
return false;
}
//If we get here, everything worked!
return true;
}
/**
* load a saved game stat
*/
public void loadGame()
{
int returnVal = chooser.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION)
{
File file = chooser.getSelectedFile();
try
{
FileInputStream fin = new FileInputStream(file);
ObjectInputStream oin = new ObjectInputStream(fin);
game = (GameWorld) oin.readObject();
fin.close();
oin.close();
//Remove the old save file
removeSave();
quitting = false;
if (game.getPC().hasProperty("WINNER"))
winner = true;
else
winner = false;
startGame();
} catch (IOException | ClassNotFoundException e)
{
logger.logMessage("Error: Unable to load save game!");
e.printStackTrace();
}
}
}
/**
* quit the current game
*/
public void quitGame()
{
//If we have a game in progress
if (game != null)
postmortem();
else
saveGame();
}
/**
* showInventory
* @return whether the player used their turn
*/
private boolean showInventory()
{
Item[] inventory = game.getPC().listInventory();
int response = MENU_CANCELLED;
boolean tookTime = false;
do
{
response = showMenu(INVENTORY_HEADER, getMenuChoices(inventory), LETTERED_MENU, buildChoiceDetails(inventory), "Press 'u' to use or 'd' to drop", new int[]{ KeyEvent.VK_U, KeyEvent.VK_D});
int action = response / MENU_DETAIL_MULT;
int which = response % MENU_DETAIL_MULT;
//What did the player decide to do?
switch (action)
{
case INV_DROP:
boolean dropAll = true;
//Is this a stack of items?
if (game.getPC().seeItem(which).getAmount() > 1)
{
int amount = game.getPC().seeItem(which).getAmount();
amount = Integer.parseInt(getText("Drop how many(" + amount + ")", String.valueOf(amount), true));
//Zero is a bad amount
if (amount < game.getPC().seeItem(which).getAmount())
{
dropAll = false;
}
//Drop a few
if (amount > 0)
{
Item drop = new Item(game.getPC().seeItem(which));
drop.setAmount(amount);
if (game.getPC().getLocation().getMap().placeItem(drop, game.getPC().getLocation().getX(), game.getPC().getLocation().getX(), true))
{
tookTime = true;
//Reduce the amount we still have
game.getPC().seeItem(which).setAmount(game.getPC().seeItem(which).getAmount() - amount);
//Sanity check, should never happen
if (game.getPC().seeItem(which).getAmount() < 1)
game.getPC().takeItem(which, true);
}
else
Mythos.logger.logMessage("There is no room to drop " + game.getPC().seeItem(which).getName(Entity.DEFINITE));
}
}
//If we dropped some don't go any further
if (dropAll)
{
if (game.getPC().dropItem(which))
{
tookTime = true;
response = MENU_CANCELLED;
}
else //Tell the player no
Mythos.logger.logMessage("There is no room to drop " + game.getPC().seeItem(which).getName(Entity.DEFINITE));
}
break;
case INV_USE:
Item item = game.getPC().seeItem(which);
if (item.getUse() == null)
{
Mythos.logger.logMessage("That item cannot be used");
break;
}
//Use the item
Cell target = null;
if (game.getCurrentMap().getTarget() != null)
target = game.getCurrentMap().getTarget().getLocation();
tookTime = game.getPC().useItem(which, target);
response = MENU_CANCELLED;
break;
}
} while (response != MENU_CANCELLED);
//Hide menu
gui.hideTitle();
gui.showMap(game.getCurrentMap());
return tookTime;
}
/**
* generate a new player & game from scratch
*/
public void newGame()
{
winner = false;
quitting = false;
gui.hideTitle();
//Create the player
int background;
String name;
if (DEBUG_AUTO_CHARGEN)
{
background = 0;
name = "Debugger";
}
else
{
name = getText("Name your character", "Player", false);
background = showMenu(BIRTH_MENU_HEADER, getMenuChoices(Backgrounds),
LETTERED_MENU, buildChoiceDetails(Backgrounds), "Are you sure? (y/N)", SIMPLE_CONFIRM);
}
//If user closed the window during birth, just quit
if (gui.isQuittingTime())
System.exit(0);
Player pc = new Player(name, Backgrounds[background]);
game = new GameWorld(pc);
game.changeMap(0, 0);
startGame();
}
private void startGame()
{
gui.showStatus();
gui.showMap(game.getCurrentMap());
gui.repaint();
try
{
playGame();
}
catch(Exception ex)
{
ex.printStackTrace();
if (gui.isQuittingTime())
quitting = true;
}
}
private void takeTurn(Actor actor)
{
actor.setDoubleMove();
actor.takeTurn();
//If actor can still act
if (actor.canDoubleMove())
actor.takeTurn();
}
/**
* Mythos game loop
*/
public void playGame() throws Exception
{
gui.updatePCStatus(game.getPC());
game.getCurrentMap().updateFOV(game.getPC().getLocation());
Mythos.logger.setGui(gui);
Mythos.logger.logTransientMessage("Welcome to Mythos!");
Iterator<Actor> actors = null;
while(!quitting)
{
//Process the player and allies
actors = game.getCurrentMap().getPcs().iterator();
while (!quitting && actors.hasNext())
{
Actor pc = actors.next();
if (pc instanceof Player)
{
//loop until the player uses their turn
while(playerCommand(getInputCode(ANY_KEY)));
//Quit if player is dead
if (pc.isDead())
quitting = true;
else
{
game.getCurrentMap().updateFOV(game.getPC().getLocation());
game.getCurrentMap().updateDMap(GameMap.PC_PATH);
}
processProjections();
}
else
{
takeTurn(pc);
processProjections();
}
gui.updateTarget();
gui.updatePCStatus(game.getPC());
gui.repaint();
}
//Place following creatures if we can
game.getCurrentMap().placeFollowers();
gui.repaint();
//Stop if player closed window or died
if (gui.isQuittingTime() ||game.getPC().isDead())
break;
game.getCurrentMap().updateDMap(GameMap.MOB_PATH);
game.getCurrentMap().updateDMap(GameMap.SMART_MOB_PATH);
//Now process the mobs
actors = game.getCurrentMap().getMobs().iterator();
while(!quitting && actors.hasNext())
{
takeTurn(actors.next());
processProjections();
//Quit if the player died
if (game.getPC().isDead())
quitting = true;
gui.updatePCStatus(game.getPC());
gui.updateTarget();
gui.repaint();
}
//Update mob DMap for allies
game.getCurrentMap().updateDMap(GameMap.ALLY_PATH);
game.getCurrentMap().updateDMap(GameMap.SMART_ALLY_PATH);
}
//Let player see the screen before quitting.
if (game.getPC().isDead())
{
postmortem();
}
}
/**
* postmortem: show the results of our game, finished or not
*/
public void postmortem()
{
if (winner)
logger.logMessage("You are a WINNER! Press any key to return to the menu");
else
{
logger.logMessage("Game over. Press any key to return to the menu");
}
getInputCode(ANY_KEY);
}
/*
* processProjections
* Run any waiting projections
*/
private void processProjections()
{
while(game.getCurrentMap().projectionsToRun())
{
Projection p = game.getCurrentMap().getNextProjection();
while (p.canProject())
{
p.project();
//Animate if projection is visible
if (p.isSeen())
{
gui.updatePCStatus(game.getPC());
gui.updateTarget();
gui.repaint();
//Pause so we can see the projection
try
{
Thread.sleep(ANIMATION_DELAY);
} catch (InterruptedException e) {}
}
}
//Cleanup projection animation
p.cleanup();
}
}
/**
* Player decides to end game
* @return whether the player went through with it
*/
private boolean suicide()
{
if (winner) //Player retires, not commits suicide
{
quitting = true;
return false;
}
else
//Get confirmation
if (confirmCommand("Give up and end your game permanently? (Use 'S' to save and quit instead)", false))
if (confirmCommand("Are you sure?", false)) //Are you really sure?
{
quitting = true;
return false;
}
return true;
}
/**
* display the help text
*/
private void showHelp()
{
//Show the help file until the player presses a key
gui.showTitle(getHelpText(HELP_PLAYER_TURN));
getInputCode(ANY_KEY);
gui.hideTitle();
gui.showStatus();
gui.showMap(game.getCurrentMap());
gui.repaint();
}
/**
* display message history
*/
private void showMessages()
{
//Show the help file until the player presses a key
gui.showTitle(logger.getHistory());
getInputCode(ANY_KEY);
gui.hideTitle();
gui.showStatus();
gui.showMap(game.getCurrentMap());
gui.repaint();
}
/**
* getMenuChoices
* Helper for showMenu()
* Build a list of menu choices from an array of entities
* @param entities
* @return
*/
private String[] getMenuChoices(Entity[] entities)
{
String[] choices = new String[entities.length];
for (int i = 0; i < choices.length; i++)
{
choices[i] = entities[i].getName(Entity.INDEFINITE);
}
return choices;
}
private String[][] buildChoiceDetails(Entity[] entities)
{
String[][] details = new String[entities.length][];
for (int i = 0; i < details.length; i++)
{
details[i] = entities[i].getDetails();
}
return details;
}
/**
* Helper function for showMenu()
* @param menuHeader
* @param menuChoices
* @param menuStyle
* @param details
* @return
*/
private String[] buildMenuText(String menuHeader, String[] menuChoices, boolean menuStyle, int choice, String[] details, String confirmInstructions)
{
//Indices used below
int startChoices = 2;
int endChoices = startChoices + menuChoices.length;
int startDetails = endChoices + 1;
int endDetails = startDetails;
if (details != null)
endDetails += details.length;
//Determine size of string array
int arraySize = endDetails + 2;
//Prepare the text to show
String[] show = new String[arraySize];
show[0] = menuHeader;
show[1] = MENU_HEADER_SEPARATOR;
//Sanity check: don't have numbered list if > 10 items
if (menuChoices.length > 10)
menuStyle = LETTERED_MENU;
//Add in our choices
for (int i = 0; i < menuChoices.length; i++)
{
if (menuStyle == LETTERED_MENU)
show[startChoices + i] = "(" + Character.valueOf((char)(KeyEvent.VK_A + i)) + ") " + menuChoices[i];
else
show[startChoices + i] = "(" + i + ") " + menuChoices[i];
}
//And finish with some explanatory text
show[endChoices] = "";
show[endDetails] = "";
//If we are showing details, show them
if (details != null)
{
show[endChoices+1] = " " + menuChoices[choice];
for (int j = 0; j < details.length; j++)
show[startDetails + j] = details[j];
show[endDetails+1] = confirmInstructions;
}
else if (menuStyle == NUMBERED_MENU)
show[endDetails+1] = MENU_INSTRUCTIONS_NUM;
else
show[endDetails+1] = MENU_INSTRUCTIONS;
return show;
}
/**
* showMenu
* Display a menu to the user and let them pick a choice
* @param menuHeader: Text displayed at top of menu
* @param menuChoices: list of lines displaying the menu options.
* @param menuStyle: preface the choices with letters or numbers.
* @param choiceDetails: String[] of text to be shown when an item is selected, in addition to a confirmation prompt. If null, no confirmation
* is offered.
* @return [users menu choice][users menu command], or MENU_CANCELLED if the user cancels out.
*/
private int showMenu(String menuHeader, String[] menuChoices, boolean menuStyle, String[][] choiceDetails, String confirmInstructions, int[] detailCommands)
{
int choice = MENU_CANCELLED;
//Show the initial choices
gui.showTitle(buildMenuText(menuHeader, menuChoices, menuStyle, MENU_CANCELLED, null, null));
boolean needsConfirm = true;
if (choiceDetails == null)
needsConfirm = false;
//Loop until we get a valid choice or the player quits
while (true)
{
if (gui.isQuittingTime())
return MENU_CANCELLED;
choice = getInputCode(MENU);
boolean valid = false;
//Validate input
if (choice == CANCEL ||
(menuStyle == NUMBERED_MENU && choice >= 0 && choice <= 9) ||
(menuStyle == LETTERED_MENU && choice >= KeyEvent.VK_A && choice <= KeyEvent.VK_Z))
valid = true;
//Check list length
if (valid)
{
//Convert to number 0-26
if (menuStyle == LETTERED_MENU && choice != CANCEL)
choice -= KeyEvent.VK_A;
//Now make sure choice is not out-of-bounds
if (choice != CANCEL && choice >= menuChoices.length)
valid = false;
}
if (choice == CANCEL || gui.isQuittingTime())
return MENU_CANCELLED;
//We have a valid choice
if (valid)
{
//Do we need to confirm the choice
if(needsConfirm)
{
//Get confirmation
gui.showTitle(buildMenuText(menuHeader, menuChoices, menuStyle, choice, choiceDetails[choice], confirmInstructions));
int choice2 = MENU_CANCELLED;
while(choice2 < 0)
{
choice2 = getInputCode(ANY_KEY);
//If cancel, go back to the menu
if (choice2 == CANCEL)
break;
//Check the choice against our list of allowed command values
boolean pass = false;
for (int i = 0; i < detailCommands.length; i++)
if (choice2 == detailCommands[i])
{
pass = true;
break;
}
if (pass)
break;
//Warn the player of invalid command
logger.logTransientMessage("Invalid command. Enter a letter in parentheses or Escape to back up");
}
//Now if we cancelled, go back and try again
if (choice2 == CANCEL || (detailCommands == SIMPLE_CONFIRM && choice2 == KeyEvent.VK_N))
{
gui.showTitle(buildMenuText(menuHeader, menuChoices, menuStyle, MENU_CANCELLED, null, null));
continue;
}
else
{
choice += MENU_DETAIL_MULT * choice2; //Add choice 2 to choice 1
break;
}
}
else //we are done
break;
}
//If we get here the user entered a bad command
logger.logTransientMessage("Invalid command. Choose a menu item or Escape to cancel");
}
//Clear confirmation for simple conf menus
if (detailCommands == SIMPLE_CONFIRM)
choice = choice % MENU_DETAIL_MULT;
return choice;
}
/**
* change to another map
* @param mapID
* @param exitID
*/
private boolean changeMaps()
{
if (game.getPC().getLocation() instanceof Exit)
{
Exit ex = ((Exit)game.getPC().getLocation());
int tMap = ex.getTargetMap();
int tExit = ex.getTargetExit();
game.changeMap(tMap, tExit);
//Update Dmaps
for (int dm = 0; dm < GameMap.DMAP_ARRAY_SIZE; dm++)
game.getCurrentMap().updateDMap(dm);
gui.showMap(game.getCurrentMap());
return true;
}
else
{
logger.logMessage(game.getPC().getName() + " sees no exit here");
return false;
}
}
private boolean operateTerrain(int dx, int dy)
{
Cell targetCell;
//If dx=dy=0, we are wanting "smart" behavior - find a direction for us, or ask if we're not sure
if (dx == 0 && dy == 0)
{
//Get the adjacent cells to choose from
LinkedList<Cell> aoc = new LinkedList<Cell>();
for (int x = -1; x < 2; x++)
{
for (int y = -1; y < 2; y++)
{
//Skip start cell
if (x == 0 && y == 0)
continue;
Cell c = game.getCurrentMap().getRelativeLocation(game.getPC().getLocation(), x, y);
if (c.isOperable())
aoc.add(c);
}
}
//If there are no operable cells, abort
if (aoc.size() == 0)
{
logger.logTransientMessage("You see nothing to operate here");
}
else if (aoc.size() == 1) //If there is only one, then choose it
{
dx = aoc.getFirst().getX() - game.getPC().getLocation().getX();
dy = aoc.getFirst().getY() - game.getPC().getLocation().getY();
}
else //Ask the player for a direction
{
int direction = getDirection(null);
dx = (direction/10) - 1;
dy = (direction%10) - 1;
}
}
//Now Operate the idicated cell
dx += game.getPC().getLocation().getX();
dy += game.getPC().getLocation().getY();
targetCell = game.getCurrentMap().getLocation(dx, dy);
if (targetCell == null)
{
logger.errorLog("operateTerrain: Received null from game.getCurrentMap().getLocation(" + dx + "," + dy +")");
return false;
}
if (targetCell.getOperation() == Cell.NO_OP)
{
Mythos.logger.logTransientMessage("You see nothing to operate there");
return false;
}
else return game.getPC().operateTerrain(targetCell);
//return targetCell.operate(1);
}
private boolean reloadCommand()
{
//Must have a gun to shoot
if (game.getPC().getEquipment(Player.MAINDHAND_SLOT) == null || game.getPC().getEquipment(Player.MAINDHAND_SLOT).getType() != Item.I_GUN)
{
Mythos.logger.logTransientMessage("You have nothing to shoot with!");
return false;
}
Item weapon = game.getPC().getEquipment(Player.MAINDHAND_SLOT);
//Make sure item has ammo property
if (!weapon.hasProperty("ammo"))
{
Mythos.logger.errorLog("Item " + weapon.getName(Entity.DEFINITE) + " is of type gun but has no ammo property!");
return false;
}
//Get example ammo item
Item check = Mythos.ItemTypes[weapon.getProperty("ammo")];
//Get inventory
Item[] inventory = game.getPC().listInventory();
//Cycle through the inventory to find the appropriate ammo
int match = -1;
for (int i = 0; i < inventory.length; i++)
{
if (inventory[i].compareTo(check) == 0)
{
match = i;
break;
}
}
//Is there any ammo?
if (match < 0)
{
Mythos.logger.logTransientMessage("No ammo for " + weapon.getName(Entity.DEFINITE) + "!");
return false;
}
else //load the ammo
{
int amount = weapon.getProperty("capacity") - weapon.getProperty("loaded");
if (amount > inventory[match].getAmount())
amount = inventory[match].getAmount();
if (amount < 1)
{
Mythos.logger.logTransientMessage("No ammo for " + weapon.getName(Entity.DEFINITE) + "!");
return false;
}
//Take from backpack
if (amount == game.getPC().seeItem(match).getAmount())
game.getPC().takeItem(match, true);
else
game.getPC().seeItem(match).setAmount(game.getPC().seeItem(match).getAmount() - amount);
String ammoName = check.getPluralName();
if (amount == 1)
ammoName = check.getSingularName();
weapon.setProperty("loaded", weapon.getProperty("loaded") + amount);
logger.logMessage(game.getPC().getName(Entity.DEFINITE) + " loads " + amount + " " + ammoName + " into " + weapon.getName(Entity.DEFINITE));
return true;
}
}
private Cell getTarget()
{
//TODO: If we don't have a target, use a look-targeting command
if (game.getCurrentMap().getTarget() == null)
{
lookAround();
if (game.getCurrentMap().getTarget() == null)
return null;
}
return game.getCurrentMap().getTarget().getLocation();
}
/**
* THrow an item
* @return whether the player took his turn (true) or cancelled
*/
private boolean throwCommand()
{
Item[] inventory = game.getPC().listInventory();
String[] choices = new String[inventory.length];
for (int i = 0; i < choices.length; i++)
choices[i] = inventory[i].getThrowName();
//THROW_HEADER
int choice = showMenu(THROW_HEADER, choices, LETTERED_MENU, null, null, null);
//Hide menu
gui.hideTitle();
gui.showMap(game.getCurrentMap());
//Cancel if asked
if (choice == MENU_CANCELLED)
return false;
//Now pick a target if we don't have one
Cell target = getTarget();
//User cancelled
if (target == null)
{
logger.logTransientMessage("Cancelled");
return false;
}
//Throw at our target
return game.getPC().throwItem(choice, target);
}
/**
* shoot
* Player shoots their weapon
* @return
*/
private boolean shootCommand()
{
//Must have a gun to shoot
if (game.getPC().getEquipment(Player.MAINDHAND_SLOT) == null || game.getPC().getEquipment(Player.MAINDHAND_SLOT).getType() != Item.I_GUN)
{
Mythos.logger.logTransientMessage("You have nothing to shoot with!");
return false;
}
Item weapon = game.getPC().getEquipment(Player.MAINDHAND_SLOT);
//Make sure item has ammo property
if (!weapon.hasProperty("ammo"))
{
Mythos.logger.errorLog("Item " + weapon.getName(Entity.DEFINITE) + " is of type gun but has no ammo property!");
return false;
}
//Gun must have ammo to shoot
if (!weapon.hasProperty("loaded"))
{
Mythos.logger.logTransientMessage("You are out of ammo!");
return false;
}
//User cancelled
Cell target = getTarget();
if (target == null)
{
logger.logTransientMessage("Cancelled");
return false;
}
//Create shot
//TODO: Handle higher rates of fire
Item shot = new Item(ItemTypes[weapon.getProperty("ammo")]);
shot.setAmount(1);
weapon.setProperty("loaded", weapon.getProperty("loaded") - shot.getAmount());
shot.setProperty("firepower", weapon.getProperty("firepower") + game.getPC().getProperty("firepower"));
return game.getPC().shoot(shot, target);
}
/**
* main: Run the game
*/
public static void main(String[] args)
{
Mythos yarg = new Mythos();
yarg.mainMenu();
System.exit(0);
}
//Input handlers///////////////////////////////////////////////////////////
/**
* Player command: turn a keypress into an action object for the player
* @param input
* @return
*/
private boolean playerCommand(int command)
{
boolean stillPlayerTurn = true;
//Movement commands
int dx = 0;
int dy = 0;
boolean move = false;
switch(command)
{
//Movement / Melee / Operate commands
case MOVE_NW:
case MOVE_NW2:
dx--;
case MOVE_N:
case MOVE_N2:
case MOVE_N3:
dy--;
move = true;
break;
case MOVE_SE:
case MOVE_SE2:
dx++;
case MOVE_S:
case MOVE_S2:
case MOVE_S3:
dy++;
move = true;
break;
case MOVE_NE:
case MOVE_NE2:
dy--;
case MOVE_E:
case MOVE_E2:
case MOVE_E3:
dx++;
move = true;
break;
case MOVE_SW:
case MOVE_SW2:
dy++;
case MOVE_W:
case MOVE_W2:
case MOVE_W3:
dx--;
move = true;
break;
case WAIT:
case WAIT2:
move = true;
break;
//Shoot our weapon
case FIRE:
stillPlayerTurn = !shootCommand();
break;
case RELOAD:
stillPlayerTurn = !reloadCommand();
break;
//Throw an item
case THROW:
stillPlayerTurn = !throwCommand();
break;
//Open inventory
case INVENTORY:
stillPlayerTurn = !showInventory();
break;
//List equipment
case EQUIPMENT:
stillPlayerTurn = !showEquipment();
break;
//Operate terrain (open/close doors. Set off traps in your square, etc)
case OPERATE:
if (operateTerrain(0,0))
{
stillPlayerTurn = false;
}
break;
//Interact with ground: pick up item, change level, etc
case GROUND_ACT:
//Take stairs
if (game.getPC().getLocation() instanceof Exit)
changeMaps();
//Pick up item
else
{
stillPlayerTurn = !game.getPC().pickupItem();
if (stillPlayerTurn)
{
Mythos.logger.logTransientMessage(game.getPC().getName() + " sees nothing to pick up!");
}
}
break;
//look around
case LOOK:
lookAround();
stillPlayerTurn = true;
break;
//Show help text
case SHOW_HELP2:
if (!shifted)
break;
case SHOW_HELP:
showHelp();
break;
//Previous messages
case SHOW_OLD_MESSAGES:
showMessages();
//Select target
case NEXT_TARGET:
game.getCurrentMap().nextTarget();
gui.updateTarget();
gui.hideStatus();
gui.showStatus();
//gui.updatePCStatus(game.getPC());
//gui.repaint();
break;
//Save game and quit
case SAVE_AND_QUIT:
if (!shifted)
{
game.getPC().swapWeapons();
gui.updatePCStatus(game.getPC());
break;
}
case EXIT_ESCAPE:
//If player clicks close, save and quit
if (gui.isQuittingTime() || confirmCommand("Save game and quit", false))
{
//Don't quit if the save fails for some reason
if (saveGame())
{
quitting = true;
stillPlayerTurn = false;
}
}
break;
//Give up and quit without saving.
case QUIT_SUICIDE:
if (!shifted)
break;
stillPlayerTurn = suicide();
break;
default:
logger.logTransientMessage("Unknown command");
stillPlayerTurn = true;
}
//Are we moving/meleeing/operating
if (move)
{
//Don't use a turn if player tries to walk into a wall (unlike a monster, though monsters should never try that in the first place)
stillPlayerTurn = !game.getPC().move(dx, dy);
}
return stillPlayerTurn;
}
private boolean showEquipment()
{
int slot = MENU_CANCELLED;
boolean tookTime = false;
//showMenu(String menuHeader, String[] menuChoices, boolean menuStyle, String[][] choiceDetails, String confirmInstructions, int[] detailCommands)
slot = showMenu(EQUIPMENT_HEADER, game.getPC().getEquipmentMenu(), LETTERED_MENU, null, null, null);
if (slot != MENU_CANCELLED)
{
//Did the player want to un-equip something or equip something?
if (game.getPC().getEquipment(slot) == null)
{
int index = showMenu(PICK_EQUIP_HEADER, getMenuChoices(game.getPC().listInventory()), LETTERED_MENU, null, null, null);
//ON cancelling, return to equipment list
if (slot == MENU_CANCELLED)
return showEquipment();
else //equip the item
{
Item i = game.getPC().seeItem(index);
if (game.getPC().canEquip(i, slot))
{
i = game.getPC().takeItem(index, false);
tookTime = game.getPC().equipItem(i, slot);
if (tookTime)
{
Mythos.logger.logMessage(game.getPC().getName(Entity.INDEFINITE) + " " + Player.EQUIP_VERB[slot] + " " + i.getName(Entity.DEFINITE));
}
else
Mythos.logger.logMessage("That item cannot be worn there");
}
else
Mythos.logger.logMessage("That item cannot be worn there");
}
}
else //Remove selected equipment
{
Item removed = game.getPC().removeEquipment(slot);
game.getPC().stowItem(removed); //put back in inventory
Mythos.logger.logMessage(game.getPC().getName(Entity.INDEFINITE) + " " + Player.UNEQUIP_VERB[slot] + " " + removed.getName(Entity.DEFINITE));
tookTime = true;
}
}
//Hide menu
gui.hideTitle();
gui.showMap(game.getCurrentMap());
return tookTime;
}
/**
* lookAround
* Look command
*/
private void lookAround()
{
Mythos.logger.logMessage(LOOK_INSTRUCTIONS);
int keypress = EXIT_ESCAPE;
game.getCurrentMap().setHighlight(game.getPC().getLocation().getX(), game.getPC().getLocation().getY());
Mythos.logger.logMessage(game.getCurrentMap().getHighlightedCellName());
do
{
keypress = getInputCode(ANY_KEY);
boolean describe = true;
if (keypress != EXIT_ESCAPE)
{
int dx = 0;
int dy = 0;
//Highlight chosen cell
switch(keypress)
{
case MOVE_NW:
case MOVE_NW2:
dx--;
case MOVE_N:
case MOVE_N2:
case MOVE_N3:
dy--;
break;
case MOVE_SE:
case MOVE_SE2:
dx++;
case MOVE_S:
case MOVE_S2:
case MOVE_S3:
dy++;
break;
case MOVE_NE:
case MOVE_NE2:
dy--;
case MOVE_E:
case MOVE_E2:
case MOVE_E3:
dx++;
break;
case MOVE_SW:
case MOVE_SW2:
dy++;
case MOVE_W:
case MOVE_W2:
case MOVE_W3:
dx--;
break;
case WAIT:
case WAIT2:
break;
case SET_TARGET:
Cell targetCell = game.getCurrentMap().getLocation(game.getCurrentMap().getHighlightX(),
game.getCurrentMap().getHighlightY());
if (targetCell.isOccupied())
{
Actor t = targetCell.getOccupant();
boolean found = false;
for (Actor m: game.getCurrentMap().getMobs())
if (m == t) //actor is a mob, not an ally
{
found = true;
game.getCurrentMap().setTarget(t);
gui.updateTarget();
gui.hideStatus();
gui.showStatus();
keypress = EXIT_ESCAPE; //quit the look interface
gui.overwriteLastMessage("Target set");
describe = false;
break;
}
if (!found)
{
gui.overwriteLastMessage("You cannot target an ally!");
describe = false;
}
}
else
{
gui.overwriteLastMessage("There is nothing to target there");
describe = false;
}
break;
}
game.getCurrentMap().changeHighlightX(dx);
game.getCurrentMap().changeHighlightY(dy);
//Describe what we see
if (describe)
gui.overwriteLastMessage(game.getCurrentMap().getHighlightedCellName());
}
} while(keypress != EXIT_ESCAPE);
//Remove the highlighting
game.getCurrentMap().removeHighlight();
gui.repaint();
}
/**
* getText: Interactive text prompt
* @param prompt: Instructions for player
* @param defaultText: if no text is entered when player hits "Enter", use this
* @param numbersOnly: only allow numbers to be typed.
* @return
*/
private String getText(String prompt, String defaultText, boolean numbersOnly)
{
prompt += ": ";
gui.showTransientMessage(prompt + defaultText);
StringBuilder text = new StringBuilder();
while(true)
{
int code = getInputCode(TEXT_ENTRY);
if (code == CONFIRM_DEFAULT)
{
//If blank, use the default
if (text.length() == 0)
return defaultText;
else
return text.toString();
}
else if (code == CANCEL)
{
return defaultText;
}
else if (code == BACKSPACE || code == BACKSPACE2)
{
//Do nothing if no text
if (text.length() == 0)
continue;
text.deleteCharAt(text.length() - 1);
gui.showTransientMessage(prompt + text.toString());
}
else if (code >= KeyEvent.VK_A && code <= KeyEvent.VK_Z)
{
boolean alpha = false;
switch(code)
{
case KeyEvent.VK_0:
text.append('0');
break;
case KeyEvent.VK_1:
text.append('1');
break;
case KeyEvent.VK_2:
text.append('2');
break;
case KeyEvent.VK_3:
text.append('3');
break;
case KeyEvent.VK_4:
text.append('4');
break;
case KeyEvent.VK_5:
text.append('5');
break;
case KeyEvent.VK_6:
text.append('6');
break;
case KeyEvent.VK_7:
text.append('7');
break;
case KeyEvent.VK_8:
text.append('8');
break;
case KeyEvent.VK_9:
text.append('9');
break;
default:
alpha = true;
}
if (alpha && !numbersOnly)
{
switch(code)
{
case KeyEvent.VK_A:
text.append(shifted ? 'A' : 'a');
break;
case KeyEvent.VK_B:
text.append(shifted ? 'B' : 'b');
break;
case KeyEvent.VK_C:
text.append(shifted ? 'C' : 'c');
break;
case KeyEvent.VK_D:
text.append(shifted ? 'D' : 'd');
break;
case KeyEvent.VK_E:
text.append(shifted ? 'E' : 'e');
break;
case KeyEvent.VK_F:
text.append(shifted ? 'F' : 'f');
break;
case KeyEvent.VK_G:
text.append(shifted ? 'G' : 'g');
break;
case KeyEvent.VK_H:
text.append(shifted ? 'H' : 'h');
break;
case KeyEvent.VK_I:
text.append(shifted ? 'I' : 'i');
break;
case KeyEvent.VK_J:
text.append(shifted ? 'J' : 'j');
break;
case KeyEvent.VK_K:
text.append(shifted ? 'K' : 'k');
break;
case KeyEvent.VK_L:
text.append(shifted ? 'L' : 'l');
break;
case KeyEvent.VK_M:
text.append(shifted ? 'M' : 'm');
break;
case KeyEvent.VK_N:
text.append(shifted ? 'N' : 'n');
break;
case KeyEvent.VK_O:
text.append(shifted ? 'O' : 'o');
break;
case KeyEvent.VK_P:
text.append(shifted ? 'P' : 'p');
break;
case KeyEvent.VK_Q:
text.append(shifted ? 'Q' : 'q');
break;
case KeyEvent.VK_R:
text.append(shifted ? 'R' : 'r');
break;
case KeyEvent.VK_S:
text.append(shifted ? 'S' : 's');
break;
case KeyEvent.VK_T:
text.append(shifted ? 'T' : 't');
break;
case KeyEvent.VK_U:
text.append(shifted ? 'U' : 'u');
break;
case KeyEvent.VK_V:
text.append(shifted ? 'V' : 'v');
break;
case KeyEvent.VK_W:
text.append(shifted ? 'W' : 'w');
break;
case KeyEvent.VK_X:
text.append(shifted ? 'X' : 'x');
break;
case KeyEvent.VK_Y:
text.append(shifted ? 'Y' : 'y');
break;
case KeyEvent.VK_Z:
text.append(shifted ? 'Z' : 'z');
break;
}
}
gui.showTransientMessage(prompt + text.toString());
}
else
{
text.append(Character.toChars(code));
gui.showTransientMessage(prompt + text.toString());
}
}
}
/**
* getDirection: get a direction from the player
* @param prompt
* @return
*/
private int getDirection(String prompt)
{
//Default prompt
if (prompt == null)
prompt = "Which direction?";
gui.showTransientMessage(prompt);
int dx = 1;
int dy = 1;
switch(getInputCode(DIRECTION))
{
case MOVE_NW:
case MOVE_NW2:
dx--;
case MOVE_N:
case MOVE_N2:
case MOVE_N3:
dy--;
break;
case MOVE_SE:
case MOVE_SE2:
dx++;
case MOVE_S:
case MOVE_S2:
case MOVE_S3:
dy++;
break;
case MOVE_NE:
case MOVE_NE2:
dy--;
case MOVE_E:
case MOVE_E2:
case MOVE_E3:
dx++;
break;
case MOVE_SW:
case MOVE_SW2:
dy++;
case MOVE_W:
case MOVE_W2:
case MOVE_W3:
dx--;
break;
case CANCEL:
return -1;
}
return dx * 10 + dy;
}
private boolean confirmCommand(String message, boolean confirmByDefault)
{
boolean confirmation = confirmByDefault;
if (confirmation)
message += " (Y/n)?";
else
message += "(y/N)?";
gui.showTransientMessage(message);
switch(getInputCode(CONFIRM))
{
case CONFIRM_YES:
confirmation = true;
break;
case CONFIRM_NO:
case CANCEL:
confirmation = false;
break;
default:
//Leave with default confirmation
}
gui.showTransientMessage("");
return confirmation;
}
private boolean isDirectionCommand(int command)
{
//TODO: Add cancel/back
switch(command)
{
case MOVE_N:
case MOVE_N2:
case MOVE_N3:
case MOVE_S:
case MOVE_S2:
case MOVE_S3:
case MOVE_E:
case MOVE_E2:
case MOVE_E3:
case MOVE_W:
case MOVE_W2:
case MOVE_W3:
case MOVE_NW:
case MOVE_NW2:
case MOVE_NE:
case MOVE_NE2:
case MOVE_SW:
case MOVE_SW2:
case MOVE_SE:
case MOVE_SE2:
case WAIT:
case WAIT2:
return true;
}
return false;
}
//Get command
private int getInputCode(int type)
{
int code = -99;
//Wait for input
while(input == null)
{
try
{
Thread.sleep(50);
} catch (InterruptedException e) {}
if (input != null)
switch(type)
{
case CONFIRM:
if (input.getKeyCode() != CONFIRM_YES && input.getKeyCode() != CONFIRM_NO
&& input.getKeyCode() != CONFIRM_DEFAULT && input.getKeyCode() != CONFIRM_DEFAULT2)
input = null;
break;
case MENU:
//Accept only escape or alphanumeric
if (input.getKeyCode() != CANCEL && (input.getKeyCode() < KeyEvent.VK_0 || input.getKeyCode() > KeyEvent.VK_9 ) &&
(input.getKeyCode() < KeyEvent.VK_A || input.getKeyCode() > KeyEvent.VK_Z))
input = null;
break;
case DIRECTION:
if (!isDirectionCommand(input.getKeyCode()))
input = null;
break;
case ANY_KEY:
break;
}
//If player closed the window, exit
if (gui.isQuittingTime())
{
//game.getPC().quitting = true;
return EXIT_ESCAPE;
}
}
code = input.getKeyCode();
input = null;
return code;
}
@Override
public void keyPressed(KeyEvent arg0)
{
if (arg0.getKeyCode() == KeyEvent.VK_SHIFT)
{
if (!shifted)
shifted = true;
}
else
input = arg0;
}
@Override
public void keyReleased(KeyEvent arg0)
{
if (arg0.getKeyCode() == KeyEvent.VK_SHIFT)
if (shifted)
shifted = false;
}
@Override
public void keyTyped(KeyEvent arg0)
{
;
}
public static int callRNG(int min, int max)
{
if (min < 0)
{
min = 0;
}
if (min > max)
{
System.out.println("Invalid callRNG! Min > Max; reducing Min");
min = max;
}
if (min == max)
return max;
//Finally, we can call the RNG
return min + randomizer.nextInt(1 + max - min); //3-6 min=3 max = 6. 6-3 = 3. nextInt(3+1) gives 0-3, +3 = 3-6
}
public static int callRNG(int max)
{
return callRNG(0, max);
}
static Actor randomMob(int level)
{
LinkedList<Actor> bestiary = new LinkedList<Actor>();
for (Actor a: CreatureTypes)
{
if (a.getLevel() <= level)
for (int j = 0; j < a.getRarity(); j++)
bestiary.add(a);
}
Actor pick = new Actor(bestiary.get(Mythos.callRNG(bestiary.size() - 1)));
return pick;
}
static Item genItem(int id)
{
Item pick = new Item(ItemTypes[id]);
//Some items come in stacks
if (pick.hasProperty("stack") || pick.hasProperty("levelstack"))
{
pick.setAmount(Mythos.callRNG(1, pick.getProperty("stack")));
}
return pick;
}
static Item randomItem(int level)
{
LinkedList<Item> catalog = new LinkedList<Item>();
for (Item i: ItemTypes)
{
if (i.getLevel() <= level)
{
for (int j = 0; j < i.getRarity(); j++)
catalog.add(i);
}
}
Item pick = genItem(catalog.get(Mythos.callRNG(catalog.size() - 1)).level);
//Some items come in stacks
if (pick.hasProperty("levelstack"))
{
int dice = level;
int sides = pick.getProperty("levelstack");
int stackSize = 0;
while (dice > 0)
{
stackSize += Mythos.callRNG(1, sides);
dice--;
}
pick.setAmount(stackSize);
}
return pick;
}
}