PDA

View Full Version : My "Realistic NPC", and an explanation of how to use


_Z3phyr_
07-12-2007, 04:16 AM
One of the things that I wanted to do was make NPCs that acted or behaved like the ones you see in those cool RPG games. You know, the ones that walk around like normal people and when you go up to them they say some message or whatever. My goal was to make a model of this so that it could be used everywhere and replace the normal default NPC script that just sits around like a douchebag facing the same direction for days on end and says the -exact- same thing every damn time you go up to it.

One semi-unrelated thing I discovered about player movement while working on this script was that players move at a speed of 1+(1/9) tiles per second in all 8 of the directions. What does that mean? It means that if you want to move X tiles to the right at player speed, that means you plug in the numbers so that the time is 1 second per 1.111... tile distance at which you move.

Here's the actual script of what I had so far. I was trying to make it into some kind of makeshift baddy or something after I made this model, but that didn't work out too well for me because I wasn't able to get the actual attack for dmg part of the script to work properly so I said "w/e it can just be a regular npc".



//#CLIENTSIDE
function onCreated() {
init_Attr();
}
function onPlayerenters() {
this.setTimer(random(0,3));
}
function onPlayertouchsme() {
this.setTimer(0);
move(0,0,0.05,0);
dir = getdir(player.x-x,player.y-y);
temp.var = int(random(0,this.messages.size()));
if (temp.var==0) { say2(this.messages[1]); }
else { say2(this.messages[temp.var]); }
this.setTimer(5);
}
function onTimeout() {
doMove();
this.setTimer(int(random(2,5)));
}
function init_Attr() {//in honor of ace2896
showcharacter();
// use this part if its a regular npc
player.head = "head0.png";
player.body = "body.png";
player.shield = "no-shield.png";
colors[0] = "orange"; colors[1] = "white"; colors[2] = "blue"; colors[3] = "red"; colors[4] = "black";

//use this part if its a custom gani npc person
//setcharani("delt_suo-kccutie","kc_kara2.png");
dir = int(random(0,3));
this.messages = {null,"Where have you set the" NL "cabbage this time, Myra!?","My armpits smell like butter.","I wish daddy would stop hitting" NL "mommy.","C-C-C-C-C-COOCAAAINE","MMMMMM CIGARETTES","Well Gertrude, I tend to be more" NL "liberal-centrist on the issue.","Ware da hood @","kekekekekekeke ^_^","I am going to stab your" NL "babies with a sharpened banana.","I once set the internet on fire."};
this.distX = {null,0,-7,0,7,0};
this.distY = {null,0,-7,0,7,0};
this.dispT = {null,.4,.5,.65,.75,1};
}
function doMove() {
temp.array = {int(random(1,5)),int(random(1,5)),int(random(1,5) )};
move(this.distX[temp.array[0]]*this.dispT[temp.array[2]],this.distY[temp.array[1]]*this.dispT[temp.array[2]],this.dispT[temp.array[2]],20);
if (this.distX[temp.array[0]]==this.distY[temp.array[1]]==0) {
dir = int(random(0,3));
setcharani("idle",null);
sleep(this.dispT[temp.array[2]]);
}
else { setcharani("walk",null); }
}

for the record, I made the first slot of my arrays null just out of habit -- it's my thing. sue me.

For those of you who do not know GS2 well but know the basics for reading it (i.e. know a thing or two about scripting in general), lemme spell out some of the variables:

this.messages is an array containing the things an npc says, which can be just one thing or a whole bunch of things.

As for movement, I didn't want to try to be fancy and make it longer than it could've been with onwall checks, so I just stuck with the default thing so it works the same as a player. If it aint broke, don't fix it, right? here's info on the movement vars:

this.distX, this.distY, and this.dispT are index arrays for the options you want to manually give the NPC for movement (a random distance looks weird because its usually small and insignificant) and allows 8-direction only movement. It's important to make the ends 0 and have that 0, #, 0, #, 0 pattern to keep the 8-direction movement.
They each stand for: Distance from X, distance from Y, and displacement of Time, respectively.
In order to move straight righ tin this script, distX must = 7 and distY must = 0. dispT gives the distance (or in my physics class as I originally learned the concept, displacement) that you travel and does not affect speed, which is constant at 1+(1/9) tiles per second aka player speed.

temp.array = { distXindex, distYindex, dispTindex };, which is declared in the doMove() function, is there to pick which values in the distX, distY, and dispT variables are used. This is the part of the script that decides where exactly the player is going and is used in the move command on the next line.
[important about temp.array]: I don't see a reason you'd ever need to alter temp.array — unless of course you are adding or erasing values in the distX, distY, and dispT arrays. The maximum values in the random() functions of temp.array need to match the .size() of each of the corresponding indecies.




With this model I'm sure you can create some interesting, very player-like NPC characters for your GS2 server. I really hope to see these NPCs around a lot. If they're not meant to move you can just erase the doMove() command in the model.

LoneAngelIbesu
07-12-2007, 06:48 AM
It seems that when the NPC is moving, there's no animation (no steps, no sounds).

_Z3phyr_
07-12-2007, 02:42 PM
My apologies. The original script was made for a special gani NPC that floated around and didn't need any kind of separate walking or moving gani. I copy/pasted and overlooked that part. Thanks.

