/**************************************************************************************
* FileHandler.java
*
*************************************************************************************/
package creid.mythos.engine;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
public abstract class FileHandler
{
//Constants/////////////////////////////////////////////////////////////////////////
//TODO: Split out common input functions: gen, sprite, id/name, properties
//Data file directory
public static final String DATA_FILE_DIR_NAME = "data";
public static final String TITLE_PAGE_FILE = "title.txt";
public static final String MAIN_HELP_PAGE_FILE = "help.txt";
public static final String MAPS_GAME_FILE = "maps.game";
public static final String TERRAIN_GAME_FILE = "terrain.game";
public static final String ACTOR_GAME_FILE = "actors.game";
public static final String BACKGROUND_GAME_FILE = "backgrounds.game";
public static final String MAP_SPECIALS_GAME_FILE = "mapspecials.game";
public static final String ITEM_GAME_FILE = "items.game";
public static final String LINE_SPLIT = "NEWLINE";
public static final int NULL_INT = -99;
public static final int PLACE_MOB = 0;
public static final int PLACE_ITEM = 1;
public static final int PLACE_TERRAIN = 2;
public static final int PLACE_SIZE = 3;
//Attributes////////////////////////////////////////////////////////////////////////
//Constructors//////////////////////////////////////////////////////////////////////
//Methods///////////////////////////////////////////////////////////////////////////
public static String[] loadTextFromFile(String filename, boolean dataFile)
{
LinkedList<String> text = new LinkedList<String>();
BufferedReader fin = null;
try
{
if (Mythos.RELEASE)
{
String path = "/" + DATA_FILE_DIR_NAME + "/" + filename;
InputStream downStream = Mythos.class.getResourceAsStream(path);
fin = new BufferedReader(new InputStreamReader(downStream));
}
else //read data files from a JAR archive
{
fin = new BufferedReader(new FileReader(DATA_FILE_DIR_NAME + File.separator + filename));
}
}
catch(FileNotFoundException ex)
{
System.out.println("Error: could not open file " + filename + ":\n" + ex);
return null;
}
try
{
while(fin.ready())
{
String line = fin.readLine();
if (dataFile)
{
line = line.replaceAll("#.*$", "").trim();
if (line.length() > 0)
text.add(line);
}
else
text.add(line);
}
} catch (IOException e)
{
System.out.println("Error reading input file in loadTextFromFile():");
e.printStackTrace();
return null;
}
finally //CLose our object
{
try
{
fin.close();
} catch (IOException e)
{
System.out.println("Error closing input file in loadTextFromFile()");
}
}
//Now convert list to array
return text.toArray(new String[0]);
}
private static int getLayoutType(String type)
{
if (type.equals("dungeon"))
return GameMap.LAYOUT_DUNGEON;
//Default to dungeon
Mythos.logger.errorLog("FileHandler.getLayoutType() unknown layout type '" + type + "', assuming dungeon");
return GameMap.LAYOUT_DUNGEON;
}
/**
* loadMaps
* @return array of GameMap objects (ungenerated) with preset connections between
*/
public static GameMap[] loadMaps()
{
String[] data = loadTextFromFile(MAPS_GAME_FILE, true);
LinkedList<GameMap> maps = new LinkedList<GameMap>();
int mapID = NULL_INT;
int level = NULL_INT;
int layout = NULL_INT;
int[] terrain = null;
Exit[] exits = null;
int[] mapSpecials = null;
for(String line : data)
{
String[] content = line.split("=");
//ID line starting a new map
if (content[0].equals("Map"))
{
//first, save the previous object
if (mapID != NULL_INT)
{
if (level == NULL_INT || layout == NULL_INT || terrain == null || exits == null)
{
Mythos.logger.errorLog("FileHandler.loadMaps(): malformed entry id=" + mapID);
}
else //Add to linked list
{
maps.add(new GameMap(layout, terrain, level, exits, mapSpecials));
}
//End of file
if (content[1].equals("end")) break;
//Clear old data
layout = NULL_INT;
terrain = null;
level = NULL_INT;
exits = null;
mapSpecials = null;
}
//New map ID
mapID = Integer.parseInt(content[1]);
}
else if (content[0].equals("Layout"))
{
layout = getLayoutType(content[1]);
}
else if (content[0].equals("Terrain"))
{
terrain = new int[]{
Integer.parseInt(content[1]),
Integer.parseInt(content[2]),
Integer.parseInt(content[3]),
Integer.parseInt(content[4]),
Integer.parseInt(content[5]),
Integer.parseInt(content[6])
};
}
else if (content[0].equals("Encounters"))
{
level = Integer.parseInt(content[1]); // First entry is challenge level
}
else if (content[0].equals("Exits"))
{
exits = new Exit[content.length - 1];
for(int i = 0; i < exits.length; i++)
{
String[] exitData = content[i+1].split(",");
//(int terrain, int x, int y, GameMap map, int targetMap, int targetExit)
exits[i] = new Exit(Mythos.TerrainTypes[Integer.parseInt(exitData[0])], GameMap.RANDOM_LOCATION, GameMap.RANDOM_LOCATION, null,
Integer.parseInt(exitData[1]), Integer.parseInt(exitData[2]));
}
}
//Map specials
else if (content[0].equals("Special"))
{
mapSpecials = new int[content.length - 1];
for (int i = 0; i < mapSpecials.length; i++)
mapSpecials[i] = Integer.parseInt(content[i+1]);
}
else
{
Mythos.logger.errorLog("Unknown content type: " + content[0] + "; ignoring line");
}
}
return maps.toArray(new GameMap[maps.size()]);
}
/**
* loadTerrainTypes
* @return array of Cell objects containing the basic settings for different forms of terrain
*/
public static Cell[] loadTerrainTypes()
{
String[] data = loadTextFromFile(TERRAIN_GAME_FILE, true);
LinkedList<Cell> terrainTypes = new LinkedList<Cell>();
int terrainID = NULL_INT;
String name = null;
boolean passable = false;
boolean swimmable = false;
boolean flyable = false;
boolean slow = false;
boolean cover = false;
boolean elevated = false;
boolean opaque = false;
boolean room = false;
int operation = Cell.NO_OP;
String opMsg = null;
char[] sprite = null;
HashMap<String, Integer> properties = new HashMap<String, Integer>();
for(String line : data)
{
String[] content = line.split("=");
//ID line starting a new map
if (content[0].equals("Terrain"))
{
//first, save the previous object
if (terrainID != NULL_INT)
{
if (name == null || sprite == null) //We don't require much for terrain, just a sprite really
{
Mythos.logger.errorLog("FileHandler.loadTerrainTypes(): malformed entry id=" + terrainID);
}
else //Add to linked list
{
terrainTypes.add(new Cell(name, sprite, passable, swimmable, flyable, slow, cover, elevated, opaque, room, operation, opMsg, terrainID));
}
//Add any properties described
if (properties.keySet().size() > 0)
{
for (String key: properties.keySet())
{
terrainTypes.getLast().setProperty(key, properties.get(key));
}
}
//Clear old data
name = null;
sprite = null;
passable = false;
swimmable = false;
flyable = false;
slow = false;
cover = false;
elevated = false;
opaque = false;
room = false;
operation = Cell.NO_OP;
opMsg = null;
properties = new HashMap<String, Integer>();
}
//End of file
if (content[1].equals("end")) break;
//New map ID
terrainID = Integer.parseInt(content[1]);
name = content[2];
}
else if (content[0].equals("Sprite"))
{
sprite = new char[]{content[1].charAt(0),content[2].charAt(0)};
if (content[1].equals("WALL"))
sprite[0] = '#';
}
else if (content[0].equals("Move"))
{
for (int i = 1; i < content.length; i++)
{
if (content[i].equals("walk"))
{
passable = true;
}
else if (content[i].equals("walk"))
{
passable = true;
}
else if (content[i].equals("swim"))
{
swimmable = true;
}
else if (content[i].equals("fly"))
{
flyable = true;
}
}
}
else if (content[0].equals("Feature"))
{
for (int i = 1; i < content.length; i++)
{
if (content[i].equals("slow"))
{
slow = true;
}
else if (content[i].equals("cover"))
{
cover = true;
}
else if (content[i].equals("elevated"))
{
elevated = true;
}
else if (content[i].equals("opaque"))
{
opaque = true;
}
else if (content[i].equals("item"))
{
room = true;
}
}
}
else if (content[0].equals("Operate"))
{
operation = Integer.parseInt(content[1]);
opMsg = content[2];
}
else
{
Mythos.logger.errorLog("Unknown content type: " + content[0] + "; ignoring line");
}
}
//set the arrays
return terrainTypes.toArray(new Cell[terrainTypes.size()]);
}
public static Item[] loadItems()
{
String[] data = loadTextFromFile(ITEM_GAME_FILE, true);
LinkedList<Item> itemTypes = new LinkedList<Item>();
int iid = NULL_INT;
String name = null;
String plural = null;
String useVerb = null;
int level = NULL_INT;
int rarity = NULL_INT;
int type = NULL_INT;
char[] sprite = null;
Item use = null;
int element = 1;
int power = 0;
HashMap<String, Integer> properties = new HashMap<String, Integer>();
for(String line : data)
{
String[] content = line.split(":");
if (content[0].equals("Item"))
{
if (iid != NULL_INT)
{
if (name == null || level == NULL_INT || rarity == NULL_INT || sprite == null || type == NULL_INT)
{
Mythos.logger.errorLog("FileHandler.loadItems(): malformed entry id=" + iid);
}
else
{
itemTypes.add(new Item(iid, name, plural, sprite, level, rarity, type, power, element, useVerb));
if (use != null)
itemTypes.getLast().setUse(use);
//Add any properties described
if (properties.keySet().size() > 0)
{
for (String key: properties.keySet())
{
itemTypes.getLast().setProperty(key, properties.get(key));
}
}
}
//End of file
if (content[1].equals("end")) break;
name = null;
plural = null;
useVerb = null;
level = NULL_INT;
rarity = NULL_INT;
type = NULL_INT;
sprite = null;
element = 1;
power = 0;
properties = new HashMap<String, Integer>();
use = null;
}
iid = Integer.parseInt(content[1]);
name = content[2];
plural = content[3];
}
else if (content[0].equals("Sprite"))
{
sprite = new char[]{content[1].charAt(0),content[2].charAt(0)};
if (content[1].equals("WALL"))
sprite[0] = '#';
else if (content[1].equals("COLON"))
sprite[0] = ':';
}
else if (content[0].equals("Use"))
{
//(int id, String singular, String plural, char[] sprite, int level, int rarity, int type, int power, int element, String useVerb)
int projectionType = Integer.parseInt(content[2]);
int range = Projection.STANDARD_RANGE;
if (content.length >= 8)
range = Integer.parseInt(content[7]);
int radius = 0;
if (content.length == 9)
radius = Integer.parseInt(content[8]);
use = new Item(-1, "effect", null, new char[]{content[5].charAt(0), content[6].charAt(0)}, -1, Entity.NEVER_GEN, Item.I_EFFECT,
Integer.parseInt(content[3]), Integer.parseInt(content[4]), content[1]);
use.setProperty("radius", radius);
use.setProperty("range", range);
use.setProperty("projectionType", projectionType);
}
else if (content[0].equals("Spawn"))
{
type = Integer.parseInt(content[1]);
level = Integer.parseInt(content[2]);
rarity = Integer.parseInt(content[3]);
}
else if (content[0].equals("Combat"))
{
power = Integer.parseInt(content[1]);
element = Integer.parseInt(content[2]);
}
else if (content[0].equals("Properties"))
{
for (int i = 1; i < content.length; i++)
{
String[] entry = content[i].split("=");
int value = 1;
if (entry.length == 2)
value = Integer.parseInt(entry[1]);
properties.put(entry[0], value);
}
}
}
return itemTypes.toArray(new Item[itemTypes.size()]);
}
private static int getAttackElement(String name)
{
return Entity.PHYSICAL;
}
public static Actor[] loadActors()
{
String[] data = loadTextFromFile(ACTOR_GAME_FILE, true);
LinkedList<Actor> actorTypes = new LinkedList<Actor>();
int aid = NULL_INT;
String name = null;
String plural = null;
char[] sprite= null;
int level = NULL_INT;
int rarity = NULL_INT;
int faction = NULL_INT;
int pathType = NULL_INT;
int preferredRange = NULL_INT;
int vigilance = NULL_INT;
int speed = NULL_INT;
boolean walk = false;
boolean swim = false;
boolean fly = false;
int size = NULL_INT;
int brawn = NULL_INT;
int grace = NULL_INT;
int will = NULL_INT;
Item attack = null;
Item attack2 = null;
int defense = 0;
StringBuilder descriptionInOneLine = null;
HashMap<String, Integer> properties = new HashMap<String, Integer>();
for(String line : data)
{
String[] content = line.split(":");
if (content[0].equals("Actor"))
{
//Save the previous creature
if (aid != NULL_INT)
{
if (name == null || sprite == null || level == NULL_INT || faction == NULL_INT || pathType == NULL_INT || preferredRange == NULL_INT ||
vigilance == NULL_INT || size == NULL_INT || brawn == NULL_INT || rarity == NULL_INT || grace == NULL_INT || will == NULL_INT)
{
Mythos.logger.errorLog("FileHandler.loadActors(): malformed entry id=" + aid);
}
else
{
actorTypes.add(new Actor(aid, name, plural, sprite, level, size, rarity,
//AI stuff
faction, pathType, preferredRange, vigilance,
//Mobility
walk, swim, fly, speed,
//Combat stats
defense, brawn, grace, will, attack, attack2));
//Add any properties described
if (properties.keySet().size() > 0)
{
for (String key: properties.keySet())
{
actorTypes.getLast().setProperty(key, properties.get(key));
}
}
if (descriptionInOneLine != null)
actorTypes.getLast().setDetails(descriptionInOneLine.toString().split(LINE_SPLIT));
}
//Clear previous entry's data
aid = NULL_INT;
name = null;
plural = null;
sprite= null;
level = NULL_INT;
faction = NULL_INT;
pathType = NULL_INT;
preferredRange = NULL_INT;
vigilance = NULL_INT;
speed = NULL_INT;
walk = false;
swim = false;
fly = false;
defense = 0;
size = NULL_INT;
brawn = NULL_INT;
grace = NULL_INT;
attack = null;
attack2 = null;
descriptionInOneLine = null;
will = NULL_INT;
properties = new HashMap<String, Integer>();
}
//End of file
if (content[1].equals("end")) break;
//Add new info
aid = Integer.parseInt(content[1]);
name = content[2];
plural = content[3];
}
else if (content[0].equals("Sprite"))
{
sprite = new char[]{content[1].charAt(0),content[2].charAt(0)};
if (content[1].equals("WALL"))
sprite[0] = '#';
else if (content[1].equals("COLON"))
sprite[0] = ':';
}
else if (content[0].equals("Gen"))
{
faction = Integer.parseInt(content[1]);
level = Integer.parseInt(content[2]);
rarity = Integer.parseInt(content[3]);
}
else if (content[0].equals("AI"))
{
pathType = Integer.parseInt(content[1]);
preferredRange = Integer.parseInt(content[2]);
vigilance = Integer.parseInt(content[3]);
}
else if (content[0].equals("Move"))
{
speed = Integer.parseInt(content[1]);
for (int i = 2; i < content.length; i++)
{
if (content[i].equals("walk"))
walk = true;
else if (content[i].equals("swim"))
swim = true;
else if (content[i].equals("fly"))
fly = true;
}
}
else if (content[0].equals("Stats"))
{
brawn = Integer.parseInt(content[2]);
grace = Integer.parseInt(content[3]);
will = Integer.parseInt(content[4]);
size = Actor.MEDIUM;
if (content[1].equals("small"))
size = Actor.SMALL;
else if (content[1].equals("large"))
size = Actor.LARGE;
}
else if (content[0].equals("Attack"))
{
Item att = new Item(-1, "Attack", null, null, 0, Entity.NEVER_GEN, Item.I_NATURAL_ATTACK, Integer.parseInt(content[3]), getAttackElement(content[2]), content[1]);
if (attack == null)
attack = att;
else
attack2 = att;
}
else if (content[0].equals("Properties"))
{
for (int i = 1; i < content.length; i++)
{
String[] entry = content[i].split("=");
properties.put(entry[0], Integer.parseInt(entry[1]));
}
}
else if (content[0].equals("Text"))
{
if (descriptionInOneLine == null)
descriptionInOneLine = new StringBuilder(content[1]);
else //appending
descriptionInOneLine.append(LINE_SPLIT + content[1]);
}
}
return actorTypes.toArray(new Actor[actorTypes.size()]);
}
/**
* loadBackgrounds
* @return backgrounds
*/
public static Actor[] loadBackgrounds()
{
String[] data = loadTextFromFile(BACKGROUND_GAME_FILE, true);
LinkedList<Actor> backgrounds = new LinkedList<Actor>();
int aid = NULL_INT;
String name = null;
String plural = null;
char[] sprite= null;
int speed = NULL_INT;
boolean walk = false;
boolean swim = false;
boolean fly = false;
int size = NULL_INT;
int brawn = NULL_INT;
int grace = NULL_INT;
int will = NULL_INT;
/*#Background
#Sprite:symbol:color
#Move:speed:walk:swim:fly
#Stats:size:brawn:grace:will
#Defense:armor[:element=X]
#Properties:
#Text:*/
StringBuilder descriptionInOneLine = null;
HashMap<String, Integer> properties = new HashMap<String, Integer>();
for(String line : data)
{
String[] content = line.split(":");
if (content[0].equals("Background"))
{
//Save the previous creature
if (aid != NULL_INT)
{
if (name == null || sprite == null || size == NULL_INT || brawn == NULL_INT || grace == NULL_INT || will == NULL_INT)
{
Mythos.logger.errorLog("FileHandler.loadBackgrounds(): malformed entry id=" + aid);
}
else
{
if (speed == NULL_INT)
speed = 0;
backgrounds.add(new Actor(aid, name, plural, sprite, 1, size, Entity.NEVER_GEN,
//AI stuff
Actor.PLAYER_FACTION, GameMap.ALLY_PATH, 0, 0,
//Mobility
walk, swim, fly, speed,
//Combat stats
0, brawn, grace, will, new Item(-1, "unarmed attack", null, null, 0, 0, Item.I_NATURAL_ATTACK, 0, 0,"punches"), null));
//Add any properties described
backgrounds.getLast().setProperty("Elite", 1);
if (properties.keySet().size() > 0)
{
for (String key: properties.keySet())
{
backgrounds.getLast().setProperty(key, properties.get(key));
}
}
if (descriptionInOneLine != null)
backgrounds.getLast().setDetails(descriptionInOneLine.toString().split(LINE_SPLIT));
}
//End of file
if (content[1].equals("end")) break;
//Clear previous entry's data
aid = NULL_INT;
name = null;
plural = null;
sprite= null;
speed = NULL_INT;
walk = false;
swim = false;
fly = false;
size = NULL_INT;
brawn = NULL_INT;
grace = NULL_INT;
descriptionInOneLine = null;
will = NULL_INT;
properties = new HashMap<String, Integer>();
}
//Add new info
aid = Integer.parseInt(content[1]);
name = content[2];
}
else if (content[0].equals("Sprite"))
{
sprite = new char[]{content[1].charAt(0),content[2].charAt(0)};
if (content[1].equals("WALL"))
sprite[0] = '#';
}
else if (content[0].equals("Move"))
{
speed = Integer.parseInt(content[1]);
for (int i = 2; i < content.length; i++)
{
if (content[i].equals("walk"))
walk = true;
else if (content[i].equals("swim"))
swim = true;
else if (content[i].equals("fly"))
fly = true;
}
}
else if (content[0].equals("Stats"))
{
brawn = Integer.parseInt(content[2]);
grace = Integer.parseInt(content[3]);
will = Integer.parseInt(content[4]);
size = Actor.MEDIUM;
if (content[1].equals("small"))
size = Actor.SMALL;
else if (content[1].equals("large"))
size = Actor.LARGE;
}
else if (content[0].equals("Properties"))
{
for (int i = 1; i < content.length; i++)
{
String[] entry = content[i].split("=");
properties.put(entry[0], Integer.parseInt(entry[1]));
}
}
else if (content[0].equals("Text"))
{
if (descriptionInOneLine == null)
descriptionInOneLine = new StringBuilder(content[1]);
else //appending
descriptionInOneLine.append(LINE_SPLIT + content[1]);
if (content.length > 2)
for (int i = 2; i < content.length; i++)
descriptionInOneLine.append(content[i]);
}
}
//Common Properties all backgrounds should have
for(Actor bg: backgrounds)
{
//Open/close doors
bg.setProperty("operate", 1);
}
return backgrounds.toArray(new Actor[backgrounds.size()]);
}
static MapSpecial[] loadSpecialRooms()
{
LinkedList<MapSpecial> rooms = new LinkedList<MapSpecial>();
String[] data = loadTextFromFile(MAP_SPECIALS_GAME_FILE, true);
int roomID = NULL_INT;
int level = NULL_INT;
char[][] roomMap = null;
String[] specials = null; //Placing creatures, items, etc
int width = NULL_INT;;
int height = NULL_INT;
int rarity = NULL_INT;
int row = 0;
for(String line: data)
{
String[] content = line.split("=");
if (content[0].equals("MapSpecial"))
{
if (roomID != NULL_INT)
{
if (roomMap == null || level == NULL_INT || rarity == NULL_INT)
Mythos.logger.errorLog("FileHandler.loadSpecialRooms(): malformed entry id=" + roomID);
else
{
rooms.add(new MapSpecial(roomMap, specials, rarity));
}
}
//End of file
if (content[1].equals("end")) break;
//Clear old data
roomMap = null;
specials = null;
width = NULL_INT;
height = NULL_INT;
rarity = NULL_INT;
level = NULL_INT;
row = 0;
roomID = Integer.parseInt(content[1]);
width = Integer.parseInt(content[2]);
height = Integer.parseInt(content[3]);
roomMap = new char[width][height];
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
roomMap[x][y] = '|';
}
else if (content[0].equals("Gen"))
{
level = Integer.parseInt(content[1]);
rarity = Integer.parseInt(content[2]);
}
else if (content[0].equals("R"))
{
if (row == roomMap.length)
{
Mythos.logger.errorLog("Ignoring invalid R line for roomID " + roomID + " (" + line + ")");
continue;
}
for (int col = 0; col < roomMap[0].length; col++)
{
roomMap[row][col] = content[1].charAt(col);
}
row++;
}
else if (content[0].equals("Symbols"))
{
specials = new String[content.length - 1];
for(int i = 1; i < content.length; i++)
{
specials[i-1] = content[i];
}
}
}
return rooms.toArray(new MapSpecial[rooms.size()]);
}
}