///////////////////////////////////////////////////////////////////
// Roving Merchant - Version 2.0
// By Deva Bryson Winblood.  04/12/2004
///////////////////////////////////////////////////////////////////
#include "antistuck_h"

//////////////////////////
// PROTOTYPES
//////////////////////////
void fnRelaunch(int nCompare);
string fnRandomStartingGood();
string fnBarbarianGoods();
string fnMurinsGoods();
string fnWoodmans();
string fnPotions();
string fnDesert();
void fnWatchForMaster(object oRMG);
int fnStoreHasHowMany(object oStore,string sTag,int nMax=1000);
void fnDoTrade(object oOx,object oStore,int nParm,object oRM);
void fnMessageStuck();
void fnDespawn(object oOb);

///////////////////////////////////////////////////////////////////////// MAIN
void main()
{// main
   int CONST_FOLLOW = 4;
   int CONST_RECON = 14;
   object oMod=GetModule();
   object oRMG=GetLocalObject(oMod,"oRMGMerchant");
   object oRMGG1=GetLocalObject(oMod,"oRMGG1");
   object oRMGG2=GetLocalObject(oMod,"oRMGG2");
   object oRMGG3=GetLocalObject(oMod,"oRMGG3");
   object oRMGG4=GetLocalObject(oMod,"oRMGG4");
   object oRMGG5=GetLocalObject(oMod,"oRMGG5");
   object oRMGOx=GetLocalObject(oMod,"oRMGOx");
   object oDest=GetLocalObject(oMod,"oRMGDest");
   int nState=GetLocalInt(oMod,"nRMGState");
   int nSpeed=GetLocalInt(oMod,"nRMGSpeed");
   int nKilled=GetLocalInt(oMod,"nRMGKilled");
   int nDestNum=GetLocalInt(oMod,"nRMGDestN");
   int nC;
   int nR;
   int nParm;
   string sResRMG="rovingmerchant";
   string sResMonk="rmgmonk";
   string sResRanger="rmgranger";
   string sResSorc="rmgsorc";
   string sResElite="rmgelite";
   string sResOx="rmgoxen";
   object oWP;
   float fDist;
   float fLDist=GetLocalFloat(oRMG,"fLDist");
   object oStore;
   object oItem;
   string sName;
   int nRMGLaunch=GetLocalInt(oMod,"nRMGLaunch");
   effect eSleep=EffectVisualEffect(VFX_IMP_SLEEP);
   int nDisableNonEssential=GetLocalInt(oMod,"nDisableNonEssential");
   if (nDisableNonEssential!=TRUE)
   { // !Disabled
     if (oRMG==OBJECT_INVALID&&oDest==OBJECT_INVALID)
     { // spawn a new merchant-----------------[ SPAWN ]----------------------
       //SendMessageToPC(GetFirstPC(),"  Spawn new merchant.");
       if (oRMGG1!=OBJECT_INVALID) fnDespawn(oRMGG1);
       if (oRMGG2!=OBJECT_INVALID) fnDespawn(oRMGG2);
       if (oRMGG3!=OBJECT_INVALID) fnDespawn(oRMGG3);
       if (oRMGG4!=OBJECT_INVALID) fnDespawn(oRMGG4);
       if (oRMGG5!=OBJECT_INVALID) fnDespawn(oRMGG5);
       if (oRMGOx!=OBJECT_INVALID) fnDespawn(oRMGOx);
       SetLocalInt(oMod,"nRMGDestN",0);
       SetLocalInt(oMod,"nRMGSpeed",12);
       SetLocalInt(oMod,"nRMGState",0);
       oWP=GetWaypointByTag("RMG_SPAWN");
       //SendMessageToPC(GetFirstPC(),"    Should spawn '"+sResRMG+"' @:"+GetTag(oWP)+" in Area:"+GetName(GetArea(oWP)));
       oRMG=CreateObject(OBJECT_TYPE_CREATURE,sResRMG,GetLocation(oWP));
       SetLocalObject(oMod,"oRMGMerchant",oRMG);
       SetAILevel(oRMG,AI_LEVEL_NORMAL);
       SetLocalString(oRMG,"sTeamID","RMG");
       //SendMessageToPC(GetFirstPC(),"RMG="+GetName(oRMG)+" LOCATION="+GetName(GetArea(oRMG)));
       if (nKilled<4)
       { // not elite
         oRMGG1=CreateObject(OBJECT_TYPE_CREATURE,sResMonk,GetLocation(oWP));
         SetLocalString(oRMGG1,"sTeamID","RMG");
         SetLocalObject(oRMGG1,"oDestWP",oRMG);
         SetLocalInt(oRMGG1,"nMState",CONST_RECON);
         SetAILevel(oRMGG1,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG1,"nParm",1); // 3 meters
         SetLocalInt(oRMGG1,"nRun",TRUE);
         SetLocalObject(oMod,"oRMGG1",oRMGG1);
         oRMGG2=CreateObject(OBJECT_TYPE_CREATURE,sResMonk,GetLocation(oWP));
         SetLocalString(oRMGG2,"sTeamID","RMG");
         SetLocalObject(oRMGG2,"oDestWP",oRMG);
         SetLocalInt(oRMGG2,"nMState",CONST_RECON);
         SetLocalObject(oMod,"oRMGG2",oRMGG2);
         SetAILevel(oRMGG2,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG2,"nParm",1); // 3 meters
         SetLocalInt(oRMGG2,"nRun",TRUE);
       } // not elite
       else
       { // elite
         oRMGG1=CreateObject(OBJECT_TYPE_CREATURE,sResElite,GetLocation(oWP));
         SetLocalString(oRMGG1,"sTeamID","RMG");
         SetLocalObject(oRMGG1,"oDestWP",oRMG);
         SetLocalInt(oRMGG1,"nMState",CONST_RECON);
         SetLocalObject(oMod,"oRMGG1",oRMGG1);
         SetAILevel(oRMGG1,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG1,"nRun",TRUE);
         SetLocalInt(oRMGG1,"nParm",1); // 3 meters
         oRMGG2=CreateObject(OBJECT_TYPE_CREATURE,sResElite,GetLocation(oWP));
         SetLocalString(oRMGG2,"sTeamID","RMG");
         SetLocalObject(oRMGG2,"oDestWP",oRMG);
         SetLocalInt(oRMGG2,"nMState",CONST_RECON);
         SetLocalObject(oMod,"oRMGG2",oRMGG2);
         SetAILevel(oRMGG2,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG2,"nRun",TRUE);
         SetLocalInt(oRMGG2,"nParm",1); // 3 meters
       } // elite
       if (nKilled>0)
       { // rangers
         oRMGG3=CreateObject(OBJECT_TYPE_CREATURE,sResRanger,GetLocation(oWP));
         SetLocalString(oRMGG3,"sTeamID","RMG");
         SetLocalObject(oRMGG3,"oDestWP",oRMG);
         SetLocalInt(oRMGG3,"nMState",CONST_FOLLOW);
         SetLocalObject(oMod,"oRMGG3",oRMGG3);
         SetAILevel(oRMGG3,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG3,"nFollowDist",1); // 7 meters
         oRMGG4=CreateObject(OBJECT_TYPE_CREATURE,sResRanger,GetLocation(oWP));
         SetLocalString(oRMGG4,"sTeamID","RMG");
         SetLocalObject(oRMGG4,"oDestWP",oRMG);
         SetLocalObject(oMod,"oRMGG4",oRMGG4);
         SetLocalInt(oRMGG4,"nMState",CONST_FOLLOW);
         SetAILevel(oRMGG3,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG3,"nFollowDist",1); // 7 meters
       } // rangers
       if (nKilled>1)
       { // sorceress
         oRMGG5=CreateObject(OBJECT_TYPE_CREATURE,sResSorc,GetLocation(oWP));
         SetLocalString(oRMGG5,"sTeamID","RMG");
         SetLocalObject(oRMGG5,"oDestWP",oRMG);
         SetLocalInt(oRMGG5,"nMState",CONST_FOLLOW);
         SetLocalObject(oMod,"oRMGG5",oRMGG5);
         SetAILevel(oRMGG5,AI_LEVEL_NORMAL);
         SetLocalInt(oRMGG5,"nFollowDist",0); // 3 meters
       } // sorceress
       oRMGOx=CreateObject(OBJECT_TYPE_CREATURE,sResOx,GetLocation(oWP));
       SetLocalString(oRMGOx,"sTeamID","RMG");
       SetLocalObject(oRMGOx,"oDestWP",oRMG);
       SetLocalInt(oRMGOx,"nMState",CONST_FOLLOW);
       SetLocalInt(oRMGOx,"nSState",0);
       SetLocalObject(oMod,"oRMGOx",oRMGOx);
       SetAILevel(oRMGOx,AI_LEVEL_NORMAL);
       SetLocalInt(oRMGOx,"nFollowDist",2); // 5 meters
       SetLocalInt(oRMGOx,"nParm",1);
       nC=0;
       nR=d6();
       while(nC<nR)
       { // random starting inventory
         nC++;
         oItem=CreateItemOnObject(fnRandomStartingGood(),oRMGOx);
       } // random starting inventory
       nSpeed=3;
       SetLocalInt(oMod,"nRMGSpeed",12);
     } // spawn a new merchant-----------------[ SPAWN ]----------------------
     else if (oRMG==OBJECT_INVALID||GetIsDead(oRMG)==TRUE||oRMGOx==OBJECT_INVALID)
     { // merchant dead - cleanup
       if (oRMG!=OBJECT_INVALID) fnDespawn(oRMG);
       if (oRMGG1!=OBJECT_INVALID) fnDespawn(oRMGG1);
       if (oRMGG2!=OBJECT_INVALID) fnDespawn(oRMGG2);
       if (oRMGG3!=OBJECT_INVALID) fnDespawn(oRMGG3);
       if (oRMGG4!=OBJECT_INVALID) fnDespawn(oRMGG4);
       if (oRMGG5!=OBJECT_INVALID) fnDespawn(oRMGG5);
       if (oRMGOx!=OBJECT_INVALID) fnDespawn(oRMGOx);
       DeleteLocalObject(oMod,"oRMGDest");
       DeleteLocalInt(oMod,"nRMGSpeed");
       DeleteLocalInt(oMod,"nRMGState");
       DeleteLocalInt(oMod,"nRMGDestN");
       nSpeed=0;
       //SendMessageToPC(GetFirstPC(),"RMG IS DEAD");
     } // merchant dead - cleanup
     else
     { // merchant active-----------------------------------------------
       //SendMessageToPC(GetFirstPC(),"RMG ACTIVE:"+IntToString(nState)+" fdist:"+FloatToString(fDist)+" fLDist:"+FloatToString(fLDist));
       nC=GetCalendarDay();
       SetLocalInt(oMod,"nRMGDay",nC);
       if (oRMGOx==OBJECT_INVALID||GetIsDead(oRMGOx)==TRUE)
       {
         nState=7;
       }
       switch(nState)
       { // main switch
         case 0: { // initialize at monestary
           SetLocalInt(oMod,"nRMGState",1);
           break;
         } // initialize at monestary
         case 1: { // pick next destination
           nDestNum++;
           oWP=GetWaypointByTag("RMG_"+IntToString(nDestNum));
           SetLocalFloat(oRMG,"fLDist",0.0);
           if (d4()==1) AssignCommand(oRMG,fnMessageStuck());
           if (oWP!=OBJECT_INVALID)
           { // chosen
             SetLocalObject(oMod,"oRMGDest",oWP);
             SetLocalInt(oMod,"nRMGDestN",nDestNum);
             SetLocalInt(oMod,"nRMGState",2);
           } // chosen
           else
           { SetLocalInt(oMod,"nRMGState",7); break; }
         } // pick next destination
         case 2: { // move to destination
           oItem=oDest;
           if (GetArea(oItem)!=GetArea(oRMG))
           { // not same area - use stand in for distance test
             oItem=GetNearestObjectByTag("RTS_SURFACE",oRMG,1);
             if (oItem==OBJECT_INVALID) oItem=GetNearestObjectByTag("PATHINGA",oRMG,1);
             if (oItem==OBJECT_INVALID)
             {
               oItem=GetFirstObjectInArea(GetArea(oRMG));
               oItem=GetNearestObject(OBJECT_TYPE_WAYPOINT,oRMG,1);
             }
           } // not same area - use stand in for distance test
           fDist=GetDistanceBetween(oItem,oRMG);
           if (fDist<2.0&&GetArea(oDest)==GetArea(oRMG))
           { // arrived
             SetLocalInt(oMod,"nRMGState",3);
           } // arrived
           else
           { // move
             AssignCommand(oRMG,ASActionMoveToObject(oDest,FALSE,1.0));
           } // move
           break;
         } // move to destination
         case 3: { // interpret destination
           sName=GetName(oDest);
           if (sName=="Camp")
             SetLocalInt(oMod,"nRMGState",4);
           else if (sName=="End"||sName=="end")
             SetLocalInt(oMod,"nRMGState",7);
           else if (sName=="1")
           {  // northern people
              nC=0;
              nR=d6();
              AssignCommand(oRMG,SpeakString("Load up the goods from the northern people."));
              while(nC<nR)
              { // random starting inventory
                nC++;
                oItem=CreateItemOnObject(fnBarbarianGoods(),oRMGOx);
              } // random starting inventory
              SetLocalInt(oMod,"nRMGState",1);
           }  // northern people
           else if (sName=="2")
           { // Murins
             nC=0;
             nR=d6();
             AssignCommand(oRMG,SpeakString("Load the goods from Murin onto the Ox."));
             while(nC<nR)
             { // random starting inventory
               nC++;
               oItem=CreateItemOnObject(fnMurinsGoods(),oRMGOx);
             } // random starting inventory
             SetLocalInt(oMod,"nRMGState",1);
           } // Murins
           else if (sName=="3")
           { // Southern Rarities
              nC=0;
              nR=d8();
              AssignCommand(oRMG,SpeakString("Put the goods from the oasis away for transport."));
              while(nC<nR)
              { // random starting inventory
                nC++;
                oItem=CreateItemOnObject(fnDesert(),oRMGOx);
              } // random starting inventory
              SetLocalInt(oMod,"nRMGState",1);
           } // Southern Rarities
           else if (sName=="4")
           { // arrows
             nC=0;
             nR=d6();
             AssignCommand(oRMG,SpeakString("Store the goods from Woodman's on the ox."));
             while(nC<nR)
             { // random starting inventory
               nC++;
               oItem=CreateItemOnObject(fnWoodmans(),oRMGOx);
             } // random starting inventory
             SetLocalInt(oMod,"nRMGState",1);
           } // arrows
           else if (sName=="5")
           { // Potions
             nC=0;
             nR=d8();
             AssignCommand(oRMG,SpeakString("Take care of the goods from the lady."));
             while(nC<nR)
             { // random starting inventory
               nC++;
               oItem=CreateItemOnObject(fnPotions(),oRMGOx);
             } // random starting inventory
             SetLocalInt(oMod,"nRMGState",1);
           } // Potions
           else if (sName=="Waypoint")
             SetLocalInt(oMod,"nRMGState",1);
           else
           { // parse
             nParm=StringToInt(GetStringRight(sName,1));
             if (nParm>0)
             { // valid
               sName=GetStringLeft(sName,GetStringLength(sName)-1);
               oStore=GetObjectByTag(sName);
               if (GetObjectType(oStore)!=OBJECT_TYPE_STORE) oStore=GetObjectByTag(sName,1);
               if (oStore!=OBJECT_INVALID&&GetObjectType(oStore)==OBJECT_TYPE_STORE)
               { // valid store found
                 fnDoTrade(oRMGOx,oStore,nParm,oRMG);
               } // valid store found
               SetLocalInt(oMod,"nRMGState",1);
             } // valid
             else
             {  SetLocalInt(oMod,"nRMGState",1); }
           } // parse
           break;
         } // interpret destination
         case 4: { // camp setup
           oItem=CreateObject(OBJECT_TYPE_PLACEABLE,"plc_campfrwspit",GetLocation(oRMG));
           AssignCommand(oRMG,SpeakString("We camp here. Stay alert!"));
           AssignCommand(oItem,fnWatchForMaster(oRMG));
           SetLocalInt(oMod,"nRMGState",5);
           DelayCommand(HoursToSeconds(1),SetLocalInt(oMod,"nRMGState",6));
           break;
        } // camp setup
         case 5: { // camp animations
           if (GetIsInCombat(oRMG)==FALSE)
           { // not fighting
             AssignCommand(oRMG,ActionPlayAnimation(ANIMATION_LOOPING_DEAD_FRONT,1.0,15.0));
             ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eSleep,oRMG,13.0);
           } // not fighting
           if (GetIsInCombat(oRMGOx)==FALSE)
           { // not fighting
             AssignCommand(oRMGOx,ActionPlayAnimation(ANIMATION_LOOPING_DEAD_FRONT,1.0,15.0));
             ApplyEffectToObject(DURATION_TYPE_TEMPORARY,eSleep,oRMGOx,13.0);
           } // not fighting
           break;
         } // camp animations
         case 6: { // camp take down
           oItem=GetNearestObjectByTag("CampfirewithSpit",oRMG,1);
           DestroyObject(oItem);
           SetLocalInt(oMod,"nRMGState",1);
           break;
         } // camp take down
         case 7: { // End of merchant
           fnDespawn(oRMG);
           if (oRMGG1!=OBJECT_INVALID) fnDespawn(oRMGG1);
           if (oRMGG2!=OBJECT_INVALID) fnDespawn(oRMGG2);
           if (oRMGG3!=OBJECT_INVALID) fnDespawn(oRMGG3);
           if (oRMGG4!=OBJECT_INVALID) fnDespawn(oRMGG4);
           if (oRMGG5!=OBJECT_INVALID) fnDespawn(oRMGG5);
           if (oRMGOx!=OBJECT_INVALID) fnDespawn(oRMGOx);
           DeleteLocalObject(oMod,"oRMGDest");
           DeleteLocalInt(oMod,"nRMGSpeed");
           DeleteLocalInt(oMod,"nRMGDestN");
           nSpeed=0;
           DeleteLocalInt(oMod,"nRMGState");
           break;
       } // End of merchant
      } // main switch
     } // merchant active---------------------------------------------------
     if (nSpeed!=0)
      DelayCommand(IntToFloat(nSpeed),fnRelaunch(nRMGLaunch));
     else
      { DelayCommand(120.0,fnRelaunch(nRMGLaunch)); }
   } // !Disabled
   else
   { // disabled
    if (oRMG!=OBJECT_INVALID)   fnDespawn(oRMG);
    if (oRMGG1!=OBJECT_INVALID) fnDespawn(oRMGG1);
    if (oRMGG2!=OBJECT_INVALID) fnDespawn(oRMGG2);
    if (oRMGG3!=OBJECT_INVALID) fnDespawn(oRMGG3);
    if (oRMGG4!=OBJECT_INVALID) fnDespawn(oRMGG4);
    if (oRMGG5!=OBJECT_INVALID) fnDespawn(oRMGG5);
    if (oRMGOx!=OBJECT_INVALID) fnDespawn(oRMGOx);
    DeleteLocalObject(oMod,"oRMGDest");
    DeleteLocalInt(oMod,"nRMGSpeed");
    DeleteLocalInt(oMod,"nRMGState");
    DeleteLocalInt(oMod,"nRMGDestN");
   } // disabled
} // main
///////////////////////////////////////////////////////////////////////// MAIN

