#include "allegro.h"

#include "Loader.h"
#include "Round.h"
#include "UnitTemplate.h"
#include "UnitCreate.h"
#include "gameConstants.h"
#include "AddUnitTemplate.h"
#include "Campaign.h"
#include "StartGameConfig.h"
#include "MusicSound.h"
#include "Unit.h"

#include <vector>
#include <string>
using namespace std;

Loader :: Loader ()
{
  lcDataFile = NULL;
  campaignDatafile = NULL;
  roundDataFile = NULL;
  youWinPic = NULL;
  youLosePic = NULL;

  unitName = new vector <string *> ();
  unitDataFile = new vector <DATAFILE *> ();
  unitTemplates = new vector <UnitTemplate *> ();

  generalDatafile = NULL;
  mouseCursor = NULL;
  goodStatusPics = new vector <BITMAP *> (numGoodStatus);
  badStatusPics = new vector <BITMAP *> (numBadStatus);

  colDepth = 0;
}

int Loader :: loadCampaign (Campaign & camp)
{
  //tries to load a campaign.

  //format: 
  //Name = name of campaign
  //Datafile = filename path of datafile for campaign
  //    will look for 'splashScreen' in datafile for the splashscreen used as the campaign splash screen
  //followed by a list of rounds.
  //[round0]
  //round data...
  //[round1]
  //round data...
  //[round2]
  //round data...
  //etc.

  //returns 0 on sucess.

  string campFilename ("data/campaigns/");
  campFilename.append (*(camp.getName ()));

  //set_config_file (camp.getFilename () -> c_str ());

  set_config_file (campFilename.c_str ());

  campaignDatafile = (char *)get_config_string ("Campaign", "Datafile", NULL);

  //try to open the datafile
  if (campaignDatafile != NULL)
    lcDataFile = load_datafile (campaignDatafile);

  if (lcDataFile == NULL)
    {
      //didn't load. Bail out!
      allegro_message ("Campaign file %s did not load. Now exiting.\n", campaignDatafile);
      return 1;
    }

  //try to import the splash screen
  camp.setBeginPic ((BITMAP *) ((find_datafile_object (lcDataFile, "splashScreen"))->dat));

  camp.setEndPic ((BITMAP *) ((find_datafile_object (lcDataFile, "victoryScreen"))->dat));

  //getting the number of rounds.
  camp.setTotalRounds (get_config_int ("Campaign", "TotalRounds", 0));

  //load the campaign song
  camp.setMusic (new string (get_config_string ("Campaign", "Music", "")));

  return 0;
}

