Yes I made a mess, out of hashtables



public class PlayerSprite extends Critter { 
	
	private long updateTime;
	private Hashtable characterAttribs;
	private Hashtable characterAnims;
	private String currentCharacter;
 
    public PlayerSprite(String startingCharacter, Hashtable animTable) {
		super((ImageSequence)animTable.get(startingCharacter).get("north"));
		characterAttribs = new Hashtable<String, String[]>();
		characterAnims = new Hashtable();
		
		currentCharacter = startingCharacter;
		characterAnims = animTable;
		
		String[] catAttribs = {"Mountains", "Field", "Tree"};
		String[] turtleAttribs = {"Lava", "Cold Water", "Ocean Water"};
		String[] snakeAttribs = {"Desert", "Swamp Water", "Trees"};
		String[] rabbitAttribs = {"Field", "Underground", "Tundra"};
		String[] penguinAttribs = {"Tundra", "Cold Water", "Ocean Water"};
		String[] salamanderAttribs = {"Lava", "Cold Water", "Swamp Water"};
		String[] mouseAttribs = {"Underground", "Desert", "Mountains"};
		String[] lizardAttribs = {"Trees", "Desert", "Lava"};
		String[] frogAttribs = {"Trees", "Underground", "Swamp Water"};
		String[] foxAttribs = {"Field", "Tundra", "Mountains"};
		
		//insert character data
		characterAttribs.put("Cat", catAttribs);
		characterAttribs.put("Turtle", turtleAttribs);
		characterAttribs.put("Snake", snakeAttribs);
		characterAttribs.put("Rabbit", rabbitAttribs);
		characterAttribs.put("Penguin", penguinAttribs);
		characterAttribs.put("Salamander", salamanderAttribs);
		characterAttribs.put("Mouse", mouseAttribs);
		characterAttribs.put("Lizard", lizardAttribs);
		characterAttribs.put("Frog", frogAttribs);
		characterAttribs.put("Fox", foxAttribs);
	}

animTableComes carrying the keys “Cat” ect… as you see where i am creating attrib arrays. Each key value has another hashtable which stores a string key for the animation and the values are image sequences. Here is my error:

adsl-71-146-102-201:~/ My Apps/Java/2D/Animal Game johncarlyle$ javac PlayerSprite.java
PlayerSprite.java:19: cannot find symbol
symbol  : method get(java.lang.String)
location: class java.lang.Object
                super((ImageSequence)animTable.get((String)startingCharacter).get("north"));
                                                  ^
Note: PlayerSprite.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Not quite sure what it means. This is the first half of my question…

(This doesnt fit on one post)

Oh right the intent, is to get the starting characters northern movment animation from the hashtable. Might as well post my resource manager so you can see if i packed the hashtable badly.

resource manager: it is init’d then loadFirstMap() is called the variable first carries “Cat”

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import java.net.*;
import java.util.*;
	

public class ResourceManager {

	private ArrayList<BufferedImage> tiles;
	private int currentMap;
	private GraphicsConfiguration gc;
	private ImageSequence anims[];
	private BufferedImage spriteSheet;
	private ImageLoader loader;
	private Hashtable characterAnimations;
	
	
	public ResourceManager(GraphicsConfiguration gc)	{
		this.gc = gc;
		characterAnimations = new Hashtable();
		
		loader = new ImageLoader();
		loadTileImages();
		loadImages();
	}
	
	public Hashtable getAnims()	{
		return characterAnimations;
	}
	
	public TileMap loadFirstMap()	{
		currentMap = 0;
		return loadNextMap();
	}
	
	public TileMap loadNextMap()	{
		TileMap map = null;
		while (map == null)	{
			currentMap++;
			try	{
				map = loadMap("Resources/Maps/Map" + currentMap + ".txt");
			}
			catch (IOException ex)	{
				if (currentMap == 1)	{
					return null;
				}
				currentMap = 0;
				map = null;
			}
		}
		System.out.println(map);
		return map;
	}
	
	public TileMap reloadMap()	{
		try	{
			return loadMap("Resources/Maps/Map" + currentMap + ".txt");
		}
		catch (IOException ex)	{
			ex.printStackTrace();
			return null;
		}
	}

