//
// Spawn Groups
//
//
// nChildrenSpawned
// : Number of Total Children ever Spawned
//
// nSpawnCount
// : Number of Children currently Alive
//
// nSpawnNumber
// : Number of Children to Maintain at Spawn
//
// nRandomWalk
// : Walking Randomly? TRUE/FALSE
//
// nPlaceable
// : Spawning Placeables? TRUE/FALSE
//
//
int ParseFlagValue(string sName, string sFlag, int nDigits, int nDefault);
int ParseSubFlagValue(string sName, string sFlag, int nDigits, string sSubFlag, int nSubDigits, int nDefault);
object GetChildByTag(object oSpawn, string sChildTag);
object GetChildByNumber(object oSpawn, int nChildNum);
object GetSpawnByID(int nSpawnID);
void DeactivateSpawn(object oSpawn);
void DeactivateSpawnsByTag(string sSpawnTag);
void DeactivateAllSpawns();
void DespawnChildren(object oSpawn);
void DespawnChildrenByTag(object oSpawn, string sSpawnTag);
//
//

// tinygiant -- set this to FALSE to turn off debug statement
const int IS_DEBUGGING = TRUE;

// tinygiant -- temporary function to send debug messages
void Debug(string sMessage)
{
  if (IS_DEBUGGING)
    SendMessageToPC(GetFirstPC(), sMessage);
}

string GetTemplateByCR(int nCR, string sGroupType)
{
  string sRetTemplate;

  if (sGroupType == "outdoor")
  {
    switch (nCR)
    {
    case 1:
      switch(d6(1))
      {
        case 1: sRetTemplate = "NW_SKELETON"; break;
        case 2: sRetTemplate = "NW_ZOMBIE01"; break;
        case 3: sRetTemplate = "NW_NIXIE"; break;
        case 4: sRetTemplate = "NW_ORCA"; break;
        case 5: sRetTemplate = "NW_ORCB"; break;
        case 6: sRetTemplate = "NW_BTLFIRE"; break;
      }
      break;
    case 2:
      switch(d4(1))
      {
        case 1: sRetTemplate = "NW_KOBOLD004"; break;
        case 2: sRetTemplate = "NW_KOBOLD005"; break;
        case 3: sRetTemplate = "NW_KOBOLD003"; break;
        case 4: sRetTemplate = "NW_PIXIE"; break;
    }
      break;
    case 3:
      switch(d4(1))
      {
        case 1: sRetTemplate = "NW_BTLBOMB"; break;
        case 2: sRetTemplate = "NW_BTLFIRE002"; break;
        case 3: sRetTemplate = "NW_BTLSTINK"; break;
        case 4: sRetTemplate = "NW_NYMPH"; break;
      }
      break;
    default:
       sRetTemplate = "";
       break;
    }
  }

  else if (sGroupType == "crypt")
  {
    switch (nCR)
    {
    case 1:
      switch(d4(1))
      {
        case 1:
        case 2: sRetTemplate = "NW_SKELETON"; break;
        case 3: sRetTemplate = "NW_ZOMBIE01"; break;
        case 4: sRetTemplate = "NW_ZOMBIE02"; break;
      }
      break;
    case 2:
      sRetTemplate = "NW_GHOUL";
      break;
    case 3:
      sRetTemplate = "NW_SHADOW";
      break;
    default:
       sRetTemplate = "";
       break;
    }  
}

  else
  {
    // unknown group type
    sRetTemplate = "";
  }

  return sRetTemplate;
}


// Convert a given EL equivalent and its encounter level,
// return the corresponding CR
float ConvertELEquivToCR(float fEquiv, float fEncounterLevel)
{
  float fCR, fEquivSq, fTemp;

  if (fEquiv == 0.0)
  {
    return 0.0;
  }

  fEquivSq = fEquiv * fEquiv;
  fTemp = log(fEquivSq);
  fTemp /= log(2.0);
  fCR = fEncounterLevel + fTemp;

  return fCR;
}

