I used to have this on the forums before I accidentally deleted all my posts. Anyway, this is a working custom ghost baddy. It is the same one I have on Waffles.
This is the original version. It shows how save[] variables can be used to control NPC execution properly with multiple players in the level.
// Created by Nalin
if (created || playerenters) {
// Respawn position of the baddy.
this.loc_orig={36.5,27};
}
// save[0] = hurt damage
// save[1] = x location of player who hurt me
// save[2] = y location of player who hurt me
// save[9] = if 1, is activated, if 0, is resting in grave
if (created || playerenters) {
// Direction to move. Can move in 8 directions. 0 is up. 4 is down.
this.move_dir=4;
// Number of tiles to move each second.
this.move_len=4;
// Set ourself as resting in a grave.
if (save[9] != 1) save[9]=0;
// Set our shape so we can damage the baddy.
setshape 1,48,48;
dontblock;
timeout = 0.5;
}
if ((created || playerenters) && isleader) {
if (save[9] == 0) Reset();
timeout = 0.1;
}
// Sometimes we can lose our timeout. Make sure that we don't!
if (isleader) {
if (npcs[-1].timeout == 0) timeout = 0.1;
}
// If a player touches us and we are in the grave, activate!
// Else, send out a hitobjects to hurt the player.
if (playertouchsme) {
if (save[9] == 0) {
save[9] = 1;
hearts = 3;
setcharani rha_ghost,;
timeout=1;
} else hitobjects 0.5,x+1.5,y+1.5;
}
// Main timeout loop.
if (timeout) {
if (save[9] == 1) {
// Check to see if a player in the level hit our NPC.
if (save[0] != 0) {
this.hitpower = save[0];
save[0] = 0;
doHit();
}
// Check if we are dead.
if (hearts == 0) {
DropLoot();
Reset();
timeout=0.1;
} else {
// Move around.
hitobjects 0.5,x+1.5,y+1.5;
this.move_dir=int(random(this.move_dir-1,this.move_dir+2));
if ( this.move_dir < 0 ) this.move_dir+=8;
if ( this.move_dir > 7 ) this.move_dir-=8;
this.move_loc={ 0, 0 };
if ( this.move_dir in {1,2,3} ) this.move_loc[0] = -this.move_len;
if ( this.move_dir in {5,6,7} ) this.move_loc[0] = this.move_len;
if ( this.move_dir in {7,0,1} ) this.move_loc[1] = -this.move_len;
if ( this.move_dir in {3,4,5} ) this.move_loc[1] = this.move_len;
if ( x+this.move_loc[0] > 61 || x+this.move_loc[0] < 0 ) { this.move_loc[0] = 0; this.move_dir+=4; }
if ( y+this.move_loc[1] > 61 || y+this.move_loc[1] < 0 ) { this.move_loc[1] = 0; this.move_dir+=4; }
move this.move_loc[0], this.move_loc[1], 1, 16+1;
sleep 0.5;
hitobjects 0.5,x+1.5,y+1.5;
timeout=0.5;
}
} else {
Reset();
timeout=1;
}
}
// If a player hit me, save his position and sword power.
// If the leader hit me, we can instantly do the hit code.
// Else, the hit code will be run next timeout loop.
if (washit) {
save[1] = playerx + 32;
save[2] = playery + 32;
if (isleader) {
this.hitpower = playerswordpower;
doHit();
}
else save[0] = playerswordpower;
}
// Do our hurt logic.
function doHit() {
if (hearts > 0) {
hearts -= this.hitpower/2;
this.dx = x-(save[1] - 32);
this.dy = y-(save[2] - 32);
this.len = (this.dx*this.dx+this.dy*this.dy)^0.5;
this.hurtdx = this.dx/this.len;
this.hurtdy = this.dy/this.len;
// Interrupt our current move and apply a hurt move.
x = x;
y = y;
move this.hurtdx*3, this.hurtdy*3, 0.25, 0;
if (hearts <= 0) timeout=1;
else timeout=0.5;
}
}
// Reset back to our original state.
function Reset() {
hearts = 0;
save[9] = 0;
setcharani rha_blank,;
x = this.loc_orig[0];
y = this.loc_orig[1];
}
// Yay, loot dropping.
function DropLoot() {
this.loot = int(random(0,20));
if ( this.loot in |0,7| ) lay greenrupee;
if ( this.loot in |8,9| ) lay bluerupee;
if ( this.loot == 10 ) lay redrupee;
if ( this.loot in |11,12| ) lay bombs;
if ( this.loot in |13,14| ) lay darts;
if ( this.loot in |15,16| ) lay heart;
}
Here is the current version. It requires the latest SVN build of the gserver to run. This version runs smooth for everybody in the level.
// Created by Nalin
// The following line prevents normal x/y location packets from being sent.
//#BLOCKPOSITIONUPDATES
if (created || playerenters) {
this.loc_orig={36.5,27};
}
// save[9] = if 1, is activated, if 0, is resting in grave
if (created || playerenters) {
// Direction to move. Can move in 8 directions. 0 is up. 4 is down.
this.move_dir=4;
// Number of tiles to move each second.
this.move_len=4;
// Set ourself as resting in a grave.
if (save[9] != 1) save[9]=0;
// Set our shape so we can damage the baddy.
setshape 1,48,48;
dontblock;
timeout = 0.5;
}
if ((created || playerenters) && isleader) {
if (save[9] == 0) Reset();
timeout = 0.1;
}
// Sometimes we can lose our timeout. Make sure that we don't!
if (isleader) {
if (npcs[-1].timeout == 0) timeout = 0.1;
}
// If a player touches us and we are in the grave, activate!
// Else, send out a hitobjects to hurt the player.
if (playertouchsme) {
if (save[9] == 0) {
save[9] = 1;
hearts = 3;
setcharani rha_ghost,;
timeout=1;
} else hitobjects 0.5,x+1.5,y+1.5;
}
// Main timeout loop.
if (timeout) {
if (save[9] == 1) {
// Check if we are dead.
if (hearts == 0) {
DropLoot();
Reset();
timeout=0.1;
} else {
// Move around.
hitobjects 0.5,x+1.5,y+1.5;
this.move_dir=int(random(this.move_dir-1,this.move_dir+2));
if ( this.move_dir < 0 ) this.move_dir+=8;
if ( this.move_dir > 7 ) this.move_dir-=8;
this.move_loc={ 0, 0 };
if ( this.move_dir in {1,2,3} ) this.move_loc[0] = -this.move_len;
if ( this.move_dir in {5,6,7} ) this.move_loc[0] = this.move_len;
if ( this.move_dir in {7,0,1} ) this.move_loc[1] = -this.move_len;
if ( this.move_dir in {3,4,5} ) this.move_loc[1] = this.move_len;
if ( (this.move_loc[0] > 0 && x+this.move_loc[0] > 61) || (this.move_loc[0] < 0 && x+this.move_loc[0] < 0) ) { this.move_loc[0] = 0; this.move_dir+=4; }
if ( (this.move_loc[1] > 0 && y+this.move_loc[1] > 61) || (this.move_loc[1] < 0 && y+this.move_loc[1] < 0) ) { this.move_loc[1] = 0; this.move_dir+=4; }
// Send a serverside move packet to all players.
triggeraction 0,0,gr.npc.move,#v(npcs[-1].id),#v(this.move_loc[0]),#v(this.move_loc[1]),1,17;
sleep 0.5;
hitobjects 0.5,x+1.5,y+1.5;
timeout=0.5;
}
} else {
Reset();
timeout=1;
}
}
// If a player hit me, do the hit logic.
if (washit) {
this.hitpower = playerswordpower;
doHit();
}
// Do our hurt logic.
function doHit() {
if (hearts > 0) {
hearts -= this.hitpower/2;
this.dx = x-playerx;
this.dy = y-playery;
this.len = (this.dx*this.dx+this.dy*this.dy)^0.5;
this.hurtdx = this.dx/this.len;
this.hurtdy = this.dy/this.len;
// Interrupt our current move and apply a hurt move.
triggeraction 0,0,gr.npc.setpos,#v(npcs[-1].id),#v(x),#v(y);
triggeraction 0,0,gr.npc.move,#v(npcs[-1].id),#v(this.hurtdx*3),#v(this.hurtdy*3),0.25,1;
if (hearts <= 0) timeout=1;
else timeout=0.5;
}
}
// Reset back to our original state.
function Reset() {
hearts = 0;
save[9] = 0;
setcharani rha_blank,;
// Since normal x/y position modification is prevented,
// use this to set a new position.
triggeraction 0,0,gr.npc.setpos,#v(npcs[-1].id),#v(this.loc_orig[0]),#v(this.loc_orig[1]);
}
// Yay, loot dropping.
function DropLoot() {
this.loot = int(random(0,20));
if ( this.loot in |0,4| ) lay heart;
if ( this.loot in |5,9| ) lay greenrupee;
if ( this.loot in |10,11| ) lay bluerupee;
if ( this.loot == 12 ) lay redrupee;
if ( this.loot in |13,14| ) lay bombs;
if ( this.loot in |15,16| ) lay darts;
}
I’ve attached the required files. Enjoy.