////////////////////////////////////////////////////////////////////////
// Astral Plane Heartbeat
////////////////////////////////////////////////////////////////////////
#include "antistuck_h"

int fnCountResidents()
{ // return how many NPCs in the area
  int nRet=0;
  int nC=1;
  object oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
  while(oCr!=OBJECT_INVALID)
  { // count
    nRet++;
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
  } // count
  return nRet;
} // fnCountResidents()

void fnMoveTo()
{ // move to point and despawn
  object oMe=OBJECT_SELF;
  object oDest=GetLocalObject(oMe,"oBDest");
  float fDist=GetDistanceBetween(oMe,oDest);
  effect eVis=EffectVisualEffect(VFX_FNF_IMPLOSION);
  if (GetIsInCombat(oMe)!=TRUE)
  { // not fighting
    if (fDist<3.0)
    { // arrived
      ApplyEffectToObject(DURATION_TYPE_INSTANT,eVis,oMe,3.0);
      DelayCommand(2.0,DestroyObject(oMe));
    } // arrived
    else
    { // move
      AssignCommand(oMe,ASActionMoveToObject(oDest,FALSE,1.0));
    } // move
  } // not fighting
  DelayCommand(10.0,fnMoveTo());
} // fnMoveTo()

void fnDestroyContainer(object oContainer)
{ // empty items
  object oItem=GetFirstItemInInventory(oContainer);
  while(oItem!=OBJECT_INVALID)
  {
    DelayCommand(0.2,DestroyObject(oItem));
    oItem=GetNextItemInInventory(oContainer);
  }
  DelayCommand(0.3,DestroyObject(oContainer));
} // fnDestroyContainer()

object fnSpawn(string sRes,location lLoc,int nVis=VFX_FNF_SUMMON_MONSTER_1)
{ // create creature
  object oRet;
  effect eVis=EffectVisualEffect(nVis);
  ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis,lLoc,1.0);
  oRet=CreateObject(OBJECT_TYPE_CREATURE,sRes,lLoc,FALSE);
  return oRet;
} // fnSpawn()


void fnWander()
{ // wander
  object oMe=OBJECT_SELF;
  object oEnemy=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  int nR;
  if (GetIsInCombat(oMe)!=TRUE&&oEnemy==OBJECT_INVALID)
  { // wander
    oEnemy=GetNearestObject(OBJECT_TYPE_ITEM,oMe,1);
    if (oEnemy!=OBJECT_INVALID&&GetDistanceBetween(oEnemy,oMe)<20.0)
    { // cleanup item
      AssignCommand(oMe,ClearAllActions());
      AssignCommand(oMe,ActionMoveToObject(oEnemy,TRUE,1.0));
      AssignCommand(oMe,ActionDoCommand(fnDestroyContainer(oEnemy)));
    } // cleanup item
    else
    { // check for containers
      oEnemy=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,1);
      if (oEnemy!=OBJECT_INVALID&&GetDistanceBetween(oEnemy,oMe)<20.0&&GetHasInventory(oEnemy)==TRUE)
      { // empty container
        AssignCommand(oMe,ClearAllActions());
        AssignCommand(oMe,ActionMoveToObject(oEnemy,TRUE,1.0));
        AssignCommand(oMe,ActionDoCommand(fnDestroyContainer(oEnemy)));
      } // empty container
      else
      { // move
        nR=d20();
        oEnemy=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,nR);
        AssignCommand(oMe,ASActionMoveToObject(oEnemy,FALSE,1.0));
      } // move
    }// check for containers
  } // wander
  DelayCommand(15.0,fnWander());
} // fnWander()

void fnDestroy()
{ // destroy self
  object oMe=OBJECT_SELF;
  object oEnemy=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  effect eVis=EffectVisualEffect(VFX_FNF_IMPLOSION);
  if (GetIsInCombat(oMe)!=TRUE&&oEnemy==OBJECT_INVALID)
  { // Destroy Self
    ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis,GetLocation(oMe),3.0);
    DelayCommand(1.0,DestroyObject(oMe));
  } // Destroy Self
  else
  { // wait a bit longer
    DelayCommand(20.0,fnDestroy());
  } // wait a bit longer
} // fnDestroy()


void fnAssignBehavior(object oCreature,int nBehavior)
{ // start the behavior for the NPC creature
  // 1 = wander for 2 game hours
  // 2 = wander for 4 game hours
  // 3 = go to another spawn point and despawn
  // 4 = wander for 6 game hours
  object oDest;
  object oNear;
  int nR;
  float fDelay=HoursToSeconds(2);
  if (nBehavior==2) fDelay=HoursToSeconds(4);
  else if (nBehavior==4) fDelay=HoursToSeconds(6);
  if (nBehavior!=3)
  { // wander
    AssignCommand(oCreature,fnWander());
    AssignCommand(oCreature,DelayCommand(fDelay,fnDestroy()));
  } // wander
  else
  { // move to destination
    oNear=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oCreature,1);
    oDest=OBJECT_INVALID;
    if (oNear!=OBJECT_INVALID)
    { // see if has a destination
      oDest=GetLocalObject(oNear,"oBDest");
    } // see if has a destination
    if (oDest==OBJECT_INVALID)
    { // find a destination
      oNear=GetNearestObject(OBJECT_TYPE_WAYPOINT,oCreature,1);
      nR=d6();
      oDest=GetWaypointByTag("ASTRAL_SPAWN"+IntToString(nR));
      if (oDest==oNear)
      { // pick another
        nR++;
        if (nR>6) nR=1;
        oDest=GetWaypointByTag("ASTRAL_SPAWN"+IntToString(nR));
      } // pick another
    } // find a destination
    SetLocalObject(oCreature,"oBDest",oDest);
    AssignCommand(oCreature,fnMoveTo());
  } // move to destination
} // fnAssignBehavior()