///////////////////
// FUNCTIONS
///////////////////
void fnRelaunch(int nCompare)
{
  if (GetLocalInt(GetModule(),"nRMGLaunch")==nCompare) ExecuteScript("rmg_main",OBJECT_SELF);
} // recall program


string fnRandomStartingGood()
{ // provide a string to a random starting good at monestary
  int nR=d20();
  string sRet="";
  if (nR!=20)
  { // common items
    nR=d6();
    if (nR==1) sRet="nw_it_mring021";
    else if (nR==2) sRet="nw_it_mring022";
    else if (nR==3) sRet="nw_it_mneck020";
    else if (nR==4) sRet="nw_it_medkit001";
    else if (nR==5) sRet="nw_it_torch001";
    else if (nR==6) sRet="fb1_cloak";
  } // common items
  else
  { // rare items
    nR=d4();
    if (nR==1) sRet="x1_wmgrenade005";
    else if (nR==2) sRet="fb1_campkit";
    else if (nR==3) sRet="fb1_warmcloak";
    else if (nR==4) sRet="nw_wblmml002";
  } // rare items
  return sRet;
} // fnRandomStartingGood();


string fnBarbarianGoods()
{ // goods provided by northern barbarians
  int nR=d20();
  string sRet="";
  if (nR!=20)
  { // common goods
    nR=d20();
    if (nR==1) sRet="bullhelm";
    else if (nR==2) sRet="hornedhelm";
    else if (nR==3) sRet="nw_aarcl001";
    else if (nR==4) sRet="nw_aarcl002";
    else if (nR==5) sRet="warmclothes";
    else if (nR==6) sRet="fb1_warmclothes";
    else if (nR==7) sRet="nw_aarcl008";
    else if (nR==8) sRet="nw_ashsw001";
    else if (nR==9) sRet="nw_ashlw001";
    else if (nR==10) sRet="nw_it_msmlmisc20";
    else if (nR==11) sRet="nw_it_mmidmisc05";
    else if (nR==12) sRet="tradeskin";
    else if (nR==13) sRet="nw_it_mpotion021";
    else if (nR==14) sRet="nw_it_mpotion022";
    else if (nR==15) sRet="nw_wamar001";
    else if (nR==16) sRet="nw_waxhn001";
    else if (nR==17) sRet="nw_waxbt001";
    else if (nR==18) sRet="nw_wswbs001";
    else if (nR==19) sRet="nw_wplss001";
    else if (nR==20) sRet="fb1_wintercloak";
  } // common goods
  else
  { // rare goods
    nR=d8();
    if (nR==1) sRet="nw_waxmhn002";
    else if (nR==2) sRet="nw_waxgr001";
    else if (nR==3) sRet="nw_wswgs001";
    else if (nR==4) sRet="nw_wblhw001";
    else if (nR==5) sRet="nw_wbwsh001";
    else if (nR==6) sRet="x1_wmgrenade007";
    else if (nR==7) sRet="fb1_ringwarm";
    else if (nR==8) sRet="nw_wblms001";
  } // rare goods
  return sRet;
} // fnBarbarianGoods();