	private TileMap loadMap(String filename) throws IOException	{
		ArrayList<String> lines = new ArrayList<String>();
		int width = 0;
		int height = 0;
		BufferedReader reader;
		
        try {
            ClassLoader classLoader = getClass().getClassLoader();
            URL url = classLoader.getResource(filename);
			if (url == null)	{
				throw new IOException("No such map:" + filename);
			}
			reader = new BufferedReader(new InputStreamReader(url.openStream()));
            
        } catch (Exception e) {
			throw new IOException();
        }
		
		LinkedList<String> party = new LinkedList<String>();
		String first = null;
		
		while (true)	{
			String line = reader.readLine();
			if (line == null)	{
				reader.close();
				break;
			}
			if (!line.startsWith("#"))	{
				lines.add(line);	
				width = Math.max(width, line.length());
			}
			else	{
				if (line.startsWith("# Party"))	{
					line = line.substring(line.indexOf(":")+2);
					
					//parse the party line
					int firstComma = line.indexOf(',');
					int secondComma = line.indexOf(',', firstComma+1);
					firstComma++;
					secondComma++;
		
					first = line.substring(1, firstComma-1);
					String second = line.substring(firstComma+1, secondComma-1);
					String third = line.substring(secondComma+1, line.length()-1);
					
					party.add(first);
					party.add(second);
					party.add(third);
				}
			}
		}
		height = lines.size();
		TileMap newMap = new TileMap(width, height, party);
		if (first == null)	{
			newMap.setPlayer(new PlayerSprite("Cat", characterAnimations));
		}
		else	{
			newMap.setPlayer(new PlayerSprite(first, characterAnimations));
		}
		
	
		return newMap;
	}
	
	
	private void addSprite(TileMap map, String hostSprite, int tileX, int tileY)	{
	}
	
	public void loadTileImages()	{
		ImageLoader loader = new ImageLoader();
		tiles = new ArrayList<BufferedImage>();
		char ch = 'A';
		while (true)	{
			String name = "tile_" + ch + ".png";
			ClassLoader classLoader = getClass().getClassLoader();
			URL url = classLoader.getResource("Resources/Images/Background Tiles/" + name);
			if (url == null)	{
				break;
			}
			tiles.add(loader.loadImage("Resources/Images/Background Tiles/" + name));
			ch++;
		}
	}
	
	public String numberToAnimal(int i)	{
		if (i == 0)	{
			return "cat";
		}
		if (i == 1)	{
			return "bird";
		}
		if (i == 2)	{
			return "fox";
		}
		if (i == 3)	{
			return "frog";
		}
		if (i == 4)	{
			return "liazrd";
		}
		if (i == 5)	{
			return "mouse";
		}
		if (i == 6)	{
			return "salamander";
		}
		if (i == 7)	{
			return "penguin";
		}
		if (i == 8)	{
			return "rabbit";
		}
		if (i == 9)	{
			return "snake";
		}
		if (i == 10) {
			return "turtle";
		}
		return null;
	}
	