int Loader :: loadRound (Campaign & camp, int rndNum, Round * rnd, const string & pcName, MusicSound * ms)
{
  //loads a round.
  //returns 0 on success.
  //tries to keep music playing in the meantime.
  vector <string> arenaData;
  int arenaCount;
  string arenaSettings;

  vector <string> spawnData;
  int spawnParamCount;

  int spawns;
  char convertBuffer [80];
  string roundSection;
  string spawnSection;
  UnitCreate * uC;

  int uCNum = 0;
  int uCs = 0;
  int uCcr = 0;
  int uCt = 0;
  int uCe = 0;
  int uSu = subUnitNone;
  fixed uCpx = itofix (0);
  fixed uCpy = itofix (0);

  string campFilename ("data/campaigns/");
  campFilename.append (*(camp.getName ()));
  set_config_file (campFilename.c_str ());

  //A campaign should have been loaded first.
  if (lcDataFile == NULL)
    {
      allegro_message ("loadround called with NULL datafile.\n");
      return 1;
    }

  sprintf (convertBuffer, "Round%d", rndNum);
  roundSection.assign (convertBuffer);

  get_argv (&arenaData, &roundSection, new string ("ArenaSize"), &arenaCount);

  if (arenaCount >= 2)
    rnd -> setArenaSize (itofix (atoi(arenaData.at(0).c_str())), itofix (atoi(arenaData.at(1).c_str())));

  //load the arena background
  get_argv (&arenaData, &roundSection, new string ("ArenaPic"), &arenaCount);

  if (arenaCount < 2)
    {
      allegro_message ("LoadRound: Arena has too few parameters");
      return 1;
    }
  else
    {
      //try to open the datafile
      roundDataFile = load_datafile (arenaData.at(0).c_str());

      if (roundDataFile == NULL)
	{
	  //didn't load. Bail out!
	  allegro_message ("Round file %s did not load. Now exiting.\n", arenaData.at(0).c_str());
	  return 1;
	}

      rnd -> setArenaPic ((BITMAP *) (find_datafile_object (roundDataFile, arenaData.at(1).c_str()) -> dat));

      if (arenaData.size () > 2)
	rnd -> setTitlePic ((BITMAP *) (find_datafile_object (roundDataFile, arenaData.at(2).c_str()) -> dat));
    }

  //get arena settings

  //name of round
  rnd -> setName (new string (get_config_string (roundSection.c_str (), "name",  "No Name")));

  //friction, really...
  rnd -> setArenaKB (ftofix (get_config_float (roundSection.c_str (), "ArenaKB", 0.5)));

  arenaSettings.assign (get_config_string (roundSection.c_str (), "ArenaWall", "Stop"));
  //arenaKnockback settings
  if (arenaSettings.compare ("Stop") == 0)
    rnd -> setArenaWall (arenaWallStop);
  else if (arenaSettings.compare ("Ignore") == 0)
    rnd -> setArenaWall (arenaWallIgnore);
  else if (arenaSettings.compare ("Bounce") == 0)
    rnd -> setArenaWall (arenaWallBounce);
  else if (arenaSettings.compare ("Push") == 0)
    rnd -> setArenaWall (arenaWallPush);

  //spawns
  spawns = get_config_int(roundSection.c_str (), "Spawns", 0);

  for (int j = 0; j < spawns; j ++)
    {
      sprintf (convertBuffer, "Spawn%d", j);
      spawnSection.assign (convertBuffer);

      get_argv (&spawnData, &roundSection, &spawnSection, &spawnParamCount);

      if (spawnParamCount == 7)
	{
	  //Copying variables over.
	  uCNum = atoi (spawnData.at(1).c_str ());
	  uCs = atoi (spawnData.at(2).c_str ());
	  uCcr = atoi (spawnData.at(3).c_str ());
	  uCt = atoi (spawnData.at(4).c_str ());
	  uCe = atoi (spawnData.at(5).c_str ());
	  uSu = atoi (spawnData.at(6).c_str ());

	  uC = new UnitCreate (spawnData.at(0), uCNum, uCs, uCcr, uCt, uCe, itofix (0), itofix (0), itofix (0), uSu, NULL);

	  rnd -> addUnitsToAppear (uC);
	}
      else if (spawnParamCount == 9 && (atoi(spawnData.at(2).c_str()) == 10))
	{
	  uCNum = atoi (spawnData.at(1).c_str ());
	  uCs = atoi (spawnData.at(2).c_str ());
	  uCcr = atoi (spawnData.at(3).c_str ());
	  uCt = atoi (spawnData.at(4).c_str ());
	  uCe = atoi (spawnData.at(5).c_str ());
	  uSu = atoi (spawnData.at(6).c_str ());
	  uCpx = itofix (atoi (spawnData.at(6).c_str ()));
	  uCpy = itofix (atoi (spawnData.at(7).c_str ()));

	  uC = new UnitCreate (spawnData.at(0), uCNum, uCs, uCcr, uCt, uCe, uCpx, uCpy, itofix(0), uSu, NULL);

	  rnd -> addUnitsToAppear (uC);
	}
    }

  //ok, add the pc (or at least it should be one...
  //Copying variables over.
  uCNum = 1;
  uCs = 9;
  uCcr = 1;
  uCt = 0;
  uCe = 0;

  uC = new UnitCreate (pcName, uCNum, uCs, uCcr, uCt, uCe, itofix (0), itofix (0), itofix (0), subUnitNone, NULL);
  rnd -> addUnitsToAppear (uC);
  return 0;
}

