Randomized Walking NPC

Found an old script on my hard drive while I was clearing things out. Don’t really know that I’ll ever use it and I’d rather it not go to waste. It should function properly on servers with multiple people. Not that we have anything like that here.

///////////////////////////////////////
//
// Randomized Walking NPC - Kondie
//
///////////////////////////////////////
//
// MODE VARIANTS
// -------------
// 0 - not busy
// 1 - busy/talking
// ?? 2 - walking ??
//
///////////////////////////////////////

// Must be on if (created) so it's only called once
if (created) { this.ogx = x; this.ogy = y;  }

if (playerenters) {

  timereverywhere;

  showcharacter;
  setcharprop #3,head26.png;
  setcharprop #C0,orange;
  setcharprop #C1,brown;
  setcharprop #C2,brown;
  setcharprop #C3,brown;
  setcharprop #C4,black;
  setcharprop #2,xialza_wooden-shield.gif;
  shieldpower = 1;
  setcharani idle,;
  dir = 2;

  this.xarea = 5; // Move within x tiles left/right of original x - pos
  this.yarea = 5; // Move within x tiles up/down of original y - pos
  this.walkSpeed = .15;

  // Set timeout for walking loop
  timeout = .05;

}

if (timeout) {

  if (this.xdone == 0 && this.ydone == 0) {

    setcharani idle,;

    this.sleep = random(2, 5);
    sleep();

    this.randomdir = (int(random(0,100))) % 4;
    dir = this.randomdir;

    if (this.randomdir == 0 || this.randomdir == 2) { // Up or down (0, 2)

      // Set 'ydone' to not done
      this.ydone = 1;

      this.destinationy = (this.randomdir == 0) ? int(random(this.ogy - this.yarea, y - 2)) : int(random(y + 2, this.ogy + this.yarea));

    } else { // Left or right (1, 3)

      // Set 'xdone' to not done
      this.xdone = 1;

      this.destinationx = (this.randomdir == 1) ? int(random(this.ogx - this.xarea, x - 2)) : int(random(x + 2, this.ogx + this.xarea));

    }

  }

  message ;
  playersCheck();

  if (this.mode == 0) walk();
  else setcharani idle,;

  timeout = .05;

}

if (playertouchsme && this.mode == 1) {

  setcharani idle,;

  this.tempdir = dir;
  dir = (playerdir + 2) % 4;

  message Why the fuck are you so close, bruh?;

  dir = this.tempdir;

  timeout = .05;

}

function walk() {

  if ( !(x in |this.destinationx - 1, this.destinationx + 1|) && x in |this.ogx - this.xarea, this.ogx + this.xarea| ) {

    setcharani walkslow,;
    x += vecx(dir) * this.walkSpeed;

  } else this.xdone = 0;

  if ( !(y in |this.destinationy - 1, this.destinationy + 1|) && y in |this.ogy - this.yarea, this.ogy + this.yarea| ) {

    setcharani walkslow,;
    y += vecy(dir) * this.walkSpeed;

  } else this.ydone = 0;

}

// Checks the surrounding area for any players so
// that if there are any, it will stop to chat.
function playersCheck() {

  for (this.i = 0; this.i < playerscount; this.i ++) {

    if (players[this.i].x in |x - 3, x + 3| && players[this.i].y in |y - 3, y + 3|) {

      this.mode = 1;

    } else this.mode = 0;

  }

}

function sleep() {

  for (this.sleepTimer = 0; this.sleepTimer < this.sleep; this.sleepTimer += .05) {

    playersCheck();
    sleep .05;

  }

}

No wall checks, huh?

Nah, I’m sure people can be responsible enough to change the x and y range, and put it in an open area.

I’m not responsible enough

you are now my scripting idol

Fine, here’s one with wall checks, a cleaner code design for easier modification, and a debug mode. I even added a spot to show you where you should put your quest flags.

[CODE]///////////////////////////////////////
//
// Randomized Walking NPC - Kondie
//
///////////////////////////////////////
//
// MODE VARIANTS
// -------------
// 0 - not busy/walking
// 1 - busy/talking
//
///////////////////////////////////////

// Must be on if (created) so it’s only called once
if (created) {

this.ogx = x;
this.ogy = y;

this.xdone = 1;
this.ydone = 1;

this.mode = 0;

// Allow for debug visuals on screen
this.debug = 0;

}

if (playerenters) {

timereverywhere;

//////////////////////////
// Attribute Declaration
// ---------------------

showcharacter;
setcharprop #3,head26.png;
setcharprop #C0,orange;
setcharprop #C1,brown;
setcharprop #C2,brown;
setcharprop #C3,brown;
setcharprop #C4,black;
setcharprop #2,xialza_wooden-shield.gif;
shieldpower = 1;
setcharani idle,;
dir = 2;

//
////////////////////////

this.xarea = 5; // Move within n tiles left/right of original x - pos
this.yarea = 5; // Move within n tiles up/down of original y - pos
this.walkSpeed = .15;

timeout = .05;

}