void fnResidents(object oMe)
{ // determine if need to spawn creatures and handle their behaviour
  object oArea=GetArea(oMe);
  object oMod=GetModule();
  int nEvil=GetLocalInt(oMod,"nEvilPower");
  int nR;
  int nC=fnCountResidents();
  int nSP=d6();
  object oCR;
  object oSpawnPoint=GetWaypointByTag("ASTRAL_SPAWN"+IntToString(nSP));
  location lLoc=GetLocation(oSpawnPoint);
  string sRes;
  int nBehavior=d4();
  //SendMessageToPC(GetFirstPC()," COUNT of ASTRAL CREATURES:"+IntToString(nC));
  if (nC<8)
  { // spawn chance
    nR=d100();
    //SendMessageToPC(GetFirstPC(),"Rolled a "+IntToString(nR)+" percentile.");
    if (nR<21)
    { // spawn
      nR=d100()+(nEvil/100);
      if (nR<34)
      { // Githyanki
        nR=d100();
        if (nR<11)
        { // lone warlock
          oCR=fnSpawn("githyankiwarlock",lLoc);
          fnAssignBehavior(oCR,nBehavior);
        } // lone warlock
        else if (nR<21)
        { // lone gith
          oCR=fnSpawn("githyankigith",lLoc);
          fnAssignBehavior(oCR,nBehavior);
        } // lone gith
        else if (nR<95)
        { // company
          oCR=fnSpawn("githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=fnSpawn("githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=fnSpawn("githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=fnSpawn("githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
        } // company
        else
        { // squad
          oCR=fnSpawn("githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"githyankiwarrior",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"githyankiwarlock",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"githyankigith",lLoc);
          fnAssignBehavior(oCR,3);
        } // squad
      } // Githyanki
      else if (nR<60)
      { // Deva
        nR=d100();
        if (nR<34)
        { // Movanic
          oCR=fnSpawn("movanicdeva",lLoc,VFX_FNF_SUMMON_MONSTER_2);
          fnAssignBehavior(oCR,nBehavior);
        } // Movanic
        else if (nR<67)
        { // Monadic
          oCR=fnSpawn("monadicdeva",lLoc,VFX_FNF_SUMMON_MONSTER_2);
          fnAssignBehavior(oCR,nBehavior);
        } // Monadic
        else if (nR<91)
        { // Astral
          oCR=fnSpawn("astraldeva",lLoc,VFX_FNF_SUMMON_MONSTER_3);
          fnAssignBehavior(oCR,nBehavior);
        } // Astral
        else if (nR<99)
        { // Planetar
          oCR=fnSpawn("planetar",lLoc,VFX_FNF_SUMMON_CELESTIAL);
          fnAssignBehavior(oCR,nBehavior);
        } // Planetar
        else
        { // solar
          oCR=fnSpawn("solar",lLoc,VFX_FNF_SUMMON_CELESTIAL);
          fnAssignBehavior(oCR,nBehavior);
        } // solar
      } // Deva
      else if (nR<75)
      { // elementals
        nR=d100();
        if (nR<34)
        { // fire
          nR=d100();
          if (nR<21) sRes="fire001";
          else if (nR<91) sRes="firehuge001";
          else { sRes="fireelder001"; }
          nR=VFX_IMP_FLAME_M;
        } // fire
        else if (nR<67)
        { // earth
          nR=d100();
          if (nR<21) sRes="earth001";
          else if (nR<91) sRes="earthhuge001";
          else { sRes="eartheld001"; }
          nR=VFX_IMP_BREACH;
        } // earth
        else
        { // air
          nR=d100();
          if (nR<21) sRes="air001";
          else if (nR<91) sRes="airhuge001";
          else { sRes="airelder001"; }
          nR=VFX_IMP_LIGHTNING_M;
        } // air
        oCR=fnSpawn(sRes,lLoc,nR);
        fnAssignBehavior(oCR,nBehavior);
      } // elementals
      else if (nR<85)
      { // miscellaneous
        nR=d100();
        if (nR<34)
        { // passing through
          oCR=fnSpawn("azer002",lLoc,VFX_IMP_FLAME_M);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"azer002",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"azer003",lLoc);
          fnAssignBehavior(oCR,3);
          oCR=CreateObject(OBJECT_TYPE_CREATURE,"azer003",lLoc);
          fnAssignBehavior(oCR,3);
        } // passing through
        else
        { // other
          nR=d100();
          if (nR<8)
          {
            nR=VFX_FNF_SUMMON_MONSTER_2;
            sRes="nighthag";
          }
          else if (nR<20)
          {
            nR=VFX_FNF_SUMMON_MONSTER_2;
            sRes="rakshasa001";
          }
          else if (nR<30)
          {
            nR=VFX_FNF_SUMMON_MONSTER_1;
            sRes="dmquasit001";
          }
          else if (nR<40)
          {
            nR=VFX_FNF_SUMMON_MONSTER_1;
            sRes="imp001";
          }
          else if (nR<50)
          {
            nR=VFX_FNF_SUMMON_MONSTER_2;
            sRes="drider002";
          }
          else if (nR==50)
          {
            nR=VFX_FNF_SUMMONDRAGON;
            sRes="dragonpris002";
          }
          else if (nR==51)
          {
            nR=VFX_FNF_UNDEAD_DRAGON;
            sRes="dracolich002";
          }
          else if (nR==52)
          {
            nR=VFX_FNF_SUMMON_EPIC_UNDEAD;
            sRes="lich002";
          }
          else if (nR==53)
          {
            nR=VFX_FNF_SUMMON_UNDEAD;
            sRes="demilich003";
          }
          else
          { // Slaad
            nR=d100();
            if (nR<34)
              sRes="slaadred001";
            else if (nR<51)
              sRes="slaadbl001";
            else if (nR<76)
              sRes="slaadgrn001";
            else if (nR<88)
              sRes="slaadgray001";
            else if (nR<95)
              sRes="slaaddeth001";
            else if (nR<99)
              sRes="slaadwhite002";
            else { sRes="slaadblack002"; }
            nR=VFX_FNF_SUMMON_MONSTER_3;
          } // slaad
          oCR=fnSpawn(sRes,lLoc,nR);
          fnAssignBehavior(oCR,nBehavior);
        } // other
      } // miscellaneous
      else
      { // fiends
        nR=d100();
        if (nR<50)
        { // least
          nR=d100();
          if (nR<51)
             sRes="erinyes001";
          else { sRes="dmsucubus001"; }
        } // least
        else if (nR<70)
        { // lesser
          nR=d100();
          if (nR<51)
            sRes="maurezhi";
          else { sRes="dmvrock001"; }
        } // lesser
        else if (nR<80)
        { // mid
          nR=d100();
          if (nR<51)
            sRes="devil002";
          else { sRes="demon001"; }
        } // mid
        else if (nR<99)
        { // upper
          nR=d100();
          if (nR<51)
            sRes="devil003";
          else { sRes="balorboss001"; }
        } // upper
        else
        { // greatest
          sRes="demonlord";
        } // greatest
        oCR=fnSpawn(sRes,lLoc,VFX_FNF_SUMMON_GATE);
        fnAssignBehavior(oCR,nBehavior);
      } // fiends
      //SendMessageToPC(GetFirstPC(),"A "+GetName(oCR)+" should have spawned at "+GetName(oSpawnPoint)+".");
    } // spawn
  } // spawn chance
} // fnResidents()

void fnVisualEffects(object oMe)
{ // do Astral plane visual effects
  effect eVis1=EffectVisualEffect(VFX_IMP_TORNADO);
  effect eVis2=EffectVisualEffect(VFX_FNF_SMOKE_PUFF);
  effect eVis3=EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_ACID);
  effect eVis4=EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_EVIL);
  effect eVis5=EffectVisualEffect(VFX_FNF_GAS_EXPLOSION_NATURE);
  effect eVis6=EffectVisualEffect(VFX_FNF_ELECTRIC_EXPLOSION);
  int nC=1;
  int nR;
  int nX;
  int nY;
  int nZ;
  vector vVec;
  location lLoc;
  object oArea=GetArea(oMe);
  while(nC<9)
  { // play visual effects
    nX=Random(16)*10;
    nY=Random(16)*10;
    nZ=Random(4)*6;
    vVec=Vector(IntToFloat(nX),IntToFloat(nY),IntToFloat(nZ));
    lLoc=Location(oArea,vVec,0.0);
    nR=d20();
    if (nR<3)
      ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY,eVis1,lLoc,6.0);
    else if (nR<8)
      ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis2,lLoc,3.0);
    else if (nR<12)
      ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis3,lLoc,3.0);
    else if (nR<15)
      ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis4,lLoc,3.0);
    else if (nR<18)
      ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis5,lLoc,3.0);
    else { ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eVis6,lLoc,3.0); }
    nC++;
  } // play visual effects
} // fnResidents()


void main()
{
   object oMe=OBJECT_SELF;
   object oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,oMe,1);
   if (oPC!=OBJECT_INVALID)
   { // PCs present
     SendMessageToPC(oPC,"You are wandering through the astral plane.");
     fnResidents(oMe);
     fnVisualEffects(oMe);
   } // PCs present
   ExecuteScript("area_hb_clean",oMe);
}