Fixed.

LoneAngelIbesu
07-12-2007, 11:50 PM
Hm, I'm still not seeing the animation. Log on to "Val Dev" and you'll see what I'm seeing. The NPC either doesn't move, but has the animation, or moves without the animation. All I did was remove the setcharani() command and the comments around the basic NPC attributes.

DustyPorViva
07-13-2007, 12:52 AM
Just a note, player's speed is dependent on their shield level. First thing I noticed about movement on Delteria was how I moved at a .6 interval, which I thought was awkward.
As for the idea, I once thought of trying to make a 'smart' AI spar NPC, which would perform player-like moves, such as circling the player and such... but I never got around to it. Graal needs more interactive NPC's.

_Z3phyr_
07-13-2007, 01:16 AM
I had this problem with the animations when I first made the script — I guess the script that I have in this old hard drive is the incomplete one.

If I remember correctly, the way to fix it was just moving the setcharani() to another line in the script. Clearly I put it in the wrong place when I edited it. It's not made to constantly be moving around, and it's made to just stand still and face a random direction every so often (i.e. when distx and disty both = 0).

edit - i tried moving the setcharanis to after the move command and below the dir change.

ShardIceFire
09-09-2007, 02:26 AM
I really like this script alot, the walking/idle animations work but the synching is really odd. sometimes it'll show them perfectly, and other times it'll stay 'idle' while walking, or 'walk' in place. Do you know how to fix that?

Chompy
09-09-2007, 04:24 PM
I really like this script alot, the walking/idle animations work but the synching is really odd. sometimes it'll show them perfectly, and other times it'll stay 'idle' while walking, or 'walk' in place. Do you know how to fix that?

Check if the gani is already displayed instead of re-displaying it without a check if it already is displaying that ani..

ShardIceFire
09-13-2007, 06:50 AM
Check if the gani is already displayed instead of re-displaying it without a check if it already is displaying that ani..

:noob: well for someone who is unfamiliar with GS2, could you explain more in depth? or show the lines of code in his script that would need to be revised?

Chompy
09-13-2007, 04:22 PM
function doMove() {
temp.array = {int(random(1,5)),int(random(1,5)),int(random(1,5) )};
move(this.distX[temp.array[0]]*this.dispT[temp.array[2]],this.distY[temp.array[1]]*this.dispT[temp.array[2]],this.dispT[temp.array[2]],20);
if (this.distX[temp.array[0]]==this.distY[temp.array[1]]==0) {
dir = int(random(0,3));
if (this.ani != "idle") {
setcharani("idle",null);
}
sleep(this.dispT[temp.array[2]]);
}else{
if (this.ani != "walk") {
setcharani("walk",null);
}
}
}

for example ;o

_Z3phyr_
11-02-2009, 05:30 AM
unfortunately, i'm not able to edit my original post

so i'll just post the updated thing with the ap's edit:


//#CLIENTSIDE
function onCreated() {
init_Attr();
}
function onPlayerenters() {
this.setTimer(random(0,3));
}
function onPlayertouchsme() {
this.setTimer(0);
move(0,0,0.05,0);
dir = getdir(player.x-x,player.y-y);
temp.var = int(random(0,this.messages.size()));
if (temp.var==0) { say2(this.messages[1]); }
else { say2(this.messages[temp.var]); }
this.setTimer(5);
}
function onTimeout() {
doMove();
this.setTimer(int(random(2,5)));
}
function init_Attr() {//in honor of ace2896
showcharacter();
// use this part if its a regular npc
player.head = "head0.png";
player.body = "body.png";
player.shield = "no-shield.png";
colors[0] = "orange"; colors[1] = "white"; colors[2] = "blue"; colors[3] = "red"; colors[4] = "black";

//use this part if its a custom gani npc person
//setcharani("delt_suo-kccutie","kc_kara2.png");
dir = int(random(0,3));
this.messages = {null,"Where have you set the" NL "cabbage this time, Myra!?","My armpits smell like butter.","I wish daddy would stop hitting" NL "mommy.","C-C-C-C-C-COOCAAAINE","MMMMMM CIGARETTES","Well Gertrude, I tend to be more" NL "liberal-centrist on the issue.","Ware da hood @","kekekekekekeke ^_^","I am going to stab your" NL "babies with a sharpened banana.","I once set the internet on fire."};
this.distX = {null,0,-7,0,7,0};
this.distY = {null,0,-7,0,7,0};
this.dispT = {null,.4,.5,.65,.75,1};
}
function doMove() {
temp.array = {int(random(1,5)),int(random(1,5)),int(random(1,5) )};
move(this.distX[temp.array[0]]*this.dispT[temp.array[2]],this.distY[temp.array[1]]*this.dispT[temp.array[2]],this.dispT[temp.array[2]],20);
if (this.distX[temp.array[0]]==this.distY[temp.array[1]]==0) {
dir = int(random(0,3));
if (this.ani != "idle") {
setcharani("idle",null);
}
sleep(this.dispT[temp.array[2]]);
}else{
if (this.ani != "walk") {
setcharani("walk",null);
}
}
}


i would have noticed thiat earlier but i've been banned from the forum for the past two years.