4. Building the NPC.
We figured earlier, that the baddy needs to somehow move, so that it
can hurt the player. You could make it walk around aimlessly, and
hope it hits the player, but what's the fun in that? So, we're going
to make it chase after the player.
To do this, first of all, we actually need a NPC, and since I said
it was going to use showcharacter, the start of the NPC ought to look like
this:
//Beginning Portion of Script
if (created){
showcharacter;
timeout=.05;
}
//End of Script
This will make it so that when the NPC is first started, it'll show
the character, then start its timeout loop. Timeout loops are explained
in sections 2Bv, 2Bv.1.
We need the timeout so that the NPC can continually check to see where
the player is, and move based on that.
The simplest way to move to the baddy is like this:
//Simple Chase Player Script
if (timeout){
if (x>playerx){x++};
if (y<playery){y--};
if (x<playerx){x--};
if (y>playery){y++};
timeout=.05;
}
//End of Script
What this does, is checks everytime there is a timeout to see where
the player is, and move left or right, and up or down 1 space based on
that.
What I don't like about this is that we aren't using any variables
for the speed at which the NPC moves. Why use a variable? Well
instead of doing x++, or y++, and all that, if you don't like the speed
at which the NPC moves, instead of changing the WHOLE script, you change
one value. So lets adds that in to the beginning, and take a look
at the whole script as is.
//Script so far, using a speed var.
if (created){
showcharacter;
this.speed=.5;
timeout=.05;
}
if (timeout){
if (x>playerx){x-=this.speed};
if (y<playery){y+=this.speed};
if (x<playerx){x+=this.speed};
if (y>playery){y-=this.speed};
timeout=.05;
}
//End of Script
So now, each timeout, the NPC moves "this.speed" spaces (so that'd
be .5 spaces each time).
The problem is, this way the NPC will walk through walls (can be good
for special effects or something), and if you were to see this script in
action, you can see the way the NPC moves is kind of jerky. So lets
fix the "jerky" look first, then add onwall detection.
The following script (which will replace the previous movement part),
I adapted from Vangel's script, it moves the NPC to the player, in a straight
line. It'll fix the jerkiness. This may take a lot of explaining,
so I'll comment the lines with a "//"
//Chasing Player, no jerkiness
if (timeout){ //this
we already established =)
this.distx=abs(playerx-x); //this
line sets the variable "this.distx" equal to the distance between the player's
x and the NPC's x.
this.disty=abs(playery-y); //this
line sets the variable "this.disty" equal to the distance between the player's
y and the NPC's y.
if (this.distx==0){ //this starts
the "if" statement for what to do when the x distance between the NPC and
the Player is 0.
this.addy=this.speed; //this
sets the variable "this.addy" to the speed we want the NPC to move at.
Since the NPC has no distance to travel on the x plane (since this.distx=0)
then, it doesn't need to move that way, so we only set the this.addy
}else if (this.disty==0){ //this
starts the "if" statement for what to do when the x distance between the
NPC and the Player is 0.
this.addx=this.speed; //this
sets the variable "this.addx" to the speed we want the NPC to move at.
Since the NPC has no distance to travel on the y plane (since this.disty=0)
then, it doesn't need to move that way, so we only set the this.addx
}else if (this.distx>this.disty){ //In
this case, this is what to do if the distx is not equal to 0, and the disty
is not 0 (thats why I used the "else" statements), and the distx is greater
than the disty.
this.ratio=this.disty/this.distx; //this
sets the var "this.ratio" to the value of disty/distx. This is used
to accurately move the NPC on the y plane, even though the focus of its
movement is on the x plane (since distx is greater than disty).
this.addx=this.speed; //since
the NPC needs to move more on the x plane, than the y plane, this.addx
is the same as the speed we want the NPC to move at.
this.addy=this.speed*this.ratio; //since
the NPC needs to move less on the y plane, than the x plane, this.addy
needs to be the speed we want it to move at, but I multiply it by this.ratio
so the NPC doesn't move to far on the y plane.
}else if (this.disty>this.distx){ //In
this case, this is what to do if the distx is not equal to 0, and the disty
is not 0 (thats why I used the "else" statements), and the disty is greater
than the distx.
this.ratio=this.distx/this.disty; //this
sets the var "this.ratio" to the value of distx/disty. This is used
to accurately move the NPC on the x plane, even though the focus of its
movement is on the y plane (since disty is greater than distx).
this.addy=this.speed; //since
the NPC needs to move more on the y plane, than the x plane, this.addy
is the same as the speed we want the NPC to move at.
this.addx=this.speed*this.ratio; //since
the NPC needs to move less on the x plane, than the y plane, this.addx
needs to be the speed we want it to move at, but I multiply it by this.ratio
so the NPC doesn't move to far on the x plane.
}
if (x>playerx){x-=this.addx}; //since
the npc's x value is greater than the player's x, it needs to negatively
move the distance specified by this.addx
if (y<playery){y+=this.addy}; //since
the npc's y value is less than the player's y, it needs to positively move
the distance specified by this.addy
if (x<playerx){x+=this.addx};//since
the npc's x value is less than the player's x, it needs to positively move
the distance specified by this.addx
if (y>playery){y-=this.addy};//since the
npc's y value is greater than the player's y, it needs to negatively move
the distance specified by this.addy
timeout=.05; //this of course restarts
the loop so the NPC will move again.
}
//End of Script
*whew* Well, I hope my comments explained that one. For
a little more help, you may want to read sections: 2Bi,
2Biv,
2Bv,
2Bv.1,
2E,
2Ei,
2Eii,
12
of the Newbie Scripters Bible.
So, our next problem was to fix the onwall detection. I'm just
gonna copy all that script above, without the comments, and add in onwall
detection (which I will comment and explain).
// Chase Script, with onwall detec.
if (timeout){
this.distx=abs(playerx-x);
this.disty=abs(playery-y);
if (this.distx==0){
this.addy=this.speed;
}else if (this.disty==0){
this.addx=this.speed;
}else if (this.distx>this.disty){
this.ratio=this.disty/this.distx;
this.addx=this.speed;
this.addy=this.speed*this.ratio;
}else if (this.disty>this.distx){
this.ratio=this.distx/this.disty;
this.addy=this.speed;
this.addx=this.speed*this.ratio;
}
if (y<playery&&!onwall(x+1.5,y+3+this.addy)) y+=this.addy;
//This
checks to see if the NPC will be on a wall BEFORE it moves. Notice
I used x+1.5, for a showcharacter, that's the center x value of it.
And y+3 (since the NPC is moving down) thats the bottom of the NPC; but
since it is moving down, I also need to check to see once it moves this.addy
spaces, if there is a wall there. And if there isn't a wall, then
the NPC can move.
if (y>playery&&!onwall(x+1.5,y-this.addy)) y-=this.addy;
//This
is the same as above, but for when the NPC is moving up instead of down
(so I used y-this.addy instead of y+3+this.addy).
if (x<playerx&&!onwall(x+3+this.addx,y+1.5)) x+=this.addx;
//This
checks to see if the NPC will be on a wall BEFORE it moves. Notice
I used y+1.5, for a showcharacter, that's the center y value of it.
And x+3 (since the NPC is moving right) thats the far right of the NPC;
but since it is moving right, I also need to check to see once it moves
this.addx spaces, if there is a wall there. And if there isn't a
wall, then the NPC can move.
if (x>playerx&&!onwall(x-this.addx,y+1.5)) x-=this.addx;
//This
is the same as above, but for when the NPC is moving left instead of right
(so I used x-this.addy instead of x+3+this.addx).
timeout=.05;
}
Again, if you need help with that, read these sections: 2G,
2Gi,
2Gii,
11
(the onwall part).
Now that we have all of our movement stuff in place, we need to make
the baddy actually hurt the player. Well, the simplest way to do
that is like so:
//Hurting Player Script
if (playertouchsme){
hurt 1;
}
//End of Script
A little trick I picked up from Bruges, is that even though the NPC
appears to be touching the player, "playertouchsme" may not always fire
(thats because of the placement of x,y values as explained in section 2G).
So, to be more accurate we can add in:
//Extra hit detection
if (abs((x+1.5)-(playerx+1.5))<=3 &&abs((playery+1.5)-(y+1.5))<=3)
{
hurt 1;
}
//End of Script
Of course, we can change the hurting stuff slightly, by adding in a
variable to the beginning of the NPC, so you can change the amount of life
the NPC takes off when the player touches it.
//Starting the NPC + New var for hurting.
if (created){
showcharacter;
this.speed=.5;
this.pow=2; //The amount of life
(in half hearts) to be taken off when the player touches the NPC.
timeout=.05;
}
if (playertouchsme||(abs((x+1.5)-(playerx+1.5))<=1.5 &&abs((playery+3)-(y+1.5))<=3)){
hurt this.pow;
}
//End of Script
Like I said, that's an incredibly simple way to hurt the player.
Later in the guide I'll tell you other ways (such as the use of swords,
and bows).
The last problem we need to tackle is the NPC needs to be able to be
hurt. So, we need to add in some stuff to the beginning:
//Some added beginning script stuff
if (created){
showcharacter;
this.speed=.5;
this.pow=2;
hearts=3; //the amount of life it
has
hurtdx=0; //this needs to be set
to 0 so the NPC can be hurt by NPC weapons
hurtdy=0; //this needs to be set
to 0 so the NPC can be hurt by NPC weapons
timeout=.05;
}
//End Script
That obviously gives the NPC some life, as well as allows it to be
hit by NPC weapons.
Now, to add in some stuff so it can actually be hurt:
//Hurting Script
if ((washit||wasshot||exploded||waspelt)&&hearts>0){ //This
checks to see of the usual stuff that could happen to a NPC so it loses
life, but it has to still have life, so I included the hearts>0 thing.
if (washit){ //what
to do when hit by a sword or NPC weapon
hurtdx=x; //necessary
so the dang thing can be hurt by a NPC weapon
hurtdy=y; //necessary
so the dang thing can be hurt by a NPC weapon
hearts-=playerswordpower;
//this
is the amount of hearts to be taken off.
}
if (wasshot){
hearts--; //Back
in the day, you could subtract the player's shoot power, but that variable
no longer exists, so I usually make it so the NPC loses 1 heart when shot.
}
if (exploded){
hearts--; //Same
as above, but for when its exploded.
}
if (waspelt){
if (peltwithbush) hearts-=.5;
//peltwithbush
means the player threw a bush at the NPC. This line hurts the NPC by 1
half heart which is the strength of a bush.
if (peltwithsign || peltwithvase
|| peltwithstone) hearts-=1; //these
three objects all drain 2 half hearts
if (peltwithblackstone) hearts
-=1.5; // Black stones are a bit stronger,
so they take off more. Feel free to adjust any of the amounts of
hearts taken off, I just go these values from Bruges.
}
}
//End of Script
Now it can be hurt.
This can be all well and good, but if you leave hurtdx and hurtdy as
is, then the NPC can be a bit glitchy. So, just add in the following
stuff afterwards:
//Unsetting hurtdx and hurtdy
if (hurtdx!=0||hurtdy!=0){
hurtdx=0;
hurtdy=0;
}
//End of Script
Well that's it for the 3 things a NPC baddy needs to do.
Lets put it all together, and see what we got.
//Script So far
if (created){
showcharacter;
this.speed=.5;
this.pow=2;
hearts=3;
hurtdx=0;
hurtdy=0;
timeout=.05;
}
if (timeout){
this.distx=abs(playerx-x);
this.disty=abs(playery-y);
if (this.distx==0){
this.addy=this.speed;
}else if (this.disty==0){
this.addx=this.speed;
}else if (this.distx>this.disty){
this.ratio=this.disty/this.distx;
this.addx=this.speed;
this.addy=this.speed*this.ratio;
}else if (this.disty>this.distx){
this.ratio=this.distx/this.disty;
this.addy=this.speed;
this.addx=this.speed*this.ratio;
}
if (y<playery&&!onwall(x+1.5,y+3+this.addy)) y+=this.addy;
if (y>playery&&!onwall(x+1.5,y-this.addy)) y-=this.addy;
if (x<playerx&&!onwall(x+3+this.addx,y+1.5)) x+=this.addx;
if (x>playerx&&!onwall(x-this.addx,y+1.5)) x-=this.addx;
timeout=.05;
}
if (playertouchsme||(abs((x+1.5)-(playerx+1.5))<=3 &&abs((playery+1.5)-(y+1.5))<=3)){
hurt this.pow;
}
if ((washit||wasshot||exploded||waspelt)&&hearts>0){
if (washit){
hurtdx=x;
hurtdy=y;
hearts-=playerswordpower;
}
if (wasshot){
hearts--;
}
if (exploded){
hearts--;
}
if (waspelt){
if (peltwithbush) hearts-=.5;
if (peltwithsign || peltwithvase
|| peltwithstone) hearts-=1;
if (peltwithblackstone) hearts
-=1.5;
}
}
if (hurtdx!=0||hurtdy!=0){
hurtdx=0;
hurtdy=0;
}
// End of Script
Yay. It does what we wanted. But wait a sec... it doesn't
LOOK good...