The code for the LevelParser is shown below. It is also documented using Java-doc, the docs are in the project directory.
Now by improving this piece of code, I do not mean optimizing it to incomprehension, or improving it's performance. What I want is to make it a bit more readable, easier to maintain. Right now, in my opinion, it is a mess.
When I was writing it, I focused only on it working and didn't care about its readability and design. And I regret it now. But I can't completely rewrite it from scratch because it is closely connected with the entire project, and that would ultimately mean rewriting the entire project. S
So is there any way to only improve parts of the code, making it more readable, without a complete rewrite? Because now that it is written and bug-free, unless I find any new bugs, I won't be looking at it for a few days, I'll be focusing on other features. When I give a second look to it, say, two weeks later, I don't want to end up like this:
Code (also on github):
package pro;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JPanel;
public class LevelParser {
List<Sprite> tileArray = new ArrayList<Sprite>();
Map<Character, String> tileMapping = new HashMap<Character, String>();
Map<Character, String> messageMapping = new HashMap<Character, String>();
Point offset = new Point(0,0);
Dimension levelSize = new Dimension(0,0);
Dimension tileSize;
JPanel frame;
/* Constructors */
public LevelParser(Dimension tileSize, JPanel frame) {
super();
this.tileSize = tileSize;
this.frame = frame;
}
public LevelParser( int w, int h, JPanel applet ){
this( new Dimension(w,h), applet );
}
/* Other Methods */
public void reset(){
tileArray.clear();
tileMapping.clear();
messageMapping.clear();
offset = new Point(0,0);
levelSize = new Dimension(0,0);
}
/* Getters and Setters */
/* Parser Lists */
public List<Sprite> getTiles() {
return tileArray;
}
public List<Sprite> getTileArray() {
return tileArray;
}
public void setTileArray(List<Sprite> tileArray) {
this.tileArray = tileArray;
}
public Map<Character, String> getTileMapping() {
return tileMapping;
}
public void setTileMapping(Map<Character, String> tileMapping) {
this.tileMapping = tileMapping;
}
public void setTiles(List<Sprite> tiles) {
this.tileArray = tiles;
}
public Dimension getTileSize() {
return tileSize;
}
public void setTileSize(Dimension tileSize) {
this.tileSize = tileSize;
}
/* Members */
public JPanel getApplet() {
return frame;
}
public void setApplet(JPanel applet) {
this.frame = applet;
}
public int getTileWidth(){
return getTileSize().width;
}
public void setTileWidth(int w){
this.tileSize.width = w;
}
public int getTileHeight(){
return getTileSize().height;
}
public void setTileHeight(int h){
this.tileSize.height = h;
}
public Point getOffset() {
return offset;
}
public void setOffset(Point offset) {
this.offset = offset;
}
/* Level Size */
public Dimension getLevelSize() {
return levelSize;
}
public void setLevelSize(Dimension levelSize) {
this.levelSize = levelSize;
}
public void setLevelWidth(int w){
levelSize.width = w;
}
public int getLevelWidth(){
return levelSize.width;
}
public void setLevelHeight(int h){
levelSize.height = h;
}
public int getLevelHeight(){
return levelSize.height;
}
/* Collision Detection */
public boolean checkCollision( AstroSprite plyr ){
for( Sprite s : tileArray ){
if ( s.isCollideable() && s.isCollidingRect(plyr) ){
// if player is moving or the tiles are scrolling...
if(plyr.getVectorY() < 0 || offset.y > 0) plyr.setTop(s.getBottom());
else if(plyr.getVectorY() > 0 || offset.y < 0) plyr.setBottom(s.getTop());
else if(plyr.getVectorX() < 0 || offset.x > 0) plyr.setLeft(s.getRight());
else if(plyr.getVectorX() > 0 || offset.x < 0) plyr.setRight(s.getLeft());
}
}
return false;
}
public Sprite getCollidingTile( Sprite plyr ){
for( Sprite s : tileArray ){
if ( s.isCollideable() && s.isCollidingRect(plyr) ){
return s;
}
}
return null;
}
public void showTileMessage(AstroSprite plyr){
int dx = 0, dy = 0;
if ( plyr.getDirection() == AstroSprite.LEFT ){
dx = -2;
}
else if ( plyr.getDirection() == AstroSprite.RIGHT ){
dx = 2;
}
else if ( plyr.getDirection() == AstroSprite.UP ){
dy = -2;
}
else if ( plyr.getDirection() == AstroSprite.DOWN ){
dy = 2;
}
plyr.move(dx, dy);
Sprite s = getCollidingTile(plyr);
if ( s != null ){
Character c = getTileMappingKeyFromSprite( s );
if (messageMapping.containsKey(c)){
TextMessage.Display( messageMapping.get(c) );
}
}
plyr.move(-dx, -dy);
}
/* Lists etc. */
public Character getTileMappingKeyFromSprite( Sprite s ){
/* It definitely has an Image Url, because all tile sprites'
* Image URLs were set in the parseLine method, Note that this
* won't work on ordinary sprites.
*/
if ( tileMapping.containsValue( s.getImageUrl() ) ){
return Utility.getKeyByValue(tileMapping, s.getImageUrl());
}
return null;
}
/* Drawing */
public void drawTiles( Graphics g ){
for( Sprite s : tileArray ){
s.move(offset.x, offset.y);
if( onScreen( s, frame ) ){
if ( ! s.draw(g) )
g.drawImage(s.getImage(),
s.getX(),
s.getY(),
getTileWidth(), getTileHeight(), getApplet());
}
}
}
public void updateTiles(){
for( Sprite s : tileArray ){
if ( onScreen( s, frame ) ){
s.update(frame);
}
}
}
boolean onScreen(Sprite spr, JPanel applet){
if ( spr.getX() < applet.getWidth() && spr.getY() < applet.getHeight() ){
return true;
}
return false;
}
/* Parsing */
/* Mapping */
public void parseTileMapping( String filename ){
try{
BufferedReader reader = new BufferedReader( new FileReader(filename) );
String line;
while( (line = reader.readLine()) != null ){
if (line.startsWith("$")){
String[] tmp = line.split("=");
String p = tmp[0].substring(1).trim();
tileMapping.put( p.charAt(0), tmp[1].trim() );
if ( tmp.length > 2 ){
messageMapping.put( p.charAt(0), tmp[2].trim() );
}
}
}
} catch(FileNotFoundException e){
System.out.println("Could not find " + filename);
System.exit(1);
} catch( IOException e){
System.out.println("Could not read line!");
System.exit(1);
}
}
/* Level */
public void parseLevel( String filename ){
parseTileMapping( filename );
BufferedReader reader;
try{
reader = new BufferedReader( new FileReader(filename) );
parseTiles( reader );
} catch(FileNotFoundException e){
System.out.println("Could not find " + filename );
System.exit(1);
}
}
public void parseTiles( BufferedReader reader ){
String line;
try{
int count = 1;
while( (line = reader.readLine()) != null){
if( ! line.startsWith("#") && ! line.startsWith("$") ){
parseLine( line, count++ );
}
}
}catch(IOException e){
System.out.println("Failed to read line!");
System.exit(1);
}
}
void parseLine( String line, int count ){
int x = 0, y = count;
for( int i = 0; i < line.length(); i++){
char c = line.charAt(i);
Image img = null;
if (tileMapping.containsKey(c)){
img = Toolkit.getDefaultToolkit().getImage( ConfigurationLoader.IMAGE_URL
+ tileMapping.get(c) );
}
if ( img != null ){
Sprite s = new Sprite( new Point(x * getTileWidth(), y * getTileHeight()),
new Dimension( getTileWidth(), getTileHeight() ),
img);
/* c definitely exists, otherwise img would have been null */
s.setImageUrl( tileMapping.get(c) );
if ( Character.isDigit(c)) s.setCollideable(false);
tileArray.add( s );
}
x++;
setLevelWidth( Math.max( x*getTileWidth(), getLevelWidth() ) );
setLevelHeight( Math.max( y*getTileHeight(), getLevelHeight() ) );
}
}
}