// Convert a given CR to its encounter level equivalent per DMG page 101.
float ConvertCRToELEquiv(float fCR, float fEncounterLevel)
{
  if (fCR > fEncounterLevel || fCR < 1.0)
  {
    return 1.;
  }

  float fEquiv, fExponent, fDenom;

  fExponent = fEncounterLevel - fCR;
  fExponent *= 0.5;
  fDenom = pow(2.0, fExponent);
  fEquiv =  1.0 / fDenom;

  return fEquiv;
}

string SpawnGroup(object oSpawn, string sTemplate)
{
    Debug("NESS: Running function SpawnGroup");
    Debug("      oSpawn -> " + GetName(oSpawn));
    Debug("      sTemplate -> " + sTemplate);
    Debug("");

    // Initialize
    string sRetTemplate;

    // Initialize Values
    int nSpawnNumber = GetLocalInt(oSpawn, "f_SpawnNumber");
    int nRandomWalk = GetLocalInt(oSpawn, "f_RandomWalk");
    int nPlaceable = GetLocalInt(oSpawn, "f_Placeable");
    int nChildrenSpawned = GetLocalInt(oSpawn, "ChildrenSpawned");
    int nSpawnCount = GetLocalInt(oSpawn, "SpawnCount");

//
// Only Make Modifications Between These Lines
// -------------------------------------------

//:: Swamp Mephit Gang
    if (sTemplate == "grp_swampmephits")
    {
        switch(d3(1))
        {
            case 1:
            sRetTemplate = "nw_mepearth";
            break;
            case 2:
            sRetTemplate = "nw_mepooze";
            break;
            case 3:
            sRetTemplate = "nw_mepwater";
            break;
        }
    }
//:: Swamp Mephit Gang	


//:: Corrupted Animals
    if (sTemplate == "grp_badanimals")
    {
        switch(d4(1))
        {
            case 1:
            sRetTemplate = "ra_badbadger001";
            break;
            case 2:
            sRetTemplate = "ra_badbear001";
            break;
            case 3:
            sRetTemplate = "ra_baddeer001";
            break;
            case 4:
            sRetTemplate = "ra_badwolf001";
            break;			
        }
    }
//:: Corrupted Animals	

//:: Undead Animals
    if (sTemplate == "grp_banimals")
    {
        switch(d3(1))
        {
            case 1:
            sRetTemplate = "ra_skelpanther01";
            break;
            case 2:
            sRetTemplate = "ra_skelrat001";
            break;
            case 3:
            sRetTemplate = "ra_zombiewolf001";
            break;
        }
    }
//:: Undead Animals	

//:: Wererats
    if (sTemplate == "group_wererats")
    {
        switch(d3(1))
        {
            case 1:
            sRetTemplate = "RA_WERERAT004";
            break;
            case 2:
            sRetTemplate = "RA_WERERAT003";
            break;
            case 3:
            sRetTemplate = "RA_WERERAT003";
            break;
        }
    }
//:: Wererats	

//:: Vampire Spawn
    if (sTemplate == "group_vampspawn")
    {
        switch(d3(1))
        {
            case 1:
            sRetTemplate = "RA_VAMPSPAWN01";
            break;
            case 2:
            sRetTemplate = "RA_VAMPSPAWN01";
            break;
            case 3:
            sRetTemplate = "RA_VAMPSPAWN02";
            break;
        }
    }
//:: Vampire Spawn	

//:: Brigands
    if (sTemplate == "group_brigands")
    {
        switch(d3(1))
        {
            case 1:
            sRetTemplate = "RA_BRIGAND001";
            break;
            case 2:
            sRetTemplate = "RA_BRIGAND001";
            break;
            case 3:
            sRetTemplate = "RA_F_BRIGAND001";
            break;
        }
    }
//:: Brigands

//:: Aragnak in Lair
    if (sTemplate == "grp_aragnak")
    {
        int iRnd = Random(9)+1;

        if (iRnd >= 7)  //  30% chance to be awake
        {
            sRetTemplate = "RA_DRAG_ARAGNAK1";
        }

        else    //  70% chance to be sleeping
        {
            sRetTemplate = "RA_DRAG_ARAGNAK2";
        }
    }
//:: Aragnak in Lair

    if (GetStringLeft(sTemplate, 7) == "scaled_")
    {
      float fEncounterLevel;
      int nScaledInProgress = GetLocalInt(oSpawn, "ScaledInProgress");
      string sGroupType = GetStringRight(sTemplate,
          GetStringLength(sTemplate) - 7);

      // First Time in for this encounter?
      if (! nScaledInProgress)
      {

        // First time in - find the party level
        int nTotalPCs = 0;
        int nTotalPCLevel = 0;

        object oArea = GetArea(OBJECT_SELF);

        object oPC = GetFirstObjectInArea(oArea);
        while (oPC != OBJECT_INVALID)
        {
          if (GetIsPC(oPC) == TRUE)
          {
              nTotalPCs++;
              nTotalPCLevel = nTotalPCLevel + GetHitDice(oPC);
          }
          oPC = GetNextObjectInArea(oArea);
        }
        if (nTotalPCs == 0)
        {
          fEncounterLevel = 0.0;
        }
        else
        {
          fEncounterLevel = IntToFloat(nTotalPCLevel) / IntToFloat(nTotalPCs);
        }

        // Save this for subsequent calls
        SetLocalFloat(oSpawn, "ScaledEncounterLevel", fEncounterLevel);

        // We're done when the CRs chosen add up to the
        // desired encounter level
        SetLocalInt(oSpawn, "ScaledCallCount", 0);
        SetLocalInt(oSpawn, "ScaledInProgress", TRUE);
      }


      fEncounterLevel = GetLocalFloat(oSpawn, "ScaledEncounterLevel");
      int nScaledCallCount = GetLocalInt(oSpawn, "ScaledCallCount");

      // For simplicity, I'm not supporting creatures with CR < 1.0)
      if (fEncounterLevel < 1.0)
      {
        // We're done... No creatures have CR low enough to add to this encounter
        sRetTemplate = "";
      }

      else
      {
        // randomly choose a CR at or below the remaining (uncovered) encounter
        // level
        int nCR = Random(FloatToInt(fEncounterLevel)) + 1;

        // cap to the largest CR we currently support in GetTemplateByCR
        if (nCR > 3)
        {
          nCR = 3;
        }

        sRetTemplate = GetTemplateByCR(nCR, sGroupType);


        // Convert CR to Encounter Level equivalent so it can be correctly
        // subtracted.  This does the real scaling work
        float fELEquiv = ConvertCRToELEquiv(IntToFloat(nCR), fEncounterLevel);
        float fElRemaining = 1.0 - fELEquiv;

        fEncounterLevel = ConvertELEquivToCR(fElRemaining, fEncounterLevel);
        SetLocalFloat(oSpawn, "ScaledEncounterLevel", fEncounterLevel);
      }

      nScaledCallCount++;
      SetLocalInt(oSpawn, "ScaledCallCount", nScaledCallCount);

      nSpawnNumber = GetLocalInt(oSpawn, "f_SpawnNumber");

      if (nScaledCallCount >= nSpawnNumber)
      {
        // reset...
        SetLocalInt(oSpawn, "ScaledInProgress", FALSE);
      }
    }

    // cr_militia
    if (sTemplate == "cr_militia")
    {
        switch(d2(1))
        {
            case 1:
            sRetTemplate = "cr_militia_m";
            break;
            case 2:
            sRetTemplate = "cr_militia_f";
            break;
        }
    }
    //

    // pg_guard
    if (sTemplate == "pg_guard")
    {
        switch(d2(1))
        {
            case 1:
            sRetTemplate = "pg_guard_m";
            break;
            case 2:
            sRetTemplate = "pg_guard_f";
            break;
        }
    }
    //

    // Goblins
    if (sTemplate == "goblins_low")
    {
        if (d2(1) == 1)
        {
            sRetTemplate = "NW_GOBLINA";
        }
        else
        {
            sRetTemplate = "NW_GOBLINB";
        }
    }
    //

    // Wererats
    if (sTemplate == "wererats")
    {
        if (d2(1) == 1)
        {
            sRetTemplate = "RA_WERERAT001";
        }
        else
        {
            sRetTemplate = "RA_WERERAT002";
        }
    }
    //

    //:: Kobolds
    if (sTemplate == "kobolds")
    {
        switch(d6(1))
        {
            case 1:
            sRetTemplate = "NW_KOBOLD001";
            break;
            case 2:
            sRetTemplate = "NW_KOBOLD002";
            break;
            case 3:
            sRetTemplate = "NW_KOBOLD003";
            break;
            case 4:
            sRetTemplate = "NW_KOBOLD004";
            break;
            case 5:
            sRetTemplate = "NW_KOBOLD005";
            break;
            case 6:
            sRetTemplate = "NW_KOBOLD006";
            break;
        }
    }
    //:: Kobolds

    // Giant Ants
    if (sTemplate == "giantants")
    {
        switch(d6(1))
        {
            case 1:
            sRetTemplate = "ra_g_ant_workr01";
            break;
            case 2:
            sRetTemplate = "ra_g_ant_soldr01";
            break;
            case 3:
            sRetTemplate = "ra_g_ant_soldr01";
            break;
            case 4:
            sRetTemplate = "ra_g_ant_workr01";
            break;
            case 5:
            sRetTemplate = "ra_g_ant_workr01";
            break;
            case 6:
            sRetTemplate = "ra_g_ant_workr01";
            break;
        }
    }
    //
        // Brigands & Leader
    if (sTemplate == "brigands01")
    {
        int nIsBossSpawned = GetLocalInt(oSpawn, "IsBossSpawned");

        Debug("NESS: nIsBossSpawned :: oSpawn -> " + GetName(oSpawn));
        Debug("NESS: nIsBossSpawned :: Value  -> " + IntToString(nIsBossSpawned));

        if (nIsBossSpawned == TRUE)
        {
            // Find the Boss
            object oBoss = GetChildByTag(oSpawn, "RA_BRIGAND002");

            Debug("NESS: oBoss -> " + (GetIsObjectValid(oBoss) ? GetName(oBoss) : "OBJECT_INVALID"));

            // Check if Boss is Alive
            if (oBoss != OBJECT_INVALID && GetIsDead(oBoss) == FALSE)
            {
                Debug("NESS: oBoss is valid and alive, assigning template 'RA_BRIGAND001'");

                // He's alive, spawn a Peon to keep him Company
                sRetTemplate = "RA_BRIGAND001";
            }
            else
            {
                Debug("NESS: oBoss is either invalid or dead, so let's respawn him.");
                sRetTemplate = "RA_BRIGAND002";
                SetLocalInt(oSpawn, "IsBossSpawned", TRUE);
                // He's dead, Deactivate Camp!
                //SetLocalInt(oSpawn, "SpawnDeactivated", TRUE);

            }
        }
        else
        {
            Debug("NESS: Boss does not exist, assiging template 'RA_BRIGAND002'");

            // No Boss, so Let's Spawn Him
            sRetTemplate = "RA_BRIGAND002";
            SetLocalInt(oSpawn, "IsBossSpawned", TRUE);
        }
    }
    //

//:: Vorlak & his Kobolds
    if (sTemplate == "vorlaks_kobolds")
    {
        int nIsBossSpawned = GetLocalInt(oSpawn, "IsBossSpawned");

        Debug("NESS: nIsBossSpawned :: oSpawn -> " + GetName(oSpawn));
        Debug("NESS: nIsBossSPawned :: Value  -> " + IntToString(nIsBossSpawned));

        if (nIsBossSpawned == TRUE)
        {
            // Find the Boss
            object oBoss = GetChildByTag(oSpawn, "OGRE_VORLAK");

            Debug("NESS: oBoss -> " + (GetIsObjectValid(oBoss) ? GetName(oBoss) : "OBJECT_INVALID"));

            // Check if Boss is Alive
            if (oBoss != OBJECT_INVALID && GetIsDead(oBoss) == FALSE)
            {
                Debug("NESS: oBoss is valid and alive, assigning templatle 'ra_koboldwar001'");

                // He's alive, spawn a Peon to keep him Company
                sRetTemplate = "ra_koboldwar001";
            }
            else
            {
                Debug("NESS: oBoss is either invalid or dead, deactivating camp");

                // He's dead, Deactivate Camp!
                SetLocalInt(oSpawn, "SpawnDeactivated", TRUE);
            }
        }
        else
        {
            Debug("NESS: Boss does not exist, assiging template 'OGRE_VORLAK'");

            // No Boss, so Let's Spawn Him
            sRetTemplate = "OGRE_VORLAK";
            SetLocalInt(oSpawn, "IsBossSpawned", TRUE);
        }
    }
//:: Vorlak & his Kobolds



    // Goblins and Boss
    if (sTemplate == "gobsnboss")
    {
        int nIsBossSpawned = GetLocalInt(oSpawn, "IsBossSpawned");

        Debug("NESS: nIsBossSpawned :: oSpawn -> " + GetName(oSpawn));
        Debug("NESS: nIsBossSPawned :: Value  -> " + IntToString(nIsBossSpawned));

        if (nIsBossSpawned == TRUE)
        {
            // Find the Boss
            object oBoss = GetChildByTag(oSpawn, "NW_GOBCHIEFA");

            Debug("NESS: oBoss -> " + (GetIsObjectValid(oBoss) ? GetName(oBoss) : "OBJECT_INVALID"));

            // Check if Boss is Alive
            if (oBoss != OBJECT_INVALID && GetIsDead(oBoss) == FALSE)
            {
                Debug("NESS: oBoss is valid and alive, assigning templatle 'NW_GOBLINA'");

                // He's alive, spawn a Peon to keep him Company
                sRetTemplate = "NW_GOBLINA";
            }
            else
            {
                Debug("NESS: oBoss is either invalid or dead, deactivating camp");

                // He's dead, Deactivate Camp!
                SetLocalInt(oSpawn, "SpawnDeactivated", TRUE);
            }
        }
        else
        {
            Debug("NESS: Boss does not exist, assiging template 'NW_GOBCHIEFA'");

            // No Boss, so Let's Spawn Him
            sRetTemplate = "NW_GOBCHIEFA";
            SetLocalInt(oSpawn, "IsBossSpawned", TRUE);
        }
    }
    //

    // Scaled Encounter
    if (sTemplate == "scaledgobs")
    {
        // Initialize Variables
        int nTotalPCs;
        int nTotalPCLevel;
        int nAveragePCLevel;
        object oArea = GetArea(OBJECT_SELF);

        // Cycle through PCs in Area
        object oPC = GetFirstObjectInArea(oArea);
        while (oPC != OBJECT_INVALID)
        {
            if (GetIsPC(oPC) == TRUE)
            {
                nTotalPCs++;
                nTotalPCLevel = nTotalPCLevel + GetHitDice(oPC);
            }
            oPC = GetNextObjectInArea(oArea);
        }
        if (nTotalPCs == 0)
        {
            nAveragePCLevel = 0;
        }
        else
        {
            nAveragePCLevel = nTotalPCLevel / nTotalPCs;
        }

        // Select a Creature to Spawn
        switch (nAveragePCLevel)
        {
            // Spawn Something with CR 1
            case 1:
                sRetTemplate = "cr1creature";
            break;
            //

            // Spawn Something with CR 5
            case 5:
                sRetTemplate = "cr5creature";
            break;
            //
        }
    }
    //

    // Pirates and Boss
    if (sTemplate == "pirates")
    {
        // Delay the Spawn for 45 Minutes
        if (GetLocalInt(oSpawn, "DelayEnded") == FALSE)
        {
            if (GetLocalInt(oSpawn, "DelayStarted") == FALSE)
            {
                // Start the Delay
                SetLocalInt(oSpawn, "DelayStarted", TRUE);
                DelayCommand(20.0, SetLocalInt(oSpawn, "DelayEnded", TRUE));
            }
            sRetTemplate = "";
            return sRetTemplate;
        }
        int nIsBossSpawned = GetLocalInt(oSpawn, "IsBossSpawned");
        if (nIsBossSpawned == TRUE)
        {
            // Find the Boss
            object oBoss = GetChildByTag(oSpawn, "NW_GOBCHIEFA");

            // Check if Boss is Alive
            if (oBoss != OBJECT_INVALID && GetIsDead(oBoss) == FALSE)
            {
                // He's alive, spawn a Peon to keep him Company
                sRetTemplate = "NW_GOBLINA";
            }
            else
            {
                // He's dead, Deactivate Camp!
                SetLocalInt(oSpawn, "SpawnDeactivated", TRUE);
            }
        }
        else
        {
            // No Boss, so Let's Spawn Him
            sRetTemplate = "NW_GOBCHIEFA";
            SetLocalInt(oSpawn, "IsBossSpawned", TRUE);
        }
    }
    //

    // Advanced Scaled Encounter
    if (sTemplate == "advscaled")
    {
        //Initalize Variables
        int nTotalPCs;
        int nTotalPCLevel;
        int nAveragePCLevel;
        object oArea = GetArea(OBJECT_SELF);

        //Cycle through PCs in area
        object oPC = GetFirstObjectInArea(oArea);
        while (oPC != OBJECT_INVALID)
        {
            if (GetIsPC(oPC) == TRUE)
            {
                nTotalPCs++;
                nTotalPCLevel = nTotalPCLevel + GetHitDice(oPC);
            }
        oPC = GetNextObjectInArea(oArea);
        }
        if (nTotalPCs == 0)
        {
            nAveragePCLevel = 0;
        }
        else
        {
            nAveragePCLevel = nTotalPCLevel / nTotalPCs;
        }

        //Select a Creature to Spawn
        switch (nAveragePCLevel)
        {
            //Spawn Something with CR 1
            case 1:
                switch (d6())
                {
                    case 1: sRetTemplate = "cr1example1";
                    case 2: sRetTemplate = "cr1example2";
                    case 3: sRetTemplate = "cr1example3";
                    case 4: sRetTemplate = "cr1example4";
                    case 5: sRetTemplate = "cr1example5";
                    case 6: sRetTemplate = "cr1example6";
                }
            break;
        }
    }
    //

    // Encounters
    if (sTemplate == "encounter")
    {
        // Declare Variables
        int nCounter, nCounterMax;
        string sCurrentTemplate;

        // Retreive and Increment Counter
        nCounter = GetLocalInt(oSpawn, "GroupCounter");
        nCounterMax = GetLocalInt(oSpawn, "CounterMax");
        nCounter++;

        // Retreive CurrentTemplate
        sCurrentTemplate = GetLocalString(oSpawn, "CurrentTemplate");

        // Check CounterMax
        if (nCounter > nCounterMax)
        {
            sCurrentTemplate = "";
            nCounter = 1;
        }

        if (sCurrentTemplate != "")
        {
            // Spawn Another CurrentTemplate
            sRetTemplate = sCurrentTemplate;
        }
        else
        {
            // Choose New CurrentTemplate and CounterMax
            switch (Random(2))
            {
                // Spawn 1-4 NW_DOGs
                case 0:
                sRetTemplate = "NW_DOG";
                nCounterMax = Random(4) + 1;
                break;
            }
            // Record New CurrentTemplate and CounterMax
            SetLocalString(oSpawn, "CurrentTemplate", sRetTemplate);
            SetLocalInt(oSpawn, "CounterMax", nCounterMax);
        }

        // Record Counter
        SetLocalInt(oSpawn, "GroupCounter", nCounter);
    }
    //

    //
    if (sTemplate == "kobolds")
    {
        int nKobold = Random(6) + 1;
        sRetTemplate = "NW_KOBOLD00" + IntToString(nKobold);
    }
    //
    //Sily's Groups
    if (sTemplate == "sily_goblin_scout")
    {
        switch(d2(1))
        {
            case 1:
            sRetTemplate = "an_goblin";
            break;
            case 2:
            sRetTemplate = "an_goblin2";
            break;
        }
    }


// -------------------------------------------
// Only Make Modifications Between These Lines
//
    return sRetTemplate;
}
//void main (){}