Read about version one.
Read about version two.
Updates
. Code optimization: Restyled the script to make it more legible, added some new comments, and I added checks for onwater() via the new function:
- checkBlockedTile(xValue, yValue) - boolean - checks if the denoted tile is not walkable
I also added the following function:
- resetIfBaddyGetsStuck() - Automatically called to reset the baddy if it gets stuck on a wall, gets stuck in water, or gets glitched due to things like players pausing in the middle of battle and such.
. Smarter AI: The baddy has a greater chance of healing itself once it's in a weak state (1/3 of its total hp or less). Also, although there are instances where you can put the baddies in a glitched state, they will no longer remain glitched thanks to the newly added resetIfBaddyGetsStuck() function.
Important: As suggested by Jerret (fowlplay4), I added a string in the onCreated() function (this.className) that you can change to whatever name you plan on calling the class so that the script will function properly without needing to change anything else (I didn't bother to add any of the chat stuff that he suggested since it seems like it would be a silly thing to include).
Also, you need to remove the period in the middle of: function onWas.Hit().
I don't remember if I changed or added anything else, but this version is much better than the previous ones. It isn't anywhere near perfect, but this is something where I generally just change things around or add things to it, I don't really have much drive to rescript it so look at it as a learning source.
PHP Code:
/*
*GBaddy - v.3.0
*@author Gambet
*/
enum {
WALK,
ATTACK,
DEAD
}
function onCreated() {
this.className = "public_gambet_gbaddy"; //Change this string to match the name of the class this script is in
showcharacter();
this.bodyimg = "body2.png";
this.shieldimg = "no-shield.png";
this.nick = "gBaddy";
this.ap = 0;
this.health = {10, 10}; //{Current Health, Maximum Health}
this.mov_speed = 0.45; //The larger the number, the faster the baddy moves
this.count = int(random(10, 40));
this.start_coordinates = {this.x, this.y};
this.mode = WALK;
for (temp.a = 0; temp.a < 5; temp.a++) this.colors[a] = "black";
if (this.ani != "idle") setcharani("idle","");
setTimer(0.1);
}
function onPlayerEnters() {
onTimeOut(); //Wake up the NPC (in case it was sleeping due to no players being in the level)
this.headimg = "head" @ int(random(0, 1000)) @ ".png"; //NPC changes head image each time you enter the level
}
function onTimeOut() {
if (players.size() > 0)
setTimer(0.1);
else
setTimer(0); //Sleep if no players in level
if (this.mode == WALK) { //Baddy walks around the level when no player is within its attack radius
this.to_check = findnearestplayer(this.x, this.y);
temp.new_x = this.x + vecx(this.dir) * this.mov_speed;
temp.new_y = this.y + vecy(this.dir) * this.mov_speed;
this.count--;
if (this.count > 0) {
temp.to_add_x = new_x + 1.5 + vecx(this.dir);
temp.to_add_y = new_y + 2 + vecy(this.dir);
if (checkBlockedTile(to_add_x, to_add_y))
this.dir = (this.dir + 2) % 4;
else {
setcharani("walk", "");
this.x = new_x;
this.y = new_y;
}
} else {
this.count = int(random(10, 40));
this.dir = (this.dir + 1 + int(random(0, 2)) * 2) % 4;
}
resetIfBaddyGetsStuck();
} else if (this.mode == ATTACK) { //Attack targeted player
this.plyr = this.to_check;
this.dist_x = this.plyr.x - this.x;
this.dist_y = this.plyr.y - this.y;
this.len = (this.dist_x * this.dist_x + this.dist_y * this.dist_y) ^ 0.5;
this.add_x = (this.dist_x / this.len);
this.add_y = (this.dist_y / this.len);
this.check_x = this.x + 1.5;
this.check_y = this.y + 2;
if (!checkBlockedTile(this.check_x+this.add_x, this.check_y+this.add_y)) {
damagePlayer();
moveBaddy();
} else if ((!checkBlockedTile(this.check_x+this.add_x, this.check_y)) || (!checkBlockedTile(this.check_x, this.check_y+this.add_y))) {
damagePlayer();
}
if ((this.health[1] / 3) >= this.health[0]) { //If low on health and other gbaddies in level, call for help
for (n: npcs) {
if (n.isinclass(this.className)) {
if (n.mode != ATTACK) { //Check to see if gbaddy being called for help isn't already attacking a player
n.mode = ATTACK;
this.chat = "Help!";
n.to_check = this.plyr;
}
}
}
}
resetIfBaddyGetsStuck();
}
if (this.mode != DEAD && this.mode != ATTACK) {
if (this.to_check.x in |this.x-10, this.x+10| && this.to_check.y in |this.y-10, this.y+10|) { //Aggro radius - gbaddy will attack a player within this range
if (this.mode != ATTACK) this.mode = ATTACK;
} else
this.mode = WALK;
}
setTimer(0.1);
}
function moveBaddy() {
setcharani("walk", "");
this.x += this.add_x * (this.mov_speed + 0.6);
this.y += this.add_y * (this.mov_speed + 0.6);
if (abs(this.dist_x) > abs(this.dist_y)) {
if (this.dist_x > 0)
this.dir = 3;
else
this.dir = 1;
} else {
if (this.dist_y > 0)
this.dir = 2;
else
this.dir = 0;
}
if (this.resetTimer != "") this.resetTimer = "";
}
function damagePlayer() { //Controls how the gbaddy reacts to the player (the gbaddy either damages the player or heals itself)
temp.randomize = (this.health[0] <= (this.health[1] / 3)) ? int(random(80, 101)) : int(random(0, 101)); //Greater chance of gbaddy healing itself when weak
temp.plyr = findPlayer(this.plyr);
if (plyr.hearts > 0) {
if (randomize in |0, 94|) //Attack player with sword
setcharani("sword", "");
else if (randomize in |94, 98|) //Cause explosion
putexplosion2(3, 3, this.x, this.y);
else if (randomize in |98, 101|) { //Heal
if (this.health[0] < this.health[1]) {
if ((this.health[0] + 5) < this.health[1]) healBaddy(int(random(0, 6)));
else if ((this.health[0] + 5) >= this.health[1]) healBaddy(this.health[1]);
}
}
} else
baddyReset();
}
function healBaddy(amount) { //The gbaddy can heal itself as one of its possible actions
if ((this.health[0] + amount) < this.health[1]) {
this.health[0] += amount;
this.chat = "*Healed for " @ amount @ "*";
} else {
this.health[0] = amount;
this.chat = "*Fully Healed*";
}
setcharani("lift", "");
scheduleevent(1, "HideChat", "idle");
}
function resetIfBaddyGetsStuck() {
if (this.checkCoords[0] != this.x && this.checkCoords[1] != this.y) {
this.checkCoords = {this.x, this.y};
this.resetTimer = "";
} else {
this.resetTimer += 1/100;
if (this.resetTimer >= 0.4) baddyReset();
}
}
function baddyReset() {
this.x = this.start_coordinates[0];
this.y = this.start_coordinates[1];
this.health[0] = this.health[1];
this.mode = WALK;
this.resetTimer = "";
}
function onWas.Hit() { //gBaddy is attacked by a player's sword
if (this.health[0] - ((player.swordpower / 2) + 0.5) > 0) {
if (this.ani != "hurt") {
setcharani("hurt", "");
temp.knockbackAmount = 3;
switch(this.plyr.dir) { //Knockback
case "2":
if (!checkBlockedTile(this.x, this.y+knockbackAmount))
this.y += knockbackAmount;
break;
case "0":
if (!checkBlockedTile(this.x, this.y-knockbackAmount))
this.y -= knockbackAmount;
break;
case "1":
if (!checkBlockedTile(this.x-knockbackAmount, this.y))
this.x -= knockbackAmount;
break;
case "3":
if (!checkBlockedTile(this.x+knockbackAmount, this.y))
this.x += knockbackAmount;
break;
}
scheduleevent(1, "Counter", "");
}
this.health[0] -= (player.swordpower / 2) + 0.5;
this.chat = this.health[0] @ "/" @ this.health[1];
scheduleevent(2, "HideChat", "");
} else {
if (this.mode != DEAD) {
this.mode = DEAD;
this.health[0] = 0;
setcharani("dead", "");
this.chat = this.health[0] @ "/" @ this.health[1];
scheduleevent(8, "Respawn", "");
}
}
}
function checkBlockedTile(xValue, yValue) { //Check if tile is not walkable
if (onwall(xValue, yValue) || onwater(xValue, yValue))
return true;
else
return false;
}
function onRespawn() {
if (this.mode == DEAD) {
setcharani("idle","");
this.chat = "";
this.health[0] = this.health[1];
this.mode = WALK;
}
}
function onCounter() {
if (this.health[0] > 0) setcharani("sword","");
}
function onHideChat() {
if (params[0] == "idle") {
this.chat = "";
setcharani("idle", "");
} else {
this.chat = "";
}
}
Enjoy.