if ( playertouchsme && this.mode == 1 ) {

say2 Hey, I really need some help. There are these bandits and . . .;

// ADD QUEST FLAGS AND CONDITIONALS HERE

}

if (timeout) {

// Debugging Tool
if (this.debug == 1) debug();

if (this.xdone == 1 && this.ydone == 1) {

setcharani idle,;

this.sleep = random(2, 5);
sleep();

this.randomdir = (int(random(0,100))) % 4;
dir = this.randomdir;

if (this.randomdir == 0 || this.randomdir == 2) { // Up or down (0, 2)

  // Set 'ydone' to not done
  this.ydone = 0;
  this.destinationy = (this.randomdir == 0) ? int(random(this.ogy - this.yarea, y)) : int(random(y, this.ogy + this.yarea));

} else { // Left or right (1, 3)

  // Set 'xdone' to not done
  this.xdone = 0;
  this.destinationx = (this.randomdir == 1) ? int(random(this.ogx - this.xarea, x)) : int(random(x, this.ogx + this.xarea));

}

}

message ;

playersCheck();
if (this.mode == 0) {

walk();

} else if (this.mode == 1) {

message Psst, come here.;
setcharani idle,;

}

timeout = .05;

}

/////////////////////////////////////////////
//
// Functions
//
// walk(), playersCheck(), sleep(), debug()
//
/////////////////////////////////////////////

function walk() {

// Pre-calculate our movements, so that we can ensure
// they don’t violate any constraints. (Walking range,
// onwall, etc.)

this.xincrease = vecx(dir) * this.walkSpeed;
this.yincrease = vecy(dir) * this.walkSpeed;

if ( !onwall(( x + 1.5 + this.xincrease + (vecx(dir) * 1)), y + 1.5) && !onwall(( x + 1.5 + this.xincrease + (vecx(dir) * 1)), y + 2.5)) {

if ( x != this.destinationx && (x + this.xincrease) in |this.ogx - this.xarea, this.ogx + this.xarea| && (x + this.xincrease) in |0, 64|) {

  setcharani walkslow,;
  x += this.xincrease;

} else this.xdone = 1;

} else this.xdone = 1;

if ( !onwall(x + 0.5, ( y + 2 + this.yincrease + (vecy(dir) * 1) ) ) && !onwall(x + 1.5, ( y + 2 + this.yincrease + (vecy(dir) * 1) ) )) {

if ( y != this.destinationy && (y + this.yincrease) in |this.ogy - this.yarea, this.ogy + this.yarea| && (y + this.yincrease) in |0, 64|) {

  setcharani walkslow,;
  y += this.yincrease;

} else this.ydone = 1;

} else this.ydone = 1;

}

// Checks the surrounding area for any players so
// that if there are any, it will stop to chat.
function playersCheck() {

this.mode = 0;
for (this.i = 0; this.i < playerscount; this.i ++) {
if (players[this.i].x in |x - 3, x + 3| && players[this.i].y in |y - 3, y + 3|) {
this.mode = 1;
}
}

}

function sleep() {

for (this.sleepTimer = 0; this.sleepTimer < this.sleep; this.sleepTimer += .05) {

playersCheck();
sleep .05;

}

}

function debug() {

showtext 501,this.ogx - 5,this.ogy - 8,arial,X: #v(x) Y: #v(y);
changeimgzoom 501,.75;

showtext 502,this.ogx - 5,this.ogy - 7,arial,X-Range: (#v(this.ogx - this.xarea), #v(this.ogx + this.xarea)) Y-Range: (#v(this.ogy - this.yarea), #v(this.ogy + this.yarea));
changeimgzoom 502,.75;

showtext 503,this.ogx - 5,this.ogy - 6,arial,X-Destination: #v(this.destinationx) Y-Destination: #v(this.destinationy);
changeimgzoom 503,.75;

showtext 504,this.ogx - 5,this.ogy - 5,arial,Mode: #v(this.mode);
changeimgzoom 504,.75;

}[/CODE]

Try to avoid using variables in the global scope (i.e. not using ‘this.’) as they will be shared by other npcs. xincrease is one such example, meaning if you had multiple of these npcs in a level, you might see them all walking in the same direction potentially

The global scope, not using this., has so much unused potential.

Not really.

spooon is like the teacher of acting no matter the aversion met.

I think it’s just asking to lead to terrible code to maintain. I prefer to use callnpc and what not to communicate information between npcs. The intent is a lot clearer as well the flow being far more deterministic.

I liked Downsider’s shoot trick.

Oops, that was just a mistake on my part. I always forget to preface variables with ‘this’ after coming back to GS1.