bool addSingleCreatureConfig( TiXmlElement* elemCreature, vector<vector<CreatureConfiguration>*>& knownCreatures, int basefile ){
  int gameID = lookupIndexedType(elemCreature->Attribute("gameID"),contentLoader.creatureNameStrings);
  if (gameID == INVALID_INDEX)
  	return false;
  const char* sheetIndexStr;
  t_SpriteWithOffset sprite;
  sprite.fileIndex=basefile;
  sprite.x=0;
  sprite.y=0;
  sprite.animFrames=ALL_FRAMES;
  int baseShadow = DEFAULT_SHADOW;
  const char* shadowStr = elemCreature->Attribute("shadow");
  if (shadowStr != NULL && shadowStr[0] != 0)
  {
	baseShadow = atoi( shadowStr );	  
  }
  if (baseShadow < 0 || baseShadow > MAX_SHADOW)
  	baseShadow = DEFAULT_SHADOW;
  const char* filename = elemCreature->Attribute("file");
	if (filename != NULL && filename[0] != 0)
	{
	  	sprite.fileIndex = loadConfigImgFile((char*)filename,elemCreature);
	}
  TiXmlElement* elemVariant = elemCreature->FirstChildElement("variant");
  while( elemVariant ){
	int professionID = INVALID_INDEX;
    const char* profStr = elemVariant->Attribute("prof");
    if (profStr == NULL || profStr[0] == 0)
    {
	    profStr = elemVariant->Attribute("profession");
    }
   	professionID = translateProfession(profStr);

    const char* customStr = elemVariant->Attribute("custom");
    if (customStr != NULL && customStr[0] == 0)
    {
	    customStr = NULL;
    } 
      
	if (customStr != NULL)
	{
		WriteErr("custom: %s\n",customStr);	
	}
    
    const char* sexstr = elemVariant->Attribute("sex");
    sheetIndexStr = elemVariant->Attribute("sheetIndex");
    enumCreatureSex cresex = eCreatureSex_NA;
    if(sexstr){
      if(strcmp( sexstr, "M" ) == 0) cresex = eCreatureSex_Male;
      if(strcmp( sexstr, "F" ) == 0) cresex = eCreatureSex_Female;
    }
    const char* specstr = elemVariant->Attribute("special");
    enumCreatureSpecialCases crespec = eCSC_Any;
    if (specstr)
    {
      if(strcmp( specstr, "Normal" ) == 0) crespec = eCSC_Normal;
      if(strcmp( specstr, "Zombie" ) == 0) crespec = eCSC_Zombie;	      
      if(strcmp( specstr, "Skeleton" ) == 0) crespec = eCSC_Skeleton;	      
    }
    sprite.animFrames = getAnimFrames(elemVariant->Attribute("frames"));
	if (sprite.animFrames == 0)
		sprite.animFrames = ALL_FRAMES;

	int shadow = baseShadow;
	const char* shadowStr = elemVariant->Attribute("shadow");
	if (shadowStr != NULL && shadowStr[0] != 0)
	{
		shadow = atoi( shadowStr );	  
	}
	if (shadow < 0 || shadow > MAX_SHADOW)
		shadow = baseShadow;
		    
    //create profession config
    sprite.sheetIndex=atoi(sheetIndexStr);
    CreatureConfiguration cre( professionID, customStr , cresex, crespec, sprite, shadow);
    //add a copy to known creatures
    pushCreatureConfig(knownCreatures, gameID, cre);

    elemVariant = elemVariant->NextSiblingElement("variant");
  }

  //create default config
  baseShadow;
  sheetIndexStr = elemCreature->Attribute("sheetIndex");
  sprite.animFrames = ALL_FRAMES;
  if (sheetIndexStr)
  {
	sprite.sheetIndex = atoi( sheetIndexStr );
    CreatureConfiguration cre( INVALID_INDEX, NULL, eCreatureSex_NA, eCSC_Any, sprite, baseShadow);
  	//add a copy to known creatures
    pushCreatureConfig(knownCreatures, gameID, cre);
  }
  return true;
}
void c_sprite::set_by_xml(TiXmlElement *elemSprite)
{
	const char* sheetIndexStr;
	sheetIndexStr = elemSprite->Attribute("sheetIndex");
	if (sheetIndexStr != NULL && sheetIndexStr[0] != 0)
	{
		sheetindex=atoi(sheetIndexStr);
	}
	const char* spriteStr;
	spriteStr = elemSprite->Attribute("sprite");
	if (spriteStr != NULL && spriteStr[0] != 0)
	{
		sheetindex=atoi(spriteStr);
	}
	const char* indexStr;
	indexStr = elemSprite->Attribute("index");
	if (indexStr != NULL && indexStr[0] != 0)
	{
		sheetindex=atoi(indexStr);
	}
	const char* animoffStr;
	animoffStr = elemSprite->Attribute("random_anim_offset");
	if (animoffStr != NULL && animoffStr[0] != 0)
	{
		randomanimation=atoi(animoffStr);
	}
	const char* scaleStr;
	scaleStr = elemSprite->Attribute("zoom");
	if (scaleStr != NULL && scaleStr[0] != 0)
	{
		int scalev=atoi(scaleStr);
		spritescale=std::pow(2.0f,(float)scalev);
	}
	//load files, if any
	const char* filename = elemSprite->Attribute("file");
	if (filename != NULL && filename[0] != 0)
	{
		fileindex = loadConfigImgFile((char*)filename,elemSprite);
		if(fileindex == -1) return;
	}

	animframes = getAnimFrames(elemSprite->Attribute("frames"));
	if (animframes == 0)
		animframes = ALL_FRAMES;

	openborders = getBorders(elemSprite->Attribute("border_open_OR"));

	floorborders = getBorders(elemSprite->Attribute("border_floor_OR"));

	wallborders = getBorders(elemSprite->Attribute("border_wall_OR"));

	rampborders = getBorders(elemSprite->Attribute("border_ramp_OR"));

	upstairborders = getBorders(elemSprite->Attribute("border_upstair_OR"));

	downstairborders = getBorders(elemSprite->Attribute("border_downstair_OR"));

	darkborders = getUnBorders(elemSprite->Attribute("border_dark_OR"));

	lightborders = getBorders(elemSprite->Attribute("border_light_OR"));

	notopenborders = getUnBorders(elemSprite->Attribute("border_open_NOR"));

	notfloorborders = getUnBorders(elemSprite->Attribute("border_floor_NOR"));

	notwallborders = getUnBorders(elemSprite->Attribute("border_wall_NOR"));

	notrampborders = getUnBorders(elemSprite->Attribute("border_ramp_NOR"));

	notupstairborders = getUnBorders(elemSprite->Attribute("border_upstair_NOR"));

	notdownstairborders = getUnBorders(elemSprite->Attribute("border_downstair_NOR"));

	//check for randomised tiles
	const char* spriteVariationsStr = elemSprite->Attribute("variations");
	if (spriteVariationsStr == NULL || spriteVariationsStr[0] == 0)
	{
		variations = 0;
	}
	else 
	{
		variations=atoi(spriteVariationsStr);
	}


	const char* waterDirStr = elemSprite->Attribute("water_direction");
	if (waterDirStr == NULL || waterDirStr[0] == 0)
	{
		water_direction = -1;
	}
	else 
	{
		water_direction=atoi(waterDirStr);
	}

	//decide what the sprite should be shaded by.
	const char* spriteVarColorStr = elemSprite->Attribute("color");
	if (spriteVarColorStr == NULL || spriteVarColorStr[0] == 0)
	{
		shadeBy = ShadeNone;
	}
	else
	{
		shadeBy = getShadeType(spriteVarColorStr);
	}

	//some sprites should only be drawn when the tile is chopped in half
	const char* spriteChopStr = elemSprite->Attribute("halftile");
	if (spriteChopStr == NULL || spriteChopStr[0] == 0)
	{
		halftile = HALFTILECHOP;
	}
	else if( strcmp(spriteChopStr, "chop") == 0)
	{
		halftile = HALFTILECHOP;
	}
	else if( strcmp(spriteChopStr, "yes") == 0)
	{
		halftile = HALFTILEYES;
	}
	else if( strcmp(spriteChopStr, "no") == 0)
	{
		halftile = HALFTILENO;
	}
	else if( strcmp(spriteChopStr, "both") == 0)
	{
		halftile = HALFTILEBOTH;
	}

	//hidden in the shadows
	const char* spriteShadowStr = elemSprite->Attribute("dark");
	if (spriteShadowStr == NULL || spriteShadowStr[0] == 0)
	{
		light = LIGHTANY;
	}
	else if( strcmp(spriteShadowStr, "yes") == 0)
	{
		light = LIGHTNO;
	}
	else if( strcmp(spriteShadowStr, "no") == 0)
	{
		light = LIGHTYES;
	}
	else if( strcmp(spriteShadowStr, "both") == 0)
	{
		light = LIGHTANY;
	}

	//some sprites are actually tile borders.
	const char* spriteBorderStr = elemSprite->Attribute("tileborder");
	if (spriteBorderStr == NULL || spriteBorderStr[0] == 0)
	{
		isoutline = OUTLINENONE;
	}
	else if( strcmp(spriteBorderStr, "none") == 0)
	{
		isoutline = OUTLINENONE;
	}
	else if( strcmp(spriteBorderStr, "left") == 0)
	{
		isoutline = OUTLINELEFT;
	}
	else if( strcmp(spriteBorderStr, "right") == 0)
	{
		isoutline = OUTLINERIGHT;
	}
	else if( strcmp(spriteBorderStr, "bottom") == 0)
	{
		isoutline = OUTLINEBOTTOM;
	}

	//Grass states
	const char* grass_growth_string = elemSprite->Attribute("grass_state");
	if (grass_growth_string == NULL || grass_growth_string[0] == 0)
	{
		grass_growth = GRASS_GROWTH_ANY;
	}
	else if( strcmp(grass_growth_string, "any") == 0)
	{
		grass_growth = GRASS_GROWTH_ANY;
	}
	else if( strcmp(grass_growth_string, "green") == 0)
	{
		grass_growth = GRASS_GROWTH_NORMAL;
	}
	else if( strcmp(grass_growth_string, "dry") == 0)
	{
		grass_growth = GRASS_GROWTH_DRY;
	}
	else if( strcmp(grass_growth_string, "dead") == 0)
	{
		grass_growth = GRASS_GROWTH_DEAD;
	}

	//do bodyparts
	const char* bodyPartStr = elemSprite->Attribute("bodypart");
	//clear old bodypart string
	memset(bodypart, 0, sizeof(bodypart));
	//copy new, if found
	if (bodyPartStr != NULL && bodyPartStr[0] != 0)
	{
		strcpy(bodypart, bodyPartStr);
	}

	uint8_t red, green, blue, alpha;
	//do custom colors
	const char* spriteRedStr = elemSprite->Attribute("red");
	if (spriteRedStr == NULL || spriteRedStr[0] == 0)
	{
		red = 255;
	}
	else red=atoi(spriteRedStr);
	const char* spriteGreenStr = elemSprite->Attribute("green");
	if (spriteGreenStr == NULL || spriteGreenStr[0] == 0)
	{
		green = 255;
	}
	else green=atoi(spriteGreenStr);
	const char* spriteBlueStr = elemSprite->Attribute("blue");
	if (spriteBlueStr == NULL || spriteBlueStr[0] == 0)
	{
		blue = 255;
	}
	else blue=atoi(spriteBlueStr);
	const char* spriteAlphaStr = elemSprite->Attribute("alpha");
	if (spriteAlphaStr == NULL || spriteAlphaStr[0] == 0)
	{
		alpha = 255;
	}
	else alpha=atoi(spriteAlphaStr);
	shadecolor = al_map_rgba(red, green, blue, alpha);

	//Should the sprite be shown only when there is snow?
	const char* spriteSnowMinStr = elemSprite->Attribute("snow_min");
	if (spriteSnowMinStr == NULL || spriteSnowMinStr[0] == 0)
	{
		snowmin = 0;
	}
	else snowmin=atoi(spriteSnowMinStr);
	const char* spriteSnowMaxStr = elemSprite->Attribute("snow_max");
	if (spriteSnowMaxStr == NULL || spriteSnowMaxStr[0] == 0)
	{
		snowmax = -1;
	}
	else snowmax=atoi(spriteSnowMaxStr);

	//Should the sprite be shown only when there is grass?
	const char* spriteGrassMinStr = elemSprite->Attribute("grass_min");
	if (spriteGrassMinStr == NULL || spriteGrassMinStr[0] == 0)
	{
		grassmin = 0;
	}
	else grassmin=atoi(spriteGrassMinStr);
	const char* spriteGrassMaxStr = elemSprite->Attribute("grass_max");
	if (spriteGrassMaxStr == NULL || spriteGrassMaxStr[0] == 0)
	{
		grassmax = -1;
	}
	else grassmax=atoi(spriteGrassMaxStr);

	//does the sprite match a particular grass type?
	const char* idstr = elemSprite->Attribute("grass_type");
	if (idstr == NULL || idstr[0] == 0)
	{
		grasstype = INVALID_INDEX;
	}
	else grasstype = lookupIndexedType(idstr,contentLoader->organic);

	//Should the sprite be shown only when there is blood?
	const char* spritebloodMinStr = elemSprite->Attribute("blood_min");
	if (spritebloodMinStr == NULL || spritebloodMinStr[0] == 0)
	{
		bloodmin = 0;
	}
	else bloodmin=atoi(spritebloodMinStr);
	const char* spritebloodMaxStr = elemSprite->Attribute("blood_max");
	if (spritebloodMaxStr == NULL || spritebloodMaxStr[0] == 0)
	{
		bloodmax = -1;
	}
	else bloodmax=atoi(spritebloodMaxStr);

	//Should the sprite be shown only when there is mud?
	const char* spritemudMinStr = elemSprite->Attribute("mud_min");
	if (spritemudMinStr == NULL || spritemudMinStr[0] == 0)
	{
		mudmin = 0;
	}
	else mudmin=atoi(spritemudMinStr);
	const char* spritemudMaxStr = elemSprite->Attribute("mud_max");
	if (spritemudMaxStr == NULL || spritemudMaxStr[0] == 0)
	{
		mudmax = -1;
	}
	else mudmax=atoi(spritemudMaxStr);

	//Add user settable sprite offsets
	const char* strOffsetX = elemSprite->Attribute("offsetx");
	if (strOffsetX == NULL || strOffsetX[0] == 0)
	{
		offset_user_x = 0;
	}
	else offset_user_x=atoi(strOffsetX);
	const char* strOffsetY = elemSprite->Attribute("offsety");
	if (strOffsetY == NULL || strOffsetY[0] == 0)
	{
		offset_user_y = 0;
	}
	else offset_user_y=atoi(strOffsetY);

	//not all tiles work well with an outline
	const char* spriteOutlineStr = elemSprite->Attribute("outline");
	if (spriteOutlineStr != NULL && spriteOutlineStr[0] != 0)
		needoutline=(atoi(spriteOutlineStr) == 1);

	//get the possible offset for blood bools
	const char* spriteBloodStr = elemSprite->Attribute("blood_sprite");
	if (spriteBloodStr != NULL && spriteBloodStr[0] != 0)
		bloodsprite=(atoi(spriteBloodStr) == 1);

	subsprites.clear();
	//add subsprites, if any.
	TiXmlElement* elemSubSprite = elemSprite->FirstChildElement("subsprite");
	for(TiXmlElement* elemSubType = elemSprite->FirstChildElement("subsprite");
		elemSubType;
		elemSubType = elemSubType->NextSiblingElement("subsprite"))
	{
		c_sprite subsprite;
		subsprite.set_size(spritewidth, spriteheight);
		subsprite.set_by_xml(elemSubType, fileindex);
		subsprite.set_offset(offset_x, offset_y);
		subsprites.push_back(subsprite);
	}
}
inline bool readNode(SpriteNode* node, TiXmlElement* elemNode, TiXmlElement* elemParent, SpriteBlock* &oldSibling)
{
	const char* strType = elemNode->Value();
	if (strcmp(strType, "if") == 0 || strcmp(strType, "else") == 0)
	{
		SpriteBlock* block = new SpriteBlock();
		if (!elemNode->Attribute("file") && elemParent->Attribute("file"))
		{
			elemNode->SetAttribute("file", elemParent->Attribute("file"));
		}
		if (!parseSpriteNode(block,elemNode))
		{
			delete(block);
			return false;
		}
		if (elemNode->Attribute("else") || strcmp(strType, "else") == 0)
		{
			if (!oldSibling)
			{
				contentError("Misplaced or invalid element in SpriteNode",elemNode);
				return false;						
			}
			oldSibling->addElse(block);	
		}
		else
		{
			node->addChild(block);
		}
		oldSibling = block;
	}
	else if (strcmp(strType, "rotate") == 0)
	{
		RotationBlock* block = new RotationBlock();
		if (!elemNode->Attribute("file") && elemParent->Attribute("file"))
		{
			elemNode->SetAttribute("file",elemParent->Attribute("file"));
		}
		if (!parseSpriteNode(block,elemNode))
		{
			delete(block);
			return false;
		}
		else
		{
			node->addChild(block);
		}
		oldSibling = NULL;
	}
	else if ((strcmp(strType, "sprite") == 0) || (strcmp(strType, "empty") == 0))
	{
		SpriteElement* sprite = new SpriteElement();
		const char* strSheetIndex = elemNode->Attribute("index");
		const char* strOffsetX = elemNode->Attribute("offsetx");
		const char* strOffsetY = elemNode->Attribute("offsety");
		const char* filename = elemNode->Attribute("file");
		getAnimFrames(elemNode->Attribute("frames"));
		sprite->sprite.animFrames = getAnimFrames(elemNode->Attribute("frames"));
		if (sprite->sprite.animFrames == 0)
			sprite->sprite.animFrames = ALL_FRAMES;

		sprite->sprite.sheetIndex = (strSheetIndex != 0 ? atoi(strSheetIndex) : -1);
		sprite->sprite.x    = (strOffsetX    != 0 ? atoi(strOffsetX)    : 0);
		sprite->sprite.y   = (strOffsetY    != 0 ? atoi(strOffsetY)    : 0);
		sprite->sprite.fileIndex = -1;
		if (filename != NULL && filename[0] != 0)
		{
		  	sprite->sprite.fileIndex = loadConfigImgFile((char*)filename,elemNode);
		}
		else
		{
		  const char* pfilename = elemParent->Attribute("file");
	      if (pfilename != NULL && pfilename[0] != 0)
	      {
		      sprite->sprite.fileIndex = loadConfigImgFile((char*)pfilename,elemNode);
	      }
		}
		node->addChild(sprite);
	}
	else if (strcmp(strType, "include") == 0)
	{
		if (!includeFile(node,elemNode,oldSibling))
		{
			return false;
		}
	}
	else
	{
		contentError("Misplaced or invalid element in SpriteNode",elemNode);
		return false;
	}		
	return true;
}