string fnMurinsGoods()
{ // stuff from murin the smith
  int nR=d20();
  string sRet="";
  if (nR!=20)
  { // common goods
    nR=d20();
    if (nR==1) sRet="nw_aarcl005";
    else if (nR==2) sRet="nw_aarcl012";
    else if (nR==3) sRet="nw_aarcl004";
    else if (nR==4) sRet="nw_aarcl003";
    else if (nR==5) sRet="nw_ashto001";
    else if (nR==6) sRet="nw_wambu001";
    else if (nR==7) sRet="nw_wswdg001";
    else if (nR==8) sRet="nw_wswls001";
    else if (nR==9) sRet="nw_wswrp001";
    else if (nR==10) sRet="nw_wswsc001";
    else if (nR==11) sRet="nw_wswss001";
    else if (nR==12) sRet="nw_wblml001";
    else if (nR==13) sRet="nw_wblhw001";
    else if (nR==14) sRet="nw_wblml001";
    else if (nR==15) sRet="nw_wblms001";
    else if (nR==16) sRet="nw_wspka001";
    else if (nR==17) sRet="nw_wplhb001";
    else if (nR==18) sRet="nw_wthax001";
    else if (nR==19) sRet="nw_wthdt001";
    else if (nR==20) sRet="nw_wbwsl001";
  } // common goods
  else
  { // rare goods
    nR=d20();
    if (nR==1) sRet="nw_aarcl007";
    else if (nR==2) sRet="nw_aarcl006";
    else if (nR==3) sRet="eaglewinghelm";
    else if (nR==4) sRet="helmofdeceit";
    else if (nR==5) sRet="helmofteeth";
    else if (nR==6) sRet="helmetdeathshead";
    else if (nR==7) sRet="helmetpincer";
    else if (nR==8) sRet="nw_wammbu008";
    else if (nR==9) sRet="nw_waxmhn002";
    else if (nR==10) sRet="nw_wswka001";
    else if (nR==11) sRet="nw_wblmcl002";
    else if (nR==12) sRet="nw_wblmml002";
    else if (nR==13) sRet="nw_wspmka002";
    else if (nR==14) sRet="nw_wspsc001";
    else if (nR==15) sRet="nw_wspmsc002";
    else if (nR==16) sRet="nw_wplsc001";
    else if (nR==17) sRet="nw_wbwxl001";
    else if (nR==18) sRet="nw_wbwxh001";
    else if (nR==19) sRet="nw_wbwmsl001";
    else if (nR==20) sRet="nw_wthsh001";
  } // rare goods
  return sRet;
} // fnMurinsGoods()

