And the thinking script.
function RC_Bots_think(%this)
{
// Realcraft Bots, version 1.5
// Target Finding, Following and Checking
cancel(%this.think); // Cancel any double-runs.
if(!isObject(%this))
return;
for(%i=0;%i<%this.getCount();%i++) // Do a loop on all bots.
{
%b=%this.getObject(%i); // Get the bot object.
if(%b.getState() $= Dead) // We don't want to control dead bots.
continue;
%t=%b.target; // Get the target.
if(isObject(%t) && %t.getState() !$= Dead) // Do we have a target?
{
// Yes! Get distance to target.
%dist = vectorSub(%b.getPosition(),%t.getPosition());
%dist = getWord(%dist,0) + getWord(%dist,1) + getWord(%dist,2);
if(%dist >= 45 || %dist <= -45) // Has target escaped?
{
// Damn, abandon target.
%b.target = 0;
continue;
}
// Find height distance
%hdif = getWord(%t.getPosition(),2) - getWord(%b.getPosition(),2);
if(%hdif >= 1 && %b.getDataBlock().jumper) // Is target above us, and can we jump?
{
%b.playThread(1,jump); // Play Jump Animation
%b.addVelocity("0 0 12.5"); // Jump!
}
while(isEventPending(%b.wanderloop))
cancel(%b.wanderloop); // We don't want it to wander if we have a 100% good target.
%b.setMoveDestination(%t.getPosition()); // Go to the target!
}
else // So, we DONT have a target?
{
// Seems that there is no target, find one.
for(%i=0;%i<ClientGroup.getCount();%i++) // Search for targets on all clients
// NOTE FOR ABOVE: Might change this to container raycast sometime.
{
%cl = ClientGroup.getObject(%i); // Get the client we found
if(!isObject(%cl.player)) // We don't want to inspect dead people
continue;
%bpos=%b.getPosition(); // Save our position
%pos[%cnt++]=%cl.player.getPosition(); // Save their position
%ID[%cnt]=%cl.player; // Save their object
}
%shortDist=""; // Reset shortest distance.
for(%i=1;%i<=%cnt;%i++) // Loop through all saved data.
{
%dist=vectorSub(%bpos,%pos[%i]); // Find distance
if(getSubStr(%dist,0,1) $= "-") // Negative numbers dont matter
%dist = getSubStr(%dist,1,strLen(%dist));
if(%dist >= 15) // Damn, its too far away.
continue;
if(%shortDist$=""||%dist<%shortDist) // Is this the closest we've found yet?
{
%shortDist=%dist; // Sure is, save shortest distance.
%target=%ID[%i]; // And save the object as the closest.
}
}
if(isObject(%target)) // Did we find anything in range?
{
// We did! Set it as target.
%b.target = %target;
}
else if(!isEventPending(%bot.wanderloop))
{
// We didn't, begin wandering and search next time.
%b.wander(50);
}
}
}
%this.think = schedule(500,0,RC_Bots_think,%this);
}
function Player::wander(%this, %radius)
{
if(isObject(%this))
{
if(%this.getClassName() $= "AIplayer")
{
if(%radius < 1)
{
%radius = 25;
}
%this.setMoveSpeed(1);
%tpos = %this.getPosition();
%ranV = getRandom(-25, 25) SPC getRandom(-25, 25) SPC 0;
%dest = vectorAdd(%tpos, %ranV);
while(isObject(firstWord(containerRaycast(%tpos, %dest, $TypeMasks::FxBrickAlwaysObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::VehicleBlockerObjectType))))
{
%ranV = getRandom(-25, 25) SPC getRandom(-25, 25) SPC 0;
%dest = vectorAdd(%tpos, %ranV);
if(%i++ > 10) //So the server doesn't crash if it's encased in something
{
break;
}
}
if(vectorDist(%dest, %this.spawnPosition) > %radius)
{
%dest = %this.spawnPosition;
jumpToZ(%this, getWord(2, %this.spawnPosition));
}
%this.setMoveDestination(%dest, false);
%this.wanderloop = %this.schedule(5000, wander, %radius);
}
}
}
function jumpToZ(%obj, %z)
{
if(isObject(%obj) && strLen(getWord(%dest = %obj.spawnPosition, 2)) > 0)
{
if(%obj.getClassName() $= "AIplayer")
{
%pos = %obj.getPosition();
%obj.setMoveX(0);
if(getRandom() > 0.5)
{
%obj.setMoveX(getRandom(-1,1));
}
if(getWord(%pos, 2) < %z)
{
%obj.botjump();
%obj.jumpsched = %obj.schedule(1000, jumpToZ, %z);
}
}
}
}