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.speed) this.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(movex, movey);
break;
case "reversemove":
this.reversing = true;
this.reversepos = this.moves.size();
onMovementFinished();
break;
case "action":
temp.act = this.("action_" @ data[1]);
for (temp.action: act) {
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.action: act) {
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.action: act) {
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] ? 3 : data[2]);
this.scheduleevent(time, "ClearChat", "");
onNextStep();
break;
case "randomchat":
if (random(0,1) <= data[1]) {
this.chat = data[2];
temp.time = (!data[3] ? 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.x1, temp.y1, temp.x2, temp.y2) {
temp.toreturn = (temp.x2 - temp.x1) ^ 2 + (temp.y2 - temp.y1) ^ 2;
return (temp.toreturn) ^ .5;
}
function doMove(dx, dy) {
temp.distance = GetDistance(this.x,this.y,this.x+dx,this.y+dy);
temp.time = distance / this.speed;
this.dir = getdir((this.x + dx) - this.x,(this.y + dy) - this.y);
move(dx, dy, temp.time, 8);
setcharani(this.ani_walk, "");
if (!this.reversing) this.moves.add({dx, dy});
}
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.