Pastebin launched a little side project called HostCabi.net, check it out ;-)Don't like ads? PRO users don't see any ads ;-)
Guest

Matchbox maze - Astar solution.

By: Apjjm on Apr 24th, 2012  |  syntax: C  |  size: 23.15 KB  |  hits: 58  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. const string MATCHBOX = "matchbox";
  2. const string WALL = "shelfwall_";
  3. const float MAX_NUMBER_OF_WALLS = 200;
  4.  
  5. void OnStart()
  6. {
  7.         //AttachProp(MATCHBOX,"TT",NODE_FILE,0,0,0);
  8.         //AttachProp(MATCHBOX,"TT",NODE_FILE,NODE_SIZE,0,0);
  9.         AStarBegin(25,0,-11);
  10. }
  11.  
  12. ///////////////////////////////
  13. //+ A* PATH FINDING
  14. ///////////////////////////////
  15. const float NODE_SIZE = 2.0f;
  16. const float NODE_SEP = 0.01f;
  17. const string NODE_FILE = "sdiv" + NODE_SIZE + ".ent";
  18. const float ASTAR_TIMESTEP = 0.2f;
  19.  
  20. void AStarBegin(float goalX, float goalY, float goalZ)
  21.  {
  22.         //Create the lists
  23.         stringListCreate("OpenList");
  24.         stringListCreate("ClosedList");
  25.  
  26.     //Create start node
  27.         string startNode = CreateNode(0,0,0,0,"");
  28.         stringListAddItem("OpenList",startNode);
  29.  
  30.         //Setup the goal node settings
  31.         SetLocalVarFloat("GoalX",goalX);
  32.         SetLocalVarFloat("GoalY",goalY);
  33.         SetLocalVarFloat("GoalZ",goalZ);
  34.        
  35.         //Start
  36.         AddTimer("TIMER",2.0f,"AStarStep");
  37.  }
  38.  
  39. void AStarStep(string &in asTimer)
  40.  {
  41.         //Get a new node from the open list. If none, then destination unreachable.
  42.         string newNode = OpenList_LeastCostNode();
  43.         if(newNode == "")
  44.          {
  45.                 AddDebugMessage("Error: Destination Unreachable",false);
  46.                 return;
  47.          }
  48.         //Add this node to the closed list and remove from open list
  49.         stringListAddItem("ClosedList",newNode);
  50.         if(stringListRemoveItems("OpenList",newNode) != 1)
  51.          {
  52.                 AddDebugMessage("Error: Multiple items removed from open list",false);
  53.                 return;
  54.          }
  55.        
  56.         //Goal Test
  57.         if(AStarGoalTest(newNode))
  58.          {
  59.                 AddDebugMessage("Goal found",false);
  60.                 FollowerBegin(newNode);
  61.                 return;
  62.          }
  63.        
  64.         //Node Properties
  65.         float nX = GetNodeProperty(newNode,"X");
  66.         float nY = GetNodeProperty(newNode,"Y");
  67.         float nZ = GetNodeProperty(newNode,"Z");
  68.         float nC = GetNodeProperty(newNode,"pCost")+1;
  69.        
  70.         //Determine all the potential new nodes from this point
  71.         string[] reachable = {  CreateNode(nX + NODE_SIZE + NODE_SEP,nY,nZ,nC,newNode),
  72.                                                         CreateNode(nX - NODE_SIZE - NODE_SEP,nY,nZ,nC,newNode),
  73.                                                         CreateNode(nX,nY,nZ + NODE_SIZE + NODE_SEP,nC,newNode),
  74.                                                         CreateNode(nX,nY,nZ - NODE_SIZE - NODE_SEP,nC,newNode) };
  75.        
  76.         //For each of the new nodes, add to open list if not already visited and not a wall:
  77.         for(uint i = 0; i<reachable.length(); i++)
  78.          {
  79.                 string nextNode = reachable[i];
  80.                 if(IsNodeColliding(nextNode) || IsNodeInPath(nextNode,"ClosedList") || IsNodeInPath(nextNode,"OpenList"))
  81.                          DeleteNode(nextNode); //This node is not valid or optimal
  82.                 else //Can assume nodes in open list are optimal as all path cost increments are 1.
  83.                         OpenList_AddNewNode(nextNode); //Add/update the open list with this node
  84.          }
  85.        
  86.         AddTimer("TIMER",ASTAR_TIMESTEP,"AStarStep");
  87.  }
  88.  
  89. float AStarHeuristic(float fromX, float fromY, float fromZ)
  90.  {
  91.         //StraightLine distance in squares
  92.         float DX = fromX - GetLocalVarFloat("GoalX");
  93.         float DY = fromY - GetLocalVarFloat("GoalY");
  94.         float DZ = fromZ - GetLocalVarFloat("GoalZ");
  95.         return sqrt(DX*DX + DY*DY + DZ*DZ) / (2 * NODE_SIZE);
  96.  }
  97.  
  98. bool AStarGoalTest(string node)
  99.  {
  100.         float dX = GetNodeProperty(node,"X")-GetLocalVarFloat("GoalX");
  101.         float dY = GetNodeProperty(node,"Y")-GetLocalVarFloat("GoalY");
  102.         float dZ = GetNodeProperty(node,"Z")-GetLocalVarFloat("GoalZ");
  103.         //Does this node contain the goal point?
  104.         return (        dX <  NODE_SIZE && dY <  NODE_SIZE && dZ <  NODE_SIZE
  105.                         &&      dX > -NODE_SIZE && dY > -NODE_SIZE && dZ > -NODE_SIZE   );
  106.  }
  107.  
  108. string OpenList_LeastCostNode()
  109.  {
  110.         string[] nodes = stringListItems("OpenList");
  111.         float leastCost = 999999.0f;
  112.         string bestNode = "";
  113.         for(uint i =0; i<nodes.length(); i++)
  114.          {
  115.                 float cost = GetNodeProperty(nodes[i],"pCost") + GetNodeProperty(nodes[i],"hCost");
  116.                 if(cost < leastCost)
  117.                 {
  118.                         bestNode = nodes[i];
  119.                         leastCost = cost;
  120.                 }
  121.          }
  122.         return bestNode;
  123.  }
  124.  
  125. void OpenList_AddNewNode(string node)
  126.  {
  127.         /*
  128.         //Use only if path costs are not the same for all nodes
  129.         //If node is already in open list, update old node to pick the best cost if new node is better
  130.         string[] nodes = stringListItems("OpenList");
  131.         for(uint i = 0; i<nodes.length(); i++)
  132.          {
  133.                 string oldNode = nodes[i];
  134.                 if(GetEntitiesCollide("node_"+oldNode,"node_"+node))
  135.                  {
  136.                         float pc1 = GetNodeProperty(node,"pCost");     
  137.                         if(pc1<GetNodeProperty(oldNode,"pCost"))
  138.                          {
  139.                                 SetLocalVarFloat("node_"+oldNode+"pCost",pc1);
  140.                                 SetLocalVarString("node_"+oldNode+"parent",GetLocalVarString("node_"+node+"parent"));
  141.                                 AddDebugMessage(GetNodeFullName(oldNode) + " Updated",false);
  142.                          }
  143.                         DeleteNode(node);
  144.                  }
  145.          }
  146.         */
  147.         //Otherwise add the node
  148.         stringListAddItem("OpenList",node);
  149.         AddDebugMessage(GetNodeFullName(node) + " Added",false);
  150.  }
  151. ///////////////////////////////
  152. //- A* PATH FINDING
  153. ///////////////////////////////
  154.  
  155. ///////////////////////////////
  156. //+ NODE
  157. ///////////////////////////////
  158. string CreateNode(float nodeX, float nodeY, float nodeZ, float pCost, string parent)
  159.  {
  160.         int id = GetLocalVarInt("NodeID"); AddLocalVarInt("NodeID",1);
  161.         string name = "node_" + id;
  162.         SetLocalVarFloat(name + "X",nodeX);
  163.         SetLocalVarFloat(name + "Y",nodeY);
  164.         SetLocalVarFloat(name + "Z",nodeZ);
  165.         SetLocalVarFloat(name + "pCost",pCost);
  166.         SetLocalVarFloat(name + "hCost",AStarHeuristic(nodeX,nodeY,nodeZ));
  167.         SetLocalVarString(name + "parent",parent);
  168.         AttachProp(MATCHBOX,name,NODE_FILE,nodeX,nodeY,nodeZ);
  169.         return ""+id;
  170.  }
  171.  
  172. void DeleteNode(string node) { RemoveAttachedPropFromProp(MATCHBOX,"node_" + node); }
  173.  
  174. bool IsNodeInPath(string newNode, string path)
  175.  {
  176.         string[] nodes = stringListItems(path);
  177.         string node = "node_" + newNode;
  178.        
  179.         for(uint i = 0; i<nodes.length(); i++)
  180.                 if(GetEntitiesCollide(node,"node_"+nodes[i])) return true;
  181.        
  182.         return false;
  183.  }
  184.  
  185. bool IsNodeColliding(string node)
  186.  {
  187.         for(int i =1; i<MAX_NUMBER_OF_WALLS; i++)
  188.                 if(GetEntitiesCollide(WALL+i,"node_"+node)) return true;
  189.  
  190.         return false;
  191.  }
  192.  
  193. float GetNodeProperty(string node, string property) { return GetLocalVarFloat("node_" + node + property); }
  194.  
  195. string GetNodeFullName(string node)
  196.  {  
  197.         return "Node("+ GetNodeProperty(node,"X")+","+
  198.                                         GetNodeProperty(node,"Y")+","+
  199.                                         GetNodeProperty(node,"Z")+","+node+")";
  200.  }
  201.  
  202. void ClearNodeAttachments()
  203.  {
  204.         for(int i =0; i<GetLocalVarInt("NodeID"); i++)
  205.          {
  206.                 if(GetEntityExists("node_"+i))
  207.                         RemoveAttachedPropFromProp(MATCHBOX,"node_"+i);
  208.          }
  209.  }
  210. ///////////////////////////////
  211. //- NODE
  212. ///////////////////////////////
  213.  
  214. ///////////////////////////////
  215. //+ PATH FOLLOWING
  216. ///////////////////////////////
  217. const float FOLLOWER_TIMESTEP = 1.0f;
  218. const float FOLLOWER_IMPULSE_MUL = 2.25f;
  219.  
  220. void FollowerBegin(string goalNode)
  221.  {
  222.         //Don't need the old open/closed list anymore, or the attachments.
  223.         stringListClear("OpenList");
  224.         stringListClear("ClosedList");
  225.         ClearNodeAttachments();
  226.        
  227.         //Use the open list as temp storage for the route from goal->start
  228.         string parent = GetLocalVarString("node_"+goalNode+"parent");
  229.         while(parent!="")
  230.          {
  231.                 stringListAddItem("OpenList",parent);
  232.                 parent = GetLocalVarString("node_"+parent+"parent");
  233.          }
  234.          
  235.         //Start the follower timer
  236.         SetLocalVarFloat("OldX",0);
  237.         SetLocalVarFloat("OldY",0);
  238.         SetLocalVarFloat("OldZ",0);
  239.         AddTimer("Follower",FOLLOWER_TIMESTEP,"tmrFollower");
  240.  }
  241.  
  242. void tmrFollower(string &in asTimer)
  243.  {
  244.         //Get the current node & remove from path
  245.         string[] toVisit = stringListItems("OpenList");
  246.         if(toVisit.length() == 0)
  247.         {
  248.                 AddDebugMessage("At Goal",false);
  249.                 return;
  250.         }
  251.         string node = toVisit[toVisit.length()-1];
  252.         stringListRemoveItems("OpenList",node);
  253.        
  254.         //Work out which direction to move in
  255.         float nX = GetNodeProperty(node,"X");
  256.         float nY = GetNodeProperty(node,"Y");
  257.         float nZ = GetNodeProperty(node,"Z");
  258.         float dX = (nX - GetLocalVarFloat("OldX")) * FOLLOWER_IMPULSE_MUL;
  259.         float dY = (nY - GetLocalVarFloat("OldY")) * FOLLOWER_IMPULSE_MUL;
  260.         float dZ = (nZ - GetLocalVarFloat("OldZ")) * FOLLOWER_IMPULSE_MUL;
  261.         SetLocalVarFloat("OldX",nX);
  262.         SetLocalVarFloat("OldY",nY);
  263.         SetLocalVarFloat("OldZ",nZ);
  264.        
  265.         //Move
  266.         AddPropImpulse(MATCHBOX,dX,dY,dZ,"world");
  267.        
  268.         //Repeat the path follower
  269.         AddTimer(asTimer,FOLLOWER_TIMESTEP,"tmrFollower");
  270.  }
  271.  
  272. ///////////////////////////////
  273. //- PATH FOLLOWING
  274. ///////////////////////////////
  275.  
  276.  
  277. ///////////////////////////////
  278. //+ MISC METHODS
  279. ///////////////////////////////
  280. //Fixed variant of AddAttachedPropToProp
  281. void AttachProp(string &in asPName,string &in asAName, string &in aFile, float afX, float afY, float afZ)
  282.  {
  283.         AddAttachedPropToProp(asPName,asAName,aFile,afX,afY,0,afZ,90.0f,afZ);
  284.  }
  285.  
  286. //SQRT
  287. bool approx(float &in x, float &in y, float &in epsilon)
  288.  { float delta = x-y; return ((delta>0?delta:-delta) <= (epsilon>0?epsilon:-epsilon)); }
  289. const uint32 _SQRTITERATIONS=16; //Maximum number of iterations for sqrt computation
  290. const float  _SQRTDELTA=0.00001f; //Margin of error allowable if complete before iteration ceiling
  291. float sqrt(float &in x) {
  292.   if(x<=0) return 0; //Early out - not valid input.
  293.   uint32 i = 0; float o = x * 0.5f;
  294.   while( i<_SQRTITERATIONS && !approx(o*o,x,_SQRTDELTA) && o != 0)
  295.     { o = 0.5f * (o + x/o); i++; }
  296.   return o;             }
  297.  
  298. ///////////////////////////////
  299. //- FIXED METHODS
  300. ///////////////////////////////
  301.  
  302. ///////////////////////////////
  303. //Begin String Parsing
  304. ///////////////////////////////
  305. //+ Private Parsing functions
  306. int  _ParseDigit(uint8 digit) {
  307.     int d = digit-48; //48 is ASCII code for 0
  308.     return ((d >= 0)&&(d<=9)) ? d : -1; }
  309. bool _ParseExp(uint8 digit) {
  310.     return (digit == 69) || (digit == 101); //ASCII for e or E
  311. }
  312. bool _ParseSign(uint8 digit) {
  313.     return (digit == 45);  // -sign
  314. }
  315. bool _ParsePoint(uint8 digit) {
  316.     return (digit == 46);
  317. }
  318. bool _ParseExpSpace(uint8 digit) {
  319.     return (digit == 43)||(digit == 32);
  320. }
  321. float _ParseMakeFloat(int n1, int n2, int n3, bool hasD, bool hasE, bool signE, bool signN, int leading) {
  322.   float output = n1;
  323.      if(hasD) {
  324.     int ln=(""+n2).length() + leading; float temp=n2;
  325.     for(int i=0; i<ln; i++) temp /= 10;
  326.     output += signN?-temp:temp;
  327.    } if(hasE) {
  328.     for(int i =0; i<n3; i++)
  329.      output = signE?(output/10):(output*10);
  330.    } return output;
  331. }
  332. int _ParseMakeInt(int n1, int n3, bool hasE, bool signE) {
  333.   int output = n1;
  334.   if(hasE) {
  335.    for(int i =0; i<n3; i++)
  336.      output = signE?(output/10):(output*10);
  337.            } return output;
  338. }
  339. //-
  340.  
  341. //Parses a given string to an array of floats
  342. float[] parseStringToFloatArray(string &in asString) {
  343.   float[] output={}; int state = 0;
  344.   bool finalise = false; //Need to store number in array?
  345.   bool reading = false; //In the process of reading a number?
  346.   bool numsign = false; //Whether or not number is signed
  347.   bool expsign = false; // " " " " " " " exponent is signed
  348.   bool hasexp = false; //Has an exponent?
  349.   bool hasdec = false; //Has points?
  350.   int numbers = 0; //Temp store for numbers
  351.   int points =0;   //Temp store for decimals
  352.   int expons =0;   //Temp store for exponent
  353.   int leading =0; //Temp for leading 0s on decimals
  354.   for(uint i=0; i<asString.length(); i++) { uint8 chr = asString[i];
  355.     int d = _ParseDigit(chr);
  356.     switch(state) {
  357.      case 0:  {  //Looking for: - or digit
  358.         if(d>=0 && d<=9) {
  359.           numbers=numsign?-d:d;
  360.           reading = true; //State we are reading a number
  361.           state = 1;  //Number has begun, jump to number reading stage
  362.          } else if(_ParseSign(chr)) {
  363.           numsign=true; //Read a minus sign. Mark number as possibly signed?
  364.          } else {
  365.           numsign=false; //Didn't read a sign - reset sign state.
  366.          } break;
  367.        }
  368.      case 1:  {  //Read digits until a "." or exponent is found
  369.         if(d>=0 && d<=9) {
  370.           numbers=(numbers * 10) + d; //Add each digit
  371.          } else if(_ParsePoint(chr)) {
  372.           state = 2; //Read a decimal point - jump to decimal reading state
  373.          } else if(_ParseExp(chr)) {
  374.           state = 3; //Read an exponent marker - jump to exponent reading state
  375.          } else { //Nothing matches. Finish.
  376.           reading = false; finalise = true;
  377.          } break;
  378.        }
  379.      case 2:  {  //Read digits until an exponent is found
  380.         if(d>=0 && d<=9) {
  381.           if(d == 0 && points == 0) leading++;
  382.           points = (points * 10) + d;
  383.           hasdec=true;
  384.          } else if(_ParseExp(chr)) {
  385.           state = 3;
  386.          } else { //Nothing matches. Finish.
  387.           reading = false; finalise = true;
  388.          } break;
  389.        }
  390.      case 3:  {  //Looking for: - + or whitespace signs or a digit
  391.         if(d>=0 && d<=9) {
  392.           expons=d; hasexp=true; state =4;
  393.          } else if(_ParseSign(chr)) {
  394.           expsign=true;
  395.          } else if(_ParseExpSpace(chr)) {
  396.           expsign=false;
  397.          } else {
  398.           expsign=false; reading = false; finalise = true;
  399.          } break;
  400.        }
  401.      case 4:  {  //Reading digits for exponent
  402.         if(d>=0 && d<=9) {
  403.           expons = (expons * 10) + d; hasexp=true;
  404.          } else {
  405.           reading = false; finalise = true;
  406.          } break;
  407.        }
  408.                   }
  409.     if(finalise) {
  410.       int index = output.length(); output.resize(index+1); //Resize array, store target index
  411.       output[index] = _ParseMakeFloat(numbers, points, expons, hasdec, hasexp, expsign, numsign, leading); //Store number
  412.       numbers = 0; points = 0; expons = 0; state = 0; leading=0; //Reset vars
  413.       finalise = false; reading = false; hasexp=false; expsign=false; numsign = false; hasdec=false;
  414.                  }
  415.    }
  416.    if(reading) {
  417.      int index = output.length(); output.resize(index+1);
  418.      output[index] = _ParseMakeFloat(numbers, points, expons, hasdec, hasexp, expsign, numsign, leading);
  419.                }
  420.   return output;    
  421. }
  422.  
  423. //Parses a given string to an array of ints
  424. int[] parseStringToIntArray(string &in asString) {
  425.   int[] output={}; int state = 0;
  426.   bool finalise = false; //Need to store number in array?
  427.   bool reading = false; //In the process of reading a number?
  428.   bool numsign = false; //Whether or not number is signed
  429.   bool expsign = false; // " " " " " " " exponent is signed
  430.   bool hasexp = false; //Has an exponent?
  431.   int numbers = 0; //Temp store for numbers
  432.   int expons =0;   //Temp store for exponent
  433.   for(uint i=0; i<asString.length(); i++) { uint8 chr = asString[i];
  434.     int d = _ParseDigit(chr);
  435.     switch(state) {
  436.      case 0:  {  //Looking for: - or digit
  437.         if(d>=0 && d<=9) {
  438.           numbers=numsign?-d:d;
  439.           reading = true; //State we are reading a number
  440.           state = 1;  //Number has begun, jump to number reading stage
  441.          } else if(_ParseSign(chr)) {
  442.           numsign=true; //Read a minus sign. Mark number as possibly signed?
  443.          } else  {
  444.           numsign=false; //Didn't read a sign - reset sign state.
  445.          } break;
  446.        }
  447.      case 1:  {  //Read digits until exponent is found
  448.         if(d>=0 && d<=9) {
  449.           numbers=(numbers * 10) + d; //Add each digit
  450.          } else if(_ParseExp(chr)) {
  451.           state = 3; //Read an exponent marker - jump to exponent reading state
  452.          } else { //Nothing matches. Finish.
  453.           reading = false;
  454.           finalise = true;
  455.          } break;
  456.        }
  457.      case 3:  {  //Looking for: - + or whitespace signs or a digit
  458.         if(d>=0 && d<=9) {
  459.           expons=d; hasexp=true; state =4;
  460.          } else if(_ParseSign(chr)) {
  461.           expsign=true;
  462.          } else if(_ParseExpSpace(chr)) {
  463.          expsign=false;
  464.          } else {
  465.           expsign=false; reading = false; finalise = true;
  466.          } break;
  467.        }
  468.      case 4:  {  //Reading digits for exponent
  469.         if(d>=0 && d<=9)
  470.          {
  471.           expons = (expons * 10) + d; hasexp=true;
  472.          } else {
  473.           reading = false; finalise = true;
  474.          }
  475.        }
  476.                   }
  477.     if(finalise) {
  478.       int index = output.length(); output.resize(index+1);
  479.       output[index] = _ParseMakeInt(numbers, expons, hasexp, expsign);
  480.       numbers = 0; expons = 0; state = 0; finalise = false; reading = false;
  481.       hasexp=false; expsign=false; numsign = false;
  482.                  }
  483.    }
  484.    if(reading) {
  485.      int index = output.length(); output.resize(index+1);
  486.      output[index] = _ParseMakeInt(numbers, expons, hasexp, expsign);
  487.                }
  488.   return output;    
  489. }
  490.  
  491. //Parses all the digits out of a given string, ignorning sign
  492. uint parseStringUInt(string &in asString) {
  493.     uint output = 0;
  494.     for(uint i=0; i<asString.length(); i++) {
  495.       int digit = _ParseDigit(asString[i]);
  496.       output = (digit > -1)?(10*output+digit):(output); }
  497.     return output;
  498. }
  499. ///////////////////////////////
  500. //End
  501. ///////////////////////////////
  502.  
  503. ///////////////////////////////
  504. //Begin String List
  505. ///////////////////////////////
  506.  
  507. //+ Constants
  508. //Change the delimiter to suit, it is currently the null character.
  509. const string _STRLPFX = "_!strli!_"; //Prefixed to all stringList var names
  510. const string _STRLDLM = "\0";    //SINGLE CHARACTER Delimiter for items
  511. //-
  512.  
  513. //+ List Management
  514. bool stringListCreate(string &in asName) {
  515.   string name = _STRLPFX + asName;
  516.   //If Exists alread, don't overwrite
  517.   if(GetLocalVarInt(name + "_exist")==1) return false;
  518.   //Else continue as normal
  519.   SetLocalVarInt(name + "_exist",1); //State list exists
  520.   SetLocalVarInt(name + "_count",0);  //Put a count on the list
  521.   SetLocalVarString(name,"");         //Define list w/ empty string
  522.   return true;
  523. }
  524.  
  525. bool stringListDestroy(string &in asName) {
  526.   string name = _STRLPFX + asName;
  527.   //If it doesn't exist - we can't destroy it
  528.   if(GetLocalVarInt(name + "_exist")!=1) return false;
  529.   //Else reduce all vars to min size - mark as not existing
  530.   SetLocalVarInt(name + "_exist",0);  //State list doesn't exist
  531.   SetLocalVarInt(name + "_count",0);  //Change the list count
  532.   SetLocalVarString(name,"");         //Define list w/ empty string
  533.   return true;
  534. }  
  535.  
  536. bool stringListExists(string &in asName) {
  537.   return (GetLocalVarInt(_STRLPFX + asName + "_exist") == 1);
  538. }
  539.  
  540. int stringListSize(string &in asName) {
  541.   return (GetLocalVarInt(_STRLPFX + asName + "_count"));
  542. }
  543.  
  544. void stringListClear(string &in asName) {
  545.   string name = _STRLPFX + asName;
  546.   //Don't do anything if list doesn't exist
  547.   if(GetLocalVarInt(name + "_exist")!=1) return;
  548.   //Else reset list to defaults
  549.   SetLocalVarInt(name + "_count",0);  //Put a count on the list to 0
  550.   SetLocalVarString(name,"");         //Define list w/ empty string
  551. }
  552.  
  553. //- List Management
  554.  
  555. //+ Item Management
  556. string[] stringListItems(string &in asName) {
  557.   string str = GetLocalVarString(_STRLPFX + asName);
  558.   //Output array vars
  559.   string[] output = {};
  560.   int outputIndex = 0;
  561.   //Early-out if string is faulty
  562.   if(!StringContains(str,_STRLDLM)) return output;
  563.   //String matching vars
  564.   uint8 dlm = _STRLDLM[0];
  565.   int last = 0;
  566.   //Loop and add each substring
  567.   for(uint i =0; i<str.length(); i++) //For each char
  568.    {
  569.     if(str[i] == dlm) //If char matches delimiter
  570.      {      
  571.       int len = int(i) - last; //Determine length of token to read
  572.       if(len >= 0)
  573.        {
  574.         output.resize(outputIndex+1); //Increase array size
  575.         output[outputIndex] = StringSub(str,last,len); //Get token into array
  576.         outputIndex++; //Location of next token
  577.        }
  578.       last = i+1; //Say token begins at the next char
  579.      }    
  580.    }
  581.   //Return array of substrings
  582.   return output;
  583. }
  584.  
  585. int stringListAddItem(string &in asName, string &in asItem) {
  586.   string name = _STRLPFX + asName;
  587.   //Return error value if list doesn't exist
  588.   if(GetLocalVarInt(name + "_exist")!=1) return -1;
  589.   //Add the item with the delimiter, increment count
  590.   int index = GetLocalVarInt(name + "_count");
  591.   AddLocalVarInt(name + "_count",1);
  592.   AddLocalVarString(name,asItem + _STRLDLM);
  593.   return index;
  594. }
  595.  
  596. void stringListAddItems(string &in asName, string[] asItem) {
  597.   //Doesnt exist / Bad parameter? return.
  598.   string name = _STRLPFX + asName;
  599.   if(GetLocalVarInt(name + "_exist")!=1  || asItem.length()==0) return;
  600.   //Append all items together w/ the delimiter
  601.   string item = "";
  602.   for(uint i =0; i<asItem.length(); i++) item += asItem[i] + _STRLDLM;
  603.   //Add this new item string, Increment count
  604.   AddLocalVarString(name,item);
  605.   AddLocalVarInt(name + "_count",asItem.length());
  606. }
  607.  
  608. string stringListGetItem(string &in asName, int alIndex) {
  609.   //This is just an array fetch + grab at index
  610.   string name = _STRLPFX + asName;
  611.   string output = "";
  612.   if(alIndex >= 0 && GetLocalVarInt(name + "_exist") == 1) //Parameters valid enough to progress?
  613.    {
  614.     string[] names = stringListItems(asName); //Get list of items
  615.     if(alIndex < int(names.length()))
  616.      {
  617.       output = names[alIndex]; //Index in bounds: set ouput to item
  618.      }
  619.    }
  620.   return output;
  621. }
  622.  
  623. int stringListGetLastIndexof(string &in asName, string &in asItem) {
  624.   //This is a linear search of the list of items
  625.   string name = _STRLPFX + asName;
  626.   int index = -1;
  627.   if(GetLocalVarInt(name + "_exist") == 1)
  628.    {
  629.     string[] items = stringListItems(asName); //Get list of items
  630.     for(int i=0; i<int(items.length()); i++)
  631.      { if(asItem == items[i]) index = i; } //Search for match. If match set index to index of match.
  632.    }
  633.   return index;
  634. }
  635.  
  636. int stringListGetFirstIndexof(string &in asName, string &in asItem) {
  637.   //This is a linear search of the list of items
  638.   string name = _STRLPFX + asName;
  639.   int index = -1;
  640.   if(GetLocalVarInt(name + "_exist") == 1)
  641.    {
  642.     string[] items = stringListItems(asName); //Get list of items
  643.     for(uint i=0; i<items.length(); i++)
  644.      {
  645.       if(asItem == items[i])
  646.         {
  647.          index = int(i);
  648.          i = items.length();
  649.         }
  650.      }
  651.    }
  652.   return index;
  653. }
  654.  
  655. bool stringListContains(string &in asName, string &in asItem) {
  656.   return stringListGetLastIndexof(asName, asItem)>=0;
  657. }
  658.  
  659. bool stringListRemoveItemAt(string &in asName, uint alIndex) {
  660.     string name = _STRLPFX + asName;
  661.     //List exists?
  662.     if(GetLocalVarInt(name + "_exist")!=1) return false;
  663.     //Get list
  664.     string[] list = stringListItems(asName);
  665.     //Re-add all but deleted item
  666.     string item = "";
  667.     int count = 0;
  668.     bool op = false;
  669.     for(uint i =0; i<list.length(); i++)
  670.     {
  671.      if(i != alIndex)
  672.       {
  673.        item += list[i] + _STRLDLM;
  674.        count++;
  675.       }
  676.      else op=true;
  677.     }
  678.     //Set back to the array
  679.     SetLocalVarInt(name + "_count",count);  //Change count
  680.     SetLocalVarString(name,item);           //Redefine list      
  681.     //Return if anything was deleted
  682.     return op;
  683. }
  684.  
  685. int stringListRemoveItems(string &in asName, string asItem) {
  686.     string name = _STRLPFX + asName;
  687.     //List exists?
  688.     if(GetLocalVarInt(name + "_exist")!=1) return 0;
  689.     //Get list
  690.     string[] list = stringListItems(asName);
  691.     //Re-add all but deleted items
  692.     string item = "";
  693.     int count = 0;
  694.     int delamt = 0;
  695.     for(uint i =0; i<list.length(); i++)
  696.     {
  697.      if(asItem != list[i])
  698.       {
  699.        item += list[i] + _STRLDLM;
  700.        count++;
  701.       }
  702.      else delamt++;
  703.     }
  704.     //Set back to the array
  705.     SetLocalVarInt(name + "_count",count);  //Change count
  706.     SetLocalVarString(name,item);           //Redefine list      
  707.     //Return if anything was deleted
  708.     return delamt;
  709. }
  710. //- Item Management
  711.  
  712. ///////////////////////////////
  713. //End String List
  714. ///////////////////////////////