int Loader :: loadAllUnitsFromCampaign (Campaign & camp)
{
  //tries to load all of the units in a given campaign
  //returns 0 on sucess.

  vector <string> campaignData;
  int cparamCount;
  int i = 0;
  string fileName;

  //[round#]
  //UnitsToAppear = a list of unit names and the file they appear in the format:
  //NameOfUnit0 DatafileForUnit0 NameOfUnit1 DatafileForUnit1 NameOfUnit2 DatafileForUnit2 NameOfUnit3 DatafileForUnit3...
  //the filename for the data is assumed to be in a file of the same name as the unit (for example, there should be a 'homa' file for the unit homa

  string campFilename ("data/campaigns/");
  campFilename.append (*(camp.getName ()));
  set_config_file (campFilename.c_str ());

  get_argv (&campaignData, new string ("Campaign"), new string ("UnitsToAppear"), &cparamCount);

  //read the name and datafile for each unit. Add them if they don't exist already.

  for (i = 0; i < cparamCount; i ++)
    { addUnit (*(new string (campaignData.at(i)))); }

  return 0;
}

string * Loader :: dontExist (const string & unitN)
{
  //searches to see if a unit with the name unitN has been loaded.
  //returns a pointer to it if it does exist.
  //otherwise, returns NULL.

  string unitToCompare (unitN);

  //unload datafiles in unitDatafile
  vector <string *> :: iterator the_iterator;

  unitName = new vector <string *> ();

  the_iterator = unitName -> begin ();
  while(the_iterator < unitName -> end()) {
    if (unitToCompare.compare (*(*the_iterator)) == 0)
      return (*the_iterator);
    the_iterator++;
  }

  return NULL;
}

vector <UnitTemplate *> * Loader :: getUnitTemplates ()
{
  return unitTemplates;
}

UnitTemplate * Loader :: getUnitTemplate (string & uName)
{
  //searches the list of unit templates for one with the same name as uName,
  //and returns a pointer to it.
  //returns NULL if it cannot be found.

  vector <UnitTemplate *> :: iterator the_iterator;
  bool done = false;
  UnitTemplate * ans = NULL;

  the_iterator = unitTemplates -> begin ();
  while(the_iterator < unitTemplates -> end() && !done) {
    if (uName.compare((*the_iterator) -> unitType) == 0)
      {
	ans = *the_iterator;
	done = true;
      }
    the_iterator++;
  }

  return ans;
}

int Loader :: addPC (const string & pcName)
{
  //loads a player character into the round.
  //returns 0 on success.

  addUnit (pcName);
  return 0;
}

int Loader :: addUnit (const string & uN)
{
  //adds a unit. 
  //returns 0 on success.
  //returns 1 on failure.
  //returns 2 if the unit has already been made.
  //returns 3 if the unit has already been made, but loaded at a different bitdepth.

  if (dontExist (uN) == NULL)
    {
      //add entry
      unitName -> push_back (new string (uN));
      //add a UnitTemplate
      setUpUnitTemplate (*(unitName -> back()));
    }
  else if (false)
    {
      //unitTemplate -> deleteBitmaps ()
      //loadAnimation ();
      return 3;
    }
  else
    return 2;
  return 0;
}