string fnWoodmans()
{ // get goods from woodmans
  int nR=d20();
  string sRet="";
  if (nR!=20)
  { // common goods
    nR=d12();
    if (nR==1) sRet="nw_aarcl009";
    else if (nR==2) sRet="warmclothes";
    else if (nR==3) sRet="nw_ashsw001";
    else if (nR==4) sRet="nw_it_medkit001";
    else if (nR==5) sRet="nw_it_mpotion023";
    else if (nR==6) sRet="nw_wamar001";
    else if (nR==7) sRet="nw_wambo001";
    else if (nR==8) sRet="nw_wbwln001";
    else if (nR==9) sRet="nw_wbwsh001";
    else if (nR==10) sRet="fb1_cloak";
    else if (nR==11) sRet="fb1_campkit";
    else if (nR==12) sRet="dh2_waterbot";
  } // common goods
  else
  { // rare goods
    nR=d6();
    if (nR==1) sRet="rangerguardarmor";
    else if (nR==2) sRet="nw_it_mring023";
    else if (nR==3) sRet="nw_wammar009";
    else if (nR==4) sRet="nw_wammbo008";
    else if (nR==5) sRet="x1_wmgrenade005";
    else if (nR==6) sRet="fb1_wintercloak";
  } // rare goods
  return sRet;
} // fnWoodmans()

