Here's a tweaked code originally found in the Aum that has NPC characters moving on a path. Current code gives 3 paths, particle trails behind, obsticle avoidance. It's not the best coding but here it is...


Code:
ENTITY* goal;

 ENTITY* goal2;
 ENTITY* goal3;


function goal_tracker();
function goal2_tracker();
function goal3_tracker();

BMAP* fire_tga = "muzzle.tga";

function fade_fire(PARTICLE *p);

function fire_effect(PARTICLE *p);


function fade_fire(PARTICLE *p)

{

       p.alpha -= 4 * time_step; // fade out the fire particles

  if (p.alpha < 0) 

         p.lifespan = 0;

}

 

function fire_effect(PARTICLE *p)

{

       set (my, PASSABLE);

       p->vel_x = .5 - random(.5);

       p->vel_y = .5 - random(.5);

       p->vel_z = .1 - random(.1);

       p.alpha = 20 + random(20);

       p.bmap = fire_tga;

       p.size = .05 + random(.01); // gives the size of the flame particles

       p.flags |= (BRIGHT | MOVE);

       p.event = fade_fire;

}
 

action npc_goal() // attach this action to the destination entity

{
       goal = my;        
}

 action npc_goal2() // attach this action to the destination entity

{
       goal2 = my;        
}

 action npc_goal3() // attach this action to the destination entity

{
       goal3 = my;        
}

action npc_tracker() // attach this action to your NPC character

{
set(my, SHADOW);
       while (!goal) {wait (1);} // wait until the goal entity is loaded
       VECTOR temp, temp_angle;
       var npc_speed = 8 - random (6);
       var covered_dist, i;
        c_setminmax(my); 
       while (1)

       {

               my.skill10 += 13 * time_step; // 6 gives the animation speed
               vec_set (temp.x, my.x); // trace 10,000 quants below the npc entity
               temp.z -= 1000;
               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 20; // play with 20
               temp.x = npc_speed * time_step;
               temp.y = 0;
               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
               covered_dist = c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
               if (covered_dist < 0.1) // the npc is stuck?
               {
                       my.pan += 90 - random(180); // then add a random angle to its pan angle
                       i = 0;
                       while (i < 10) // walk in the new direction for 10 frames, play with 10
                       {
                               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
                               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
                               i++;
                               wait (1);
                       }                                
               }
               else // the npc can move? Then rotate it towards the goal again!
               {
                       vec_set(temp_angle, goal.x);
                       vec_sub(temp_angle, my.x);
                       vec_to_angle(my.pan, temp_angle);
                       my.tilt = 0;
               }
               if (vec_dist(goal.x, my.x) < 50) // the npc has found the goal entity?

                       break; // then get out of the while loop!
               wait (1);
              effect(fire_effect, 5, my.x, nullvector); 
       }
goal2_tracker();
       // the goal was reached here
//       ent_animate(my, "stand", 0, 0); // switch to "stand"
}



function goal_tracker() // attach this action to your NPC character

{

       while (!goal) {wait (1);} // wait until the goal entity is loaded
       VECTOR temp, temp_angle;
       var npc_speed = 8 - random (6);
       var covered_dist, i;
       while (1)
       {
               my.skill10 += 20 * time_step; // 6 gives the animation speed
               vec_set (temp.x, my.x); // trace 10,000 quants below the npc entity
               temp.z -= 1000;
               
//               temp.z += 1 * sin(0.05 * total_ticks) * time_step;

//               wait (1);
               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 5; // play with 20
               temp.x = npc_speed * time_step;
               temp.y = 0;
               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
               covered_dist = c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
               if (covered_dist < 0.1) // the npc is stuck?
               {
                       my.pan += 90 - random(180)* sin;
                      // then add a random angle to its pan angle
                       i = 0;
                       while (i < 10) // walk in the new direction for 10 frames, play with 10
                       {
                               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
                               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
                               i++;
                               wait (1);
                               
                       }                                
               }
               else // the npc can move? Then rotate it towards the goal again!
               {
                       vec_set(temp_angle, goal.x);
                       vec_sub(temp_angle, my.x);
                       vec_to_angle(my.pan, temp_angle);
                       my.tilt = 0;
               }
               if (vec_dist(goal.x, my.x) < 50) // the npc has found the goal entity?
                       break; // then get out of the while loop!
               wait (1);
                 effect(fire_effect, 5, my.x, nullvector); 
       }
goal2_tracker();
       // the goal was reached here
//       ent_animate(my, "stand", 0, 0); // switch to "stand"
}


function goal2_tracker() // attach this action to your NPC character
{
       while (!goal2) {wait (1);} // wait until the goal entity is loaded
       VECTOR temp, temp_angle;
       var npc_speed = 7 - random (6);
       var covered_dist, i;
       
       while (1)
       {
               my.skill10 += 50 * time_step; // 6 gives the animation speed
               vec_set (temp.x, my.x); // trace 10,000 quants below the npc entity
               temp.z -= 1000;
               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 20; // play with 20
               temp.x = npc_speed * time_step;
               temp.y = 0;
               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
               covered_dist = c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
//                my.pan += 3 * time_step;

//               my.tilt += 4 * time_step;

//               my.roll += 2 * time_step;
               if (covered_dist < 0.1) // the npc is stuck?
               {
                       my.pan += 90 - random(180)* time_step; // then add a random angle to its pan angle
                       i = 0;
                       while (i < 10) // walk in the new direction for 10 frames, play with 10
                       {
                               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
                               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
                                i++;
                               wait (1);

                       }                                

               }

               else // the npc can move? Then rotate it towards the goal again!

               {

                       vec_set(temp_angle, goal2.x);

                       vec_sub(temp_angle, my.x);
                       vec_to_angle(my.pan, temp_angle);
                       my.tilt = 0;
               }
               if (vec_dist(goal2.x, my.x) < 50) // the npc has found the goal entity?
                       break; // then get out of the while loop!
               wait (1);
                 effect(fire_effect, 5, my.x, nullvector); 
       }
       wait (8);
goal3_tracker();
       // the goal was reached here
//       ent_animate(my, "stand", 0, 0); // switch to "stand"
}


function goal3_tracker() // attach this action to your NPC character
{
       while (!goal3) {wait (1);} // wait until the goal entity is loaded
       VECTOR temp, temp_angle;
       var npc_speed = 7 - random (6);
       var covered_dist, i;
       while (1)
       {
               my.skill10 += 50 * time_step; // 6 gives the animation speed
               vec_set (temp.x, my.x); // trace 10,000 quants below the npc entity
               temp.z -= 10000;
               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 20; // play with 20
               temp.x = npc_speed * time_step;
               temp.y = 0;
               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
               covered_dist = c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
               if (covered_dist < 0.1) // the npc is stuck?
               {
                       my.pan += 90 - random(180); // then add a random angle to its pan angle
                       i = 0;
                       while (i < 10) // walk in the new direction for 10 frames, play with 10
                       {
                               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);
                               ent_animate(my, "walk", my.skill10, ANM_CYCLE); 
                                i++;
                               wait (1);

                       }                                

               }

               else // the npc can move? Then rotate it towards the goal again!

               {

                       vec_set(temp_angle, goal3.x);

                       vec_sub(temp_angle, my.x);
                       vec_to_angle(my.pan, temp_angle);
                       my.tilt = 0;
               }
               if (vec_dist(goal3.x, my.x) < 50) // the npc has found the goal entity?
                       break; // then get out of the while loop!
               wait (1);
                 effect(fire_effect, 5, my.x, nullvector); 
       }
goal_tracker();
       // the goal was reached here
//       ent_animate(my, "stand", 0, 0); // switch to "stand"
}