int Loader :: setUpUnitTemplate (const string & uN)
{
  //makes and adds a unitTemplate for uN, the name of the unit.
  //also loads the datafile for it.
  //returns 0 on success.

  UnitTemplate * uttemp = NULL;
  string fileName;
  string trueOrFalse;
  bool validChar = false;

  vector <AddUnitTemplate *> * addedUnits = new vector <AddUnitTemplate *> (0);
  vector <string> addedUnitsRaw;

  vector <string> statusDurations;
  int statusLengthCount;

  int addedUnitsCount;
  int addedUnitsCount2;
  string addedUnitsStr;
  char addedUnitsBuffer [80];
  char * soundFilename = NULL;

  string affiliationName;
  SAMPLE * tempSamp = NULL;

  fileName.assign ("data/unit/");
  fileName.append (uN);
  set_config_file (fileName.c_str ());

  //read from it and set up stuff
	  
  uttemp = new UnitTemplate ();
	  	  
  uttemp -> unitType = uN;

  //getting the affiliation
  affiliationName.assign (get_config_string (NULL, "affiliation", "player"));
  if (affiliationName.compare ("player") == 0)
    uttemp -> affiliation = affiliationPlayer;
  else if (affiliationName.compare ("enemy") == 0)
    uttemp -> affiliation = affiliationEnemy;
  else if (affiliationName.compare ("neutral") == 0)
    uttemp -> affiliation = affiliationNeutral;
  else if (affiliationName.compare ("playerProjectile") == 0)
    uttemp -> affiliation = affiliationPlayerProjectile;
  else if (affiliationName.compare ("enemyProjectile") == 0)
    uttemp -> affiliation = affiliationEnemyProjectile;

  affiliationName.assign (get_config_string (NULL, "isThePC", "false"));
  if (affiliationName.compare ("true") == 0)
    uttemp -> isThePC = true;

  loadUnitProperties (uttemp);

  uttemp -> maxHP = int(get_config_int (NULL, "maxHP", 1000));
  uttemp -> timeToLive = int(get_config_int (NULL, "timeToLive", -20));
  uttemp -> walkMag = ftofix (get_config_float (NULL, "walkspeed", 5.0));
  uttemp -> juiceForPlayer = get_config_int (NULL, "juiceForPlayer", 0);

  uttemp -> alignStyle = get_config_int (NULL, "alignStyle", 0);
  uttemp -> alignDist = ftofix (get_config_float (NULL, "alignDist", 0.0));
  uttemp -> alignAngleMin = ftofix (get_config_float (NULL, "alignAngleMin", 0.0));
  uttemp -> alignAngleMax = ftofix (get_config_float (NULL, "alignAngleMax", 0.0));

  //AI:
  affiliationName.assign (get_config_string ("AI", "targetAffiliation", "player"));

  if (affiliationName.compare ("player") == 0)
    uttemp -> uI -> targetAffiliation = affiliationPlayer;
  else if (affiliationName.compare ("enemy") == 0)
    uttemp -> uI -> targetAffiliation = affiliationEnemy;
  else if (affiliationName.compare ("neutral") == 0)
    uttemp -> uI -> targetAffiliation = affiliationNeutral;
  else if (affiliationName.compare ("playerProjectile") == 0)
    uttemp -> uI -> targetAffiliation = affiliationPlayerProjectile;
  else if (affiliationName.compare ("enemyProjectile") == 0)
    uttemp -> uI -> targetAffiliation = affiliationEnemyProjectile;

  uttemp -> uI -> targetPriorityHP = (get_config_int ("AI", "targetPriorityHP", 0));
  uttemp -> uI -> switchTarget = (get_config_int ("AI", "switchTarget", switchTargetNever));
  uttemp -> uI -> homeTimeInbetweenCorrection = get_config_int ("AI", "homeTimeInbetweenCorrection", 3);
  uttemp -> uI -> homeAngleCorrection = ftofix (get_config_float ("AI", "homeAngleCorrection", 15.0));
  uttemp -> uI -> attackPeriodIndex = get_config_int ("AI", "attackPeriodIndex", 0);
  uttemp -> uI -> attackDistMin = ftofix (get_config_float ("AI", "attackDistMin", 0.0));
  uttemp -> uI -> attackDistMax = ftofix (get_config_float ("AI", "attackDistMax", 0.0));
  uttemp -> uI -> attackAngleSpread = ftofix (get_config_float ("AI", "attackAngleSpread", 256.0));


  trueOrFalse.assign (get_config_string ("AI", "careAboutArena", "false"));
  if (trueOrFalse.compare ("true") == 0)
    uttemp -> uI -> careAboutArena = true;

  //now for animation
  validChar = loadAnimation (uttemp, uN);

  //load sounds
  for (int soundi = 0; soundi < numSoundToPlay; soundi++)
    {
      soundFilename = NULL;

      switch (soundi)
	{
	case (soundToPlayHit): soundFilename = (char *)get_config_string ("Sound", "Hit", NULL); break;
	case (soundToPlayDead): soundFilename = (char *)get_config_string ("Sound", "Dead", NULL); break;
	case (soundToPlayAtt1): soundFilename = (char *)get_config_string ("Sound", "Att1", NULL); break;
	case (soundToPlayAtt2): soundFilename = (char *)get_config_string ("Sound", "Att2", NULL); break;
	case (soundToPlayAtt3): soundFilename = (char *)get_config_string ("Sound", "Att3", NULL); break;
	default: soundFilename = NULL; break;
	}
      if (soundFilename != NULL)
	{
	  tempSamp = NULL;
	  tempSamp = load_sample (soundFilename);
	  if (tempSamp == NULL)
	    tempSamp = alogg_load_ogg (soundFilename);

	  if (tempSamp != NULL)
	    uttemp -> soundsToPlay -> at (soundi) = tempSamp;
	}
    }

  //for attacks and damage.
  uttemp -> hitkbDir = ftofix (get_config_float ("damage", "kbDir", 0.0));
  uttemp -> hitkbMag = ftofix (get_config_float ("damage", "kbMag", 0.0));
  uttemp -> hitkbDur = get_config_int ("damage", "kbDuration", 0);
  uttemp -> damageDealt = get_config_int ("damage", "damageDealt", 0);

  get_argv (&statusDurations, new string ("damage"), new string ("goodStatus"), &statusLengthCount);
  uttemp -> setStatus (statusDurations, 0);

  get_argv (&statusDurations, new string ("damage"), new string ("badStatus"), &statusLengthCount);
  uttemp -> setStatus (statusDurations, 1);

  //now, for added units!
  addedUnitsCount = get_config_int(NULL, "addedUnits", 0);

  for (int i = 0; i < addedUnitsCount; i++)
    {
      sprintf (addedUnitsBuffer, "addedUnit%d", i);
      addedUnitsStr.clear ();
      addedUnitsStr.assign (addedUnitsBuffer);

      get_argv (&addedUnitsRaw, NULL, &addedUnitsStr, &addedUnitsCount2);

      if (addedUnitsCount2 == 15)
	{
	  addedUnits -> push_back (new AddUnitTemplate (new string (addedUnitsRaw.at(0)), ftofix (atof (addedUnitsRaw.at(1).c_str())), ftofix (atof (addedUnitsRaw.at(2).c_str())), atoi (addedUnitsRaw.at(3).c_str()), ftofix (atof (addedUnitsRaw.at(4).c_str())), ftofix (atof (addedUnitsRaw.at(5).c_str())), atoi (addedUnitsRaw.at(6).c_str()), atoi (addedUnitsRaw.at(7).c_str()), atoi (addedUnitsRaw.at(8).c_str()), atoi (addedUnitsRaw.at(9).c_str()), atoi (addedUnitsRaw.at(10).c_str()), atoi (addedUnitsRaw.at(11).c_str()), atoi (addedUnitsRaw.at(12).c_str()), (addedUnitsRaw.at(13).compare ("true")==0), atoi (addedUnitsRaw.at(14).c_str()) ));

	  push_config_state ();
	  addUnit (*(new string (*(addedUnits -> back () -> unitType))));
	  pop_config_state ();
	}
    }
  uttemp -> addedUnits = new vector <AddUnitTemplate *> (*addedUnits);

  if (validChar)
    unitTemplates -> push_back (uttemp);
  else
    allegro_message ("Unit %s is not valid. \n", uN.c_str ());
  
  return 0;
}