string fnPotions()
{ // potions trader
  int nR=d20();
  string sRet="";
  if (nR!=20)
  { // common
    nR=d8();
    if (nR==1) sRet="dh2_waterbot";
    else if (nR==2) sRet="nw_it_mpotion019";
    else if (nR==3) sRet="nw_it_mpotion006";
    else if (nR==4) sRet="nw_it_mpotion001";
    else if (nR==5) sRet="nw_it_mpotion011";
    else if (nR==6) sRet="nw_it_mpotion015";
  } // common
  else
  { // rare
    nR=d4();
    if (nR==1) sRet="nw_it_mpotion005";
    else if (nR==2) sRet="nw_it_mpotion020";
    else if (nR==3) sRet="nw_it_mpotion002";
    else if (nR==4) sRet="x1_wmgrenade005";
  } // rare
  return sRet;
} // fnPotions()

string fnDesert()
{ // desert goods
  int nR=d8();
  string sRet="";
  if (nR==1) sRet="x1_wmgrenade006";
  else if (nR==2) sRet="x1_wmgrenade002";
  else if (nR==3) sRet="x1_wmgrenade004";
  else if (nR==4) sRet="x1_wmgrenade003";
  else if (nR==5) sRet="nw_wbwsl001";
  else if (nR==6) sRet="nw_wambu001";
  else if (nR==7) sRet="nw_it_picks001";
  else if (nR==8) sRet="nw_it_medkit001";
  return sRet;
} // fnDesertGoods()