	private void loadImages()	{
		spriteSheet = loader.loadImage("Resources/Images/sheet.png");
		
		for (int i = 0 ; i < 11 ; i++ )	{
			Hashtable iAnims = new Hashtable();
			BufferedImage savedImage = spriteSheet;
			for (int x = 0 ; x < 18 ; x++)	{
				if (x == 0)		{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 1)		{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("north", seq);
				}
				if (x == 2)		{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 3)		{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("north east", seq);
				}
				if (x == 4)		{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 5)		{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("east", seq);
				}
				if (x == 6)		{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 7)		{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("south east", seq);
				}
				if (x == 8)		{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 9)		{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("south", seq);
				}
				if (x == 10)	{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 11)	{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("south west", seq);
				}
				if (x == 12)	{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 13)	{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("west", seq);
				}
				if (x == 14)	{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 15)	{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("north west", seq);
				}
				if (x == 16)	{
					savedImage = spriteSheet.getSubimage(x*36, i*36+179, 36, 36);
				}
				if (x == 17)	{
					ImageSequence seq =  new ImageSequence();
					seq.addFrame(savedImage, 150);
					seq.addFrame(spriteSheet.getSubimage(x*36, i*36+179, 36, 36), 150);
					iAnims.put("sleeping", seq);
				}
			}
			characterAnimations.put(numberToAnimal(i), iAnims);
		}
	}
}

When you are doing a cast and then a method call on the same casted object, you need to put brackets around it.


//wrong
super((ImageSequence)animTable.get(startingCharacter).get("north"));


//right
       \/                         \/
super( ( (ImageSequence)animTable ).get(startingCharacter).get("north"));

super( ( (ImageSequence) animTable ).get( startingCharacter ).get( "north" ) );

I know what I am trying to do but I can’t wrap my tiny brain around it… here is my error.

PlayerSprite.java:19: inconvertible types
found   : java.util.Hashtable
required: ImageSequence
                super( ((ImageSequence) animTable).get( startingCharacter ).get( "north" ) );
                                        ^
Note: PlayerSprite.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
adsl-71-146-101-161:~/ My

Its giving me the nested hashtable not the image sequence within the nested hashtable

This code contains an attempt to convert animTable to an ImageSequence. As I understand it, you want to convert the contents of the hashtable to ImageSequences. Take a look at the first parenthesized expression:

(ImageSequence)animTable

This is analogous to:

(int)x

That is, it converts animTable to an ImageSequence. If you want to convert the result of “animTable.get(startingCharacter)” to an ImageSequence, you need to form it thusly

(ImageSequence)animTable.get(startingCharacter)

which yields the following complete statement:

super( ( (ImageSequence)animTable.get( startingCharacter)).get( "north" ) );

In the future, I recommend the use of generics to make life easier and reduce the number of parentheses.

PlayerSprite.java:21: cannot find symbol
symbol  : method get(java.lang.String)
location: class ImageSequence
                super( ( (ImageSequence)animTable.get( startingCharacter)).get( "north" ) );
                       ^

I am asking ImageSequence to get()? There are way too many ( and ) in there. What was this method you suggested I use instead of this ? Also whats wrong with the current code… again.

Why are you asking me? It’s your code. Seriously, if you don’t know what is in which table, then you probably need to refactor your solution. In my experience, the use of cascading hashtables like this is usually a sign of poorly designed object interactions. I don’t mean to be overly harsh here, but rather I’m trying to steer you towards a better solution.

For example, why use a hashtable to store four animations and key them as “north”, “south”, etc.? How about introducing an interface instead:


interface CharacterAnimation {
  ImageSequence northAnim();
  ImageSequence southAnim();
  etc.
}

Now, write the rest of your code to refer to CharacterAnimation, and you have reduced the coupling between components. Interfaces allow you to add a layer of abstraction such that you can replace the implementation without changing the interface.

More to the point: consider the following code


JPanel p = new JPanel();
Hashtable t = new Hashtable();
t.put( "foo", new JButton());

p.add(  (JButton) t.get("foo") );

The table maps strings to jbuttons, and so if I want to get at the JButton mapped by “foo”, I must cast the result of the expression t.get(“foo”). I could use more parentheses:


p.add( (JButton) (t.get("foo") );

Maybe that approach makes more sense to you, but it is semantically equivalent to the other block of code.

Nope. Unless I am misunderstanding the nature of your code, my previous example has the minimal number of parentheses.

Recommendation #1: Refactor the code. Rethink your object model. Focus on the dynamic model: what objects are responsible for processing what messages? Test each class independently, then bring them together.
or
Recommendation #2: Use the code I already provided.

What is wrong with your current code is that you were casting the table itself to a new class, not the result of the “get” expression. It would be as if I used my jbutton example above and tried to do this:


((JButton)t).get("foo")

The key is the first part: ((JButton)t). This casts the table into a JButton, or attempts to. It cannot be done, since these two types are – to use the phrase the compiler helpfully gave you – inconvertible.

Alright, took a different direcion, and it works perfectly fine. Not sure why I tried nested hashtables in the first place, I don’t like single ones… Thanks for all your help.