bool Loader :: loadAnimation (UnitTemplate * uttemp, const string & uN)
{
  bool validChar = false;
  string animName;
  string datafileName;
  vector <string> unitData;
  int uparamCount;
  BITMAP * tempBmp = NULL;
  DATAFILE * tempDF = NULL;
  string animPicName;
  int animIndex = 0;
  int tempInt = 0;

  set_color_conversion (COLORCONV_KEEP_TRANS | COLORCONV_TOTAL);

  //load the animation file
  datafileName.clear ();
  datafileName.append ("data/unit/");
  datafileName.append (get_config_string (NULL, "datafile", NULL));
  unitDataFile -> push_back (load_datafile (datafileName.c_str()));

  if (unitDataFile == NULL)
    {
      allegro_message ("Could not find %s, cannot load Unit.", datafileName.c_str ());
      validChar = false;
    }

  //will have to do for each animation
  if (unitDataFile != NULL)
    {
      for (int k = 0; k < charAnimNum; k ++)
	{
	  switch (k)
	    {
	    case (charAnimIdle): animName.assign ("animIdle"); animIndex = charAnimIdle; break;
	    case (charAnimRun): animName.assign ("animRun"); animIndex = charAnimRun; break;
	    case (charAnimAttack1): animName.assign ("animAttack1"); animIndex = charAnimAttack1; break;
	    case (charAnimAttack2): animName.assign ("animAttack2"); animIndex = charAnimAttack2; break;
	    case (charAnimAttack4): animName.assign ("animAttack4"); animIndex = charAnimAttack4; break;
	    case (charAnimDead): animName.assign ("animDead"); animIndex = charAnimDead; break;
	    case (charAnimStun): animName.assign ("animStun"); animIndex = charAnimStun; break;
	    case (charAnimTaunt): animName.assign ("animTaunt"); animIndex = charAnimTaunt; break;
	    case (charAnimAim): animName.assign ("animAim"); animIndex = charAnimAim; break;
	    }

	  get_argv(&unitData, new string ("Animation"), &animName, &uparamCount);

	  if (uparamCount > 3)
	    {
	      uttemp -> numLoops -> at (animIndex) = atoi (unitData.at(0).c_str ());
	      uttemp -> loopAfterFrame -> at (animIndex) = atoi (unitData.at(1).c_str ());
	      uttemp -> loopToFrame -> at (animIndex) = atoi (unitData.at(2).c_str ());
	    }

	  for (int j = 3; j + 1 < uparamCount; j += 2)
	    {
	      //add animation from the datafile to the end of the vector
	      tempBmp = NULL;
	      animPicName.assign (unitData.at(j));

	      tempDF = find_datafile_object (unitDataFile -> back (), animPicName.c_str ());
	      tempInt = atoi (unitData.at(j + 1).c_str ());
	      if (tempInt < 0) tempInt = 0;

	      if (tempDF == NULL)
		allegro_message ("could not find %s frame for %s, skipping it.\n", animPicName.c_str (), uN.c_str ());

	      if (tempDF != NULL)
		{ tempBmp = (BITMAP *) (tempDF -> dat);
		if (tempBmp != NULL)
		  { uttemp -> vecPics -> at(animIndex) -> push_back (tempBmp);
		  uttemp -> holdFrameFor -> at (animIndex) -> push_back (tempInt);
		  validChar = true;}}
	    }
	}
      if (!validChar)
	allegro_message ("no animations found for unit %s in file %s; cannot load unit. \n", uN.c_str (), datafileName.c_str());
    }

  return validChar;
}