void fnWatchForMaster(object oRMG)
{ // placeable look for master
  float fDist=GetDistanceBetween(oRMG,OBJECT_SELF);
  if (fDist<20.0&&GetArea(oRMG)==GetArea(OBJECT_SELF))
    DelayCommand(30.0,fnWatchForMaster(oRMG));
  else { DestroyObject(OBJECT_SELF); }
} // fnWatchForMaster()

int fnStoreHasHowMany(object oStore,string sTag,int nMax=1000)
{ // count items
  object oItem=GetFirstItemInInventory(oStore);
  int nCount=0;
  while(oItem!=OBJECT_INVALID&&nCount<nMax)
  { // poll
    if (GetTag(oItem)==sTag) nCount++;
    oItem=GetNextItemInInventory(oStore);
  } // poll
  return nCount;
} // fnStoreHasHowMany()

void fnDoTrade(object oOx,object oStore,int nParm,object oRM)
{ // do trade
  int nCount=1;
  int nAllowed=0;
  object oItem;
  object oInv=GetFirstItemInInventory(oOx);
  float fDelay=0.0;
  int nChance;
  int nP;
  if (nParm==1) nChance=25;
  else if (nParm==2) nChance=50;
  else if (nParm==3) { nChance=25; nAllowed=1; }
  else if (nParm==4) { nChance=50; nAllowed=1; }
  else if (nParm==5) { nChance=75; nAllowed=1; }
  else if (nParm==6) { nChance=25; nAllowed=2; }
  else if (nParm==7) { nChance=50; nAllowed=2; }
  else if (nParm==8) { nChance=75; nAllowed=2; }
  else if (nParm==9) { nChance=50; nAllowed=3; }
  while(oInv!=OBJECT_INVALID)
  { // oxen inventory
    if (nChance>d100())
    { // percentage passed
      if (nAllowed!=0)
      { // check count
        nP=fnStoreHasHowMany(oStore,GetTag(oInv),nAllowed);
        if (nP<nAllowed) nCount=1;
        else { nCount=0; }
      } // check count
      if (nCount==1)
      { // do trade
        DelayCommand(fDelay,AssignCommand(oRM,SpeakString("Traded "+GetName(oInv)+" here with this merchant.")));
        fDelay=fDelay+1.0;
        oItem=CreateItemOnObject(GetResRef(oInv),oStore,GetItemStackSize(oInv));
        nP=GetGoldPieceValue(oItem);
        nP=nP/20;
        if (nP<1) nP=1;
        oItem=CreateItemOnObject("nw_it_gold001",oOx,nP);
        DelayCommand(4.0,DestroyObject(oInv));
      } // do trade
    } // percentage passed
    oInv=GetNextItemInInventory(oOx);
  } // oxen inventory
} // fnDoTrade();

