Custom ghost baddy

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.

One angry-ass Casper.

Ive seen the ghost image on Maloria, who made it?

Dunno, but they were the images used for ghost mode when the whole P2P rolled around.

Yeah. I just hijacked the ghost mode graphics that come with Graal.