int Loader :: loadGeneralData ()
{
  //loads general data.
  generalDatafile = load_datafile ("data/generalData.dat");

  if (generalDatafile == NULL)
    {
      //didn't load. Bail out!
      allegro_message ("General datafile did not load. Now exiting.\n");
      return 1;
    }

  //now get stuff!!
  mouseCursor = (BITMAP *) ((find_datafile_object (generalDatafile, "cursor"))->dat);

  goodStatusPics -> at (0) = (BITMAP *) ((find_datafile_object (generalDatafile, "goodStatus0"))->dat);
  goodStatusPics -> at (1) = (BITMAP *) ((find_datafile_object (generalDatafile, "goodStatus1"))->dat);
  goodStatusPics -> at (2) = (BITMAP *) ((find_datafile_object (generalDatafile, "goodStatus2"))->dat);
  goodStatusPics -> at (3) = (BITMAP *) ((find_datafile_object (generalDatafile, "goodStatus3"))->dat);

  badStatusPics -> at (0) = (BITMAP *) ((find_datafile_object (generalDatafile, "badStatus0"))->dat);
  badStatusPics -> at (1) = (BITMAP *) ((find_datafile_object (generalDatafile, "badStatus1"))->dat);
  badStatusPics -> at (2) = (BITMAP *) ((find_datafile_object (generalDatafile, "badStatus2"))->dat);
  badStatusPics -> at (3) = (BITMAP *) ((find_datafile_object (generalDatafile, "badStatus3"))->dat);

  //------
  youWinPic = (BITMAP *) ((find_datafile_object (generalDatafile, "youWin"))->dat);
  youLosePic = (BITMAP *) ((find_datafile_object (generalDatafile, "youLose"))->dat);

  return 0;
}