void fnMessageStuck()
{  //
   string sSay;
   string sSay2="NA";
   int nR=d4();
   int nW=GetWeather(GetArea(OBJECT_SELF));
   object oMod=GetModule();
   object oBanter=GetLocalObject(oMod,"oRMGG5");
   if (oBanter==OBJECT_INVALID) oBanter=GetLocalObject(oMod,"oRMGG4");
   if (oBanter==OBJECT_INVALID) oBanter=GetLocalObject(oMod,"oRMGG3");
   if (oBanter==OBJECT_INVALID) oBanter=GetLocalObject(oMod,"oRMGG2");
   if (oBanter==OBJECT_INVALID) oBanter=GetLocalObject(oMod,"oRMGG1");
   if (nR==1)
   { // Weather
     if (nW==WEATHER_CLEAR) sSay="It is beautiful out here!";
     else if (nW==WEATHER_RAIN) sSay="Blasted rain!";
     else if (nW==WEATHER_SNOW) sSay="One good thing about this snow.  It makes it a tad bit warmer.";
     else { sSay="Something looks out of place out here."; }
   } // weather
   else if (nR==2)
   { // Thought saw something
     nR=d4();
     if (nR==1) sSay="I thought I saw something.  Let me check it out fast.";
     else if (nR==2) sSay="What is that over there?";
     else if (nR==3) sSay="What is that?";
     else if (nR==4) sSay="I think there is something overe there.";
   } // thought saw something
   else if (nR==3)
   { // day/night
     if (GetIsDay()==TRUE||GetIsDawn()==TRUE) sSay="I hope this is a nice day.";
     else { sSay="Keep your eyes open.  No telling what this night might bring."; }
   } // day/night
   else if (nR==4)
   { // banter
     nR=d4();
     if (nR==1) { sSay="Are you paying attention?"; sSay2="Yes sir!"; }
     else if (nR==2)
     {
       sSay="Have you seen anything?";
       nR=d4();
       if (nR==1) sSay2="Just the usual wildlife sir.";
       else if (nR==2) sSay2="No sir.";
       else if (nR==3) sSay2="I am not sure sir.  I am alert!";
       else if (nR==4) sSay2="I thought I might have seen something.  I am watching.";
     }
     else if (nR==3) { sSay="Keep up!"; sSay2="Yes sir!"; }
     else if (nR==4) { sSay="Watch where you are going!"; sSay2="I am sir!"; }
   } // banter
   SpeakString(sSay);
   if (sSay2!="NA"&&oBanter!=OBJECT_INVALID) DelayCommand(4.0,AssignCommand(oBanter,SpeakString(sSay2)));
}  //fnMessageStuck()


void fnDespawn(object oOb)
{ // despawn when not fighting
  if (GetIsInCombat(oOb)==TRUE)
  { // wait a little bit
    DelayCommand(15.0,fnDespawn(oOb));
  } // wait a little bit
  else
  { // despawn
    if (oOb!=OBJECT_INVALID) DestroyObject(oOb);
  } // despawn
} // fnDespawn()