View Single Post
  #1  
Old 04-29-2009, 09:36 PM
fowlplay4 fowlplay4 is offline
team canada
fowlplay4's Avatar
Join Date: Jul 2004
Location: Canada
Posts: 5,200
fowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond reputefowlplay4 has a reputation beyond repute
NPC Movement / Actions

To make it easier for level makers to add some NPC life into their levels I created this.

It allows the user to make an npc follow a simple movement path, and use some animation while staying the on clientside to avoid using the server's resources also looks a lot smoother.

Class: movement
PHP Code:
/*
   Allows for Simple Movement, and Actions to breathe
   npc life into the server.
*/

//#CLIENTSIDE

function onCreated() {
  if (!
this.speedthis.speed 6//Tiles Per Second.
  
this.actionpos 0;
  
doAction(0); 
}

function 
doAction(ind) {
  if (
ind == this.actions.size()) return;
  
temp.action this.actions[ind];
  
temp.data action.tokenize(":");
  if (
data[0].starts("!")) {
    
this.actions.delete(ind);
    
this.actionpos--;
    
data[0] = data[0].substring(1);
  }
  switch (
data[0]) {
    case 
"move":
      
doMove(data[1], data[2]);
      break;
    case 
"moveto":
      
temp.movex data[1] - this.x;
      
temp.movey data[2] - this.y;
      
doMove(movexmovey);
      break;
    case 
"reversemove":
      
this.reversing true;
      
this.reversepos this.moves.size();
      
onMovementFinished();
      break;
    case 
"action":
      
temp.act this.("action_" data[1]);
      for (
temp.actionact) {
        
this.actions.insert(this.actionpos+1+temp.i"!" action);
        
temp.i++;
      }
      
onNextStep();
      break;
    case 
"randomaction":
      if (
random(0,1) <= data[1]) {
        
temp.act this.("action_" data[2]);
        for (
temp.actionact) {
          
this.actions.insert(this.actionpos+1+temp.i"!" action);
          
temp.i++;
        }
      }
      else if (
data[3].length() > 0) {
        
temp.act this.("action_" data[3]);
        for (
temp.actionact) {
          
this.actions.insert(this.actionpos+1+temp.i"!" action);
          
temp.i++;
        }
      }
      
onNextStep();
      break;
    case 
"setdir":
      
this.dir data[1];
      break;
    case 
"setani":
      
setcharani(data[1], data[2]);
      
onNextStep();
      break;
    case 
"randomani":
      if (
random(0,1) <= data[1]) {
        
setcharani(data[2], data[3]);
      }
      
onNextStep();
      break;
    case 
"chat":
      
this.chat data[1];
      
temp.time = (!data[2] ? data[2]);
      
this.scheduleevent(time"ClearChat""");
      
onNextStep();
      break;
    case 
"randomchat":
      if (
random(0,1) <= data[1]) {
        
this.chat data[2];
        
temp.time = (!data[3] ? data[3]);
        
this.scheduleevent(time"ClearChat""");
      }
      
onNextStep();
      break;
    case 
"wait":
      
this.scheduleevent(data[1], "NextStep""");
      break;
    case 
"repeat":
      
this.actionpos 0;
      
doAction(this.actionpos);
      break;
  }
}

// Movement Functions

function GetDistance(temp.x1temp.y1temp.x2temp.y2) {
  
temp.toreturn = (temp.x2 temp.x1) ^ + (temp.y2 temp.y1) ^ 2;
  return (
temp.toreturn) ^ .5;
}

function 
doMove(dxdy) {
  
temp.distance GetDistance(this.x,this.y,this.x+dx,this.y+dy);
  
temp.time distance this.speed;
  
this.dir getdir((this.dx) - this.x,(this.dy) - this.y);
  
move(dxdytemp.time8);
  
setcharani(this.ani_walk"");
  if (!
this.reversingthis.moves.add({dxdy});
}

function 
onMovementFinished() {
  
setcharani(this.ani_idle"");
  if (
this.reversing) {
    
this.reversepos--;
    if (
this.reversepos <= -1) {
      
this.reversing false;
      
this.moves "";
      return 
onNextStep();
    }
    
temp.move this.moves[this.reversepos];
    return 
doMove(-temp.move[0], -temp.move[1]);
  }
  else 
onNextStep();
}

// Action Related Functions

function onClearChat() {
  
this.chat "";
}

function 
onNextStep() {
  
this.actionpos++;
  
doAction(this.actionpos);

You can easily use the above to make a fisherman walk up and down the riverside, and get him to stop and fish. Here's my example including the use of every action that is scripted into it.

PHP Code:
/**
Actions:
  move:dx:dy  - Moves NPC dx, dy
  moveto:x:y  - Moves NPC to x, y
  setdir:dir  - Sets NPC direction to dir
  reversemove - Does the Reverse of Every Move 
                it's made so far. Works like a 
                return home command.
    
  chat:chattext:length - Sets NPC Chat to chattext
                         for 3 seconds if no length
                         is decided.

  randomchat:0-1:chattext:length - Same Idea just with the
                                   random chance factor.
    
  setani:gani:param - Sets NPC Gani to gani, 
                      param isn't required.

  randomani:0-1:gani:param - Same idea as setani, just with
                             random chance factor.

  wait:seconds - NPC waits seconds before next action.

  repeat - NPC starts from the 1st action again.

  action:name - NPC does every thing in 
                this.action_actionname, view example below.

  randomaction:0-1:name - Same idea as action just with
                          random chance factor.
*/
//Near OSL, Fisherman NPC Example.
//#CLIENTSIDE 
function onCreated() {
  
// Initialize the attributes
  
showcharacter();
  
this.head "head4.png";
  
this.colors = {"orange""white""white""black""blue"};
  
this.shield "no-shield.gif";
  
this.dir 1;
  
//Ganis
  
this.ani_walk "walk"//Gani while walking.
  
this.ani_idle "idle"//Gani after finished moving.
  
this.speed 8// Movement Speed (tiles per second, default is 6)
  //Actions
  
this.actions = {
    
"move:4.8:0",       // Depending on the place you'll have
    
"move:0:-12",       // to change these.
    
"move:-4.8:-4.8",
    
"move:0:-4.8",
    
"move:-3.6:-2.4",
    
"action:fishing",
    
"reversemove",
    
"action:fishing",
    
"repeat"
  
};
  
//Sub-Actions Example.
  
this.action_fishing = {
    
"setani:zodiac_fishingattack",
    
"wait:5",
    
"randomaction:0.25:reel",
    
"wait:3",
    
"setani:zodiac_fishingattack",
    
"wait:1"
  
};
  
this.action_reel = {
    
"setani:zodiac_fishingreeling",
    
"randomchat:0.3:Almost had that one.. -sigh-:4"
  
};
  
join("movement");

In the future I may attempt to sync the movements over clients but it would require timevar3 and some tricky math to determine where the npc would be at a certain time but it's not really needed since this is mainly for aesthetics.

Edit: If you do need the actions sync'd on the serverside just remove the //#CLIENTSIDE from the scripts, however there is nothing that puts the script at "rest."

Feel free to PM me if you need help setting it up.

Enjoy.
__________________
Quote:

Last edited by fowlplay4; 04-30-2009 at 12:19 AM..
Reply With Quote