void Loader :: loadUnitProperties (UnitTemplate * utt)
{
  //unit properties are loaded here.
  vector <string> propertyStringData (0);
  int propertySize;
  string subsection;
  string section ("UnitProperties");

  for (int j = 0; j < 4; j++)
    {
      if (j == 0)
	subsection.assign ("Player");
      else if (j == 1)
	subsection.assign ("Hitbox");
      else if (j == 2)
	subsection.assign ("Status");
      else if (j == 3)
	subsection.assign ("Defense");

      get_argv (&propertyStringData, &section, &subsection, &propertySize);
      
      //now search for criterion
      for (int i = 0; i < propertySize; i ++)
	{
	  if (propertyStringData.at(i).compare ("InvincibleWhenHit") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerInvincibleWhenHit;

	  if (propertyStringData.at(i).compare ("ChainAttack1") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerChainAttack1;

	  if (propertyStringData.at(i).compare ("ChainAttack2") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerChainAttack2;

	  if (propertyStringData.at(i).compare ("ChainAttack4") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerChainAttack4;

	  if (propertyStringData.at(i).compare ("ClearStunFor3rdAttack") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerClearStunFor3rdAttack;

	  if (propertyStringData.at(i).compare ("IgnoreHealthFor1stAttack") == 0)
	    utt -> unitPropertyPlayer += unitPropertyPlayerIgnoreHealthFor1stAttack;

	  if (propertyStringData.at(i).compare ("HitsOpponentWhenStunned") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsOpponentWhenStunned;

	  if (propertyStringData.at(i).compare ("HitsAllyWhenStunned") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsAllyWhenStunned;

	  if (propertyStringData.at(i).compare ("GoesThroughOpponent") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxGoesThroughOpponent;

	  if (propertyStringData.at(i).compare ("GoesThroughAlly") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxGoesThroughAlly;

	  if (propertyStringData.at(i).compare ("HitsPlayer") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsPlayer;

	  if (propertyStringData.at(i).compare ("HitsEnemy") == 0)
	    utt -> unitPropertyHitbox +=  unitPropertyHitboxHitsEnemy;

	  if (propertyStringData.at(i).compare ("HitsPlayerProjectile") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsPlayerProjectile;

	  if (propertyStringData.at(i).compare ("HitsEnemyProjectile") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsEnemyProjectile;

	  if (propertyStringData.at(i).compare ("HitsNeutral") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsNeutral;

	  if (propertyStringData.at(i).compare ("DiesWhenHitsWall") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenHitsWall;

	  if (propertyStringData.at(i).compare ("DiesWhenPlayerHits") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenPlayerHits;

	  if (propertyStringData.at(i).compare ("DiesWhenPlayerProjectileHits") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenPlayerProjectileHits;
	  if (propertyStringData.at(i).compare ("DiesWhenEnemyHits") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenEnemyHits;

	  if (propertyStringData.at(i).compare ("DiesWhenEnemyProjectileHits") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenEnemyProjectileHits;

	  if (propertyStringData.at(i).compare ("DiesWhenNeutralHits") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDiesWhenNeutralHits;

	  if (propertyStringData.at(i).compare ("HitsIndestructible") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxHitsIndestructible;

	  if (propertyStringData.at(i).compare ("DealsNoKnockback") == 0)
	    utt -> unitPropertyHitbox += unitPropertyHitboxDealsNoKnockback;

	  if (propertyStringData.at(i).compare ("ResistGoodAilments") == 0)
	    utt -> unitPropertyStatus += unitPropertyStatusResistGoodAilments;

	  if (propertyStringData.at(i).compare ("ResistBadAilments") == 0)
	    utt -> unitPropertyStatus += unitPropertyStatusResistBadAilments;

	  if (propertyStringData.at(i).compare ("ImmuneGoodAilments") == 0)
	    utt -> unitPropertyStatus += unitPropertyStatusImmuneGoodAilments;

	  if (propertyStringData.at(i).compare ("ImmuneBadAilments") == 0)
	    utt -> unitPropertyStatus += unitPropertyStatusImmuneBadAilments;

	  if (propertyStringData.at(i).compare ("Indestructible") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseIndestructible;

	  if (propertyStringData.at(i).compare ("Bouncy") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseBouncy;

	  if (propertyStringData.at(i).compare ("Heavy") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseHeavy;

	  if (propertyStringData.at(i).compare ("NoDamage") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseNoDamage;

	  if (propertyStringData.at(i).compare ("NoKnockBack") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseNoKnockBack;

	  if (propertyStringData.at(i).compare ("IgnoreWallKnockBack") == 0)
	    utt -> unitPropertyDefense += unitPropertyDefenseIgnoreWallKnockback;
	}
    }
}

void Loader :: get_argv (vector <string> * ans, string * section, string * name, int * argc)
{
  //reads stuff in the form of Allegro's get_config_argv function, but it stores the answer in ans. Much safer (no memory leaks or seg faults!)
  
  if (section != NULL)
    superData = get_config_argv(section -> c_str (), name -> c_str(), argc);
  else
    superData = get_config_argv(NULL, name -> c_str(), argc);

  ans -> clear ();
  ans -> resize (*argc);

  for (int i = 0; i < *argc; i ++)
    {
      ans -> at(i).assign ((superData [i]));
    }
}

BITMAP * Loader :: getMouseCursor ()
{ return mouseCursor; }

BITMAP * Loader :: getYouWinPic ()
{ return youWinPic; }

BITMAP * Loader :: getYouLosePic ()
{ return youLosePic; }

vector <BITMAP *> * Loader :: getGoodStatusPics ()
{ return goodStatusPics; }

vector <BITMAP *> * Loader :: getBadStatusPics ()
{ return badStatusPics; }

void Loader :: unloadAll ()
{
  //unloads everything.
  if (roundDataFile != NULL)
    unload_datafile (roundDataFile);

  if (lcDataFile != NULL)
    unload_datafile (lcDataFile);

  if (mouseCursor != NULL)
    destroy_bitmap (mouseCursor);

  if (generalDatafile != NULL)
    unload_datafile (generalDatafile);

  //unload datafiles in unitDatafile
  vector <DATAFILE *> :: iterator the_iterator;

  the_iterator = unitDataFile -> begin ();
  while(the_iterator < unitDataFile -> end()) 
    {unload_datafile ((*the_iterator));
    the_iterator++;}

  vector <BITMAP *> :: iterator iter;
  iter = goodStatusPics -> begin ();
  while(iter < goodStatusPics -> end())
    {destroy_bitmap ((*iter));
    iter++;}
  iter = badStatusPics -> begin ();
  while(iter < badStatusPics -> end()) 
    {destroy_bitmap ((*iter));
    iter++;}
}

Loader :: ~Loader ()
{ unloadAll (); }


int Loader :: loadCharAndCamp (StartGameConfig * sgc)
{
  //load from the files listOfCharacters.txt and listOfCampaigns.txt
  //input information into sgc

  //open file, read number of entries

  string name;
  string desc;
  string endOfDesc ("[End]");

  char thisLine [256];
  string checker;

  unsigned int newlinePos = 0;
  unsigned int foundEndOfDesc = 0;
  bool getName = true;

  FILE * textFile = NULL;

  for (int i = 0; i < 2; i ++)
    {

      if (i == 0)
	textFile = fopen ("data/listOfCampaigns.txt", "r");
      else
	textFile = fopen ("data/listOfCharacters.txt", "r");

      while (feof (textFile) == 0)
	{
	  //read the next line
	  fgets (thisLine, 256, textFile);
	  checker.assign (thisLine);
      
	  //are we getting names, or descriptions?
	  if (getName)
	    {
	      newlinePos = checker.find ("\n", 0);
	      
	      if (newlinePos != string :: npos)
		{
		  checker.erase (newlinePos, 1);
		}

	      name.assign (checker);
	      
	      getName = false;
	    }
	  else
	    {
	      //first check to see if it has read the end of description mod.
	      foundEndOfDesc = checker.find (endOfDesc);
	      
	      if (foundEndOfDesc == string :: npos)
		{
		  //OK. read and format the line.
		  desc.append (checker);
		}
	      else
		{
		  //this name/desc pair is done. Add it and reset.
		  //add to sgc here

		  if (i == 0)
		    sgc -> addCampName (name, desc);
		  else
		    sgc -> addCharName (name, desc);

		  desc.clear ();
		  name.clear ();

		  newlinePos = 0;
		  foundEndOfDesc = 0;
		  getName = true;
		}
	    }
	}
      
      fclose (textFile);
    }

  return 0;
}
