Official Soldat Forums

Server Talk => Scripting Discussions and Help => Topic started by: D4NG3R NL on May 15, 2009, 02:27:34 am

Title: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 15, 2009, 02:27:34 am
I need to activate the "Witch" bot when you come to near.

Part of code I have now:

Code: [Select]
//Made by D4NG3R for the zombie survival server, based on the codec of the origional author:
//Shantec


function OnPlayerDamage(Victim, Shooter: byte; Damage: integer): integer;
var Vx,Vy,Sx,Sy: single;
begin
  if (IdToName(victim) = 'Witch') and (IdToName(shooter) <> 'Witch') then begin
  Vx := GetPlayerStat(Victim,'X');
  Vy := GetPlayerStat(Victim,'Y');
  Sx := GetPlayerStat(Shooter,'X');
  Sy := GetPlayerStat(Shooter,'Y');
If (Distance(Vx,Vy,Sx,Sy) < 180) and (GetPlayerStat(victim,'Alive') = true) then begin
  //Here needs to bee the bot start part
  WriteConsole(ID,'You startled a Witch!',RGB(200,200,200))
end;
  end;
end;

procedure OnPlayerKill(Killer, Victim: byte; Weapon: string);
begin
  if (IdToName(victim) = 'Witch') and (IdToName(killer) <> 'Witch') then begin
  WriteConsole(ID,'You killed the Witch.',RGB(200,200,200))
  end;
end;
Title: Re: How to activate bot on see within a certain area.
Post by: DorkeyDear on May 15, 2009, 05:17:13 am
You could use Avarax's SpawnBot function which adds a bot to the game at a given location. It's under Script Releases somewhere.

I also suggest doing if (GetPlayerStat(#, 'Human') = false) then.
Title: Re: How to activate bot on see within a certain area.
Post by: shantec on May 15, 2009, 03:03:01 pm
First you must to do is dummy bot, named witch (is it possible? or does the name need to be "dummy"?), i think you want to make LeftForDead alike witch, amirite?
Then, if the distance is small enough, kick the dummy named witch, and spawn "living" witch bot
Quote
using Avarax's SpawnBot
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 15, 2009, 05:57:54 pm
First you must to do is dummy bot, named witch (is it possible? or does the name need to be "dummy"?), i think you want to make LeftForDead alike witch, amirite?
Then, if the distance is small enough, kick the dummy named witch, and spawn "living" witch bot
Quote
using Avarax's SpawnBot

Hmmm, that actually is a great idea...

Code: [Select]
//Witch Testing script based on some codec of: Shantec's "Taliban" script

function PlaceBot(botname: string; team: byte; X,Y: single): byte;
function OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;
var Wx,Wy,Px,Py: single;
begin
if (IdToName(Witch) = 'Dummy') and (IdToName(Player) <> 'Witch') then begin
Wx := GetPlayerStat(Witch,'X');
Wy := GetPlayerStat(Witch,'Y');
Px := GetPlayerStat(Player,'X');
Py := GetPlayerStat(Player,'Y');
If (Distance(Wx,Wy,Px,Py) < 180) and (GetPlayerStat(Witch,'Alive') = true) then begin
Command('/kick Dummy')
PlaceBot('Witch',GetPlayerStat(Dummy,'Team'),GetPlayerStat(Dummy,'X'),GetPlayerStat(Dummy,'Y'));
WriteConsole(ID, 'You startled the witch!', $FFFFFF);
end;
end;

procedure OnPlayerKill(Witch: byte; Weapon: string);
begin
if (IdToName(Witch) = 'Witch') then begin
Command('/kick Witch')
Command('/Addbot2 Dummy)
end;
end;

[Error] (4:1) 'BEGIN' expected
Line 4 word 1 already IS begin....
Whats wrong xD
Title: Re: How to activate bot on see within a certain area.
Post by: Hacktank on May 15, 2009, 07:03:32 pm
You have one function right after another.

Code: [Select]
function PlaceBot(botname: string; team: byte; X,Y: single): byte;
function OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;
var Wx,Wy,Px,Py: single;
begin;

Code: [Select]
function PlaceBot(botname: string; team: byte; X,Y: single): byte;
begin
//actual placebot stuff here
end;

function OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;
var Wx,Wy,Px,Py: single;
begin;

PS: lines are counted weather blank or not. Also the compiler adds one for some reason. So if it gave an error on line 5 spot 3 it would actuall be line 4 spot 3, whitch on yours is
Code: [Select]
fu >n< ction OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;.
Title: Re: How to activate bot on see within a certain area.
Post by: Gizd on May 16, 2009, 01:48:33 am
Quote
function OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;
...
WriteConsole(ID, 'You startled the witch!', $FFFFFF);
There is no 'ID' there...
And change Command('/kick Dummy') into KickPlayer(Witch)
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 16, 2009, 03:10:15 am
Code: [Select]
function PlaceBot(botname: string; team: byte; X,Y: single): byte;
var i,n,tempType: byte;
    spawn: array of byte;
    tempX,tempY: single;
begin
  SetArrayLength(spawn,0);
  n:=0;
  for i:=1 to 254 do
    If GetSpawnStat(i,'Active') = true then
      If GetSpawnStat(i,'Style') = team then begin
        n:=n + 1;
        SetArrayLength(spawn,n);
        spawn[n-1]:=i;
        SetSpawnStat(i,'Active',false);
      end;
  for i:=1 to 254 do
    If GetSpawnStat(i,'Active') = false then begin
      SetSpawnStat(i,'Active',true);
      tempType:=GetSpawnStat(i,'Style');
      tempX:=GetSpawnStat(i,'X');
      tempY:=GetSpawnStat(i,'Y');
      SetSpawnStat(i,'Style',team);
      SetSpawnStat(i,'X',X);
      SetSpawnStat(i,'Y',Y);
      break;
    end;
  result:=Command('/addbot' + inttostr(team) + ' ' + botname);
  SetSpawnStat(i,'Active',false);
  SetSpawnStat(i,'Style',tempType);
  SetSpawnStat(i,'X',tempX);
  SetSpawnStat(i,'Y',tempY);
  If n > 0 then
    for i:=0 to n-1 do
      SetSpawnStat(spawn[i],'Active',true);
end;

copied & pasted from Avarax MMod.
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 16, 2009, 01:57:04 pm
Ok now its making me kinda dizzy xD

What I need to get:

X & Y of the "Dummy"
Then at those X & Y coordinates a "Witch" bot spawns

What happens:
Dummy gets kicked
Witch gets added, when killed she gets kicked & after 60 seconds a Dummy re-spawns.

The script should run when the player gets in a certain area OR when the dummy gets damaged!

I know the dummy gets hit it needs to run a OnDamage script, but HOW do I have to get the distance to work to?
Title: Re: How to activate bot on see within a certain area.
Post by: iDante on May 16, 2009, 02:17:11 pm
In AppOnIdle check everyone's distance to the bot using the Distance function in the scriptcore.
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 16, 2009, 02:17:26 pm
Distance(x,y,x2,y2: single): single;

if Distance(x,y,x2,y2) < 150 then //...

or for short distances like 0-50 units

if Abs(x - x2) <= 50 then
  if Abs(y - y2) <= 50 then // ...

Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 16, 2009, 02:31:55 pm
In AppOnIdle check everyone's distance to the bot using the Distance function in the scriptcore.

If the script constantly checks the player positions wouldn't it make lag?
Title: Re: How to activate bot on see within a certain area.
Post by: EnEsCe on May 16, 2009, 02:45:23 pm
In AppOnIdle check everyone's distance to the bot using the Distance function in the scriptcore.

If the script constantly checks the player positions wouldn't it make lag?
Yep, the Distance function's code is quite heavy so it is unwise to be calling it frequently.
Title: Re: How to activate bot on see within a certain area.
Post by: danmer on May 16, 2009, 04:56:23 pm
well... isn't it just some powers and sqrt? Or do you mean that even that is heavy already? Oo
Title: Re: How to activate bot on see within a certain area.
Post by: DorkeyDear on May 16, 2009, 04:58:53 pm
You could also have:
Code: [Select]
const
  Distance = 150.0;

var
  Distance2: double;

procedure ActivateServer();
begin
  Distance2 := Distance * Distance;
end;

procedure AppOnIdle(Ticks: cardinal);
begin
  if (GetPlayerStat(__, 'X') * GetPlayerStat(__, 'X') + GetPlayerStat(__, 'Y') * GetPlayerStat(__, 'Y') < Distance2) then begin
    // ...
  end;
end;
That way it never does any sort of Sqrt, which is a source (afaik) of some slowness. If not, it now only squares 2 numbers (by multiplying, not some complex power algorithm) instead of three (except on activateserver, which it squares the 3rd number, but sense it doesn't vary, only need to do this once)
Title: Re: How to activate bot on see within a certain area.
Post by: iDante on May 16, 2009, 07:51:13 pm
It's a good idea Curt, here's how I'd implement that.
Code: [Select]
const
   Distance = 150.0;
   BOTID = 1;

var
   Distance2: single;

procedure ActivateServer;
begin
   Distance2 := Distance * Distance;
end;

procedure AppOnIdle(Ticks: cardinal);
var
   i: integer;
   wx,wy,x,y: single;
begin
   GetPlayerXY(BOTID, wx, wy);
   for i := 1 to 32 do begin
      GetPlayerXY(i, x, y);
      if ( ( (x-wx)*(x-wx) + (y-wy)*(y-wy) ) < Distance2 ) AND ( i <> BOTID ) then begin
         { It is close enough, do stuff }
      end;
   end;
end;
Has not been tested, probably wrong
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 17, 2009, 03:16:44 am
Faster and working version:

Code: [Select]
const
  Dist = 150;
  Max_Players = 20;
  Distance2 = Dist*Dist;

procedure AppOnIdle(Ticks: cardinal);
var
   i: byte;
   x,y,x2,y2: single;
begin
  GetPlayerXY(BOTID, x, y);
  for i := 1 to Max_Players do
    if GetPlayerStat(i,'Active') then
      if i <> BOTID then
      begin
        GetPlayerXY(i, x2, y2);
        x2:=x2-x;
        y2:=y2-y;
        if x2*x2 + y2*y2 < Distance2 then
        begin
          { It is close enough, do stuff }
        end;
   end;
end;
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 17, 2009, 11:18:58 am
Faster and working version:

Code: [Select]
const
  Dist = 150;
  Max_Players = 20;
  Distance2 = Dist*Dist;

procedure AppOnIdle(Ticks: cardinal);
var
   i: byte;
   x,y,x2,y2: single;
begin
  GetPlayerXY(BOTID, x, y);
  for i := 1 to Max_Players do
    if GetPlayerStat(i,'Active') then
      if i <> BOTID then
      begin
        GetPlayerXY(i, x2, y2);
        x2:=x2-x;
        y2:=y2-y;
        if x2*x2 + y2*y2 < Distance2 then
        begin
          { It is close enough, do stuff }
        end;
   end;
end;

Unknown Identifier BOTID. When I change BOTID to Witch It gives the same thing...

Or do I have to add something to this!? (Sorry I'm quite new to the whole scripting part of soldat.... :(
Title: Re: How to activate bot on see within a certain area.
Post by: croat1gamer on May 17, 2009, 11:31:37 am
BOTID is the id number of the bot in the game.
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 17, 2009, 11:32:49 am
BOTID is for you to know where to put ID of the witch.
There is BOTID in two places, maybe you missed one
Quote
  GetPlayerXY(BOTID, x, y);
  for i := 1 to Max_Players do
    if GetPlayerStat(i,'Active') then
      if i <> BOTID then
Title: Re: How to activate bot on see within a certain area.
Post by: DorkeyDear on May 17, 2009, 11:47:09 am
Code: [Select]
const
  Dist = 150.0;
  Dist2 = Dist * Dist;

function PlaceBot(botname: string; team: byte; X,Y: single): byte;
var i,n,tempType: byte;
    spawn: array of byte;
    tempX,tempY: single;
begin
  SetArrayLength(spawn,0);
  n:=0;
  for i:=1 to 254 do
    If GetSpawnStat(i,'Active') = true then
      If GetSpawnStat(i,'Style') = team then begin
        n:=n + 1;
        SetArrayLength(spawn,n);
        spawn[n-1]:=i;
        SetSpawnStat(i,'Active',false);
      end;
  for i:=1 to 254 do
    If GetSpawnStat(i,'Active') = false then begin
      SetSpawnStat(i,'Active',true);
      tempType:=GetSpawnStat(i,'Style');
      tempX:=GetSpawnStat(i,'X');
      tempY:=GetSpawnStat(i,'Y');
      SetSpawnStat(i,'Style',team);
      SetSpawnStat(i,'X',X);
      SetSpawnStat(i,'Y',Y);
      break;
    end;
  result:=Command('/addbot' + inttostr(team) + ' ' + botname);
  SetSpawnStat(i,'Active',false);
  SetSpawnStat(i,'Style',tempType);
  SetSpawnStat(i,'X',tempX);
  SetSpawnStat(i,'Y',tempY);
  If n > 0 then
    for i:=0 to n-1 do
      SetSpawnStat(spawn[i],'Active',true);
end;

procedure AppOnIdle(Ticks: cardinal);
var
  i, j: byte;
  X1, Y1, X2, Y2, DX, DY: single;
begin
  for i := 1 to 32 do
    if (GetPlayerStat(i, 'Active') = true) then
      if (GetPlayerStat(i, 'Human') = true) then begin
        GetPlayerXY(i, X1, Y1);
        for j := 1 to 32 do
          if ((i <> j) and (GetPlayerStat(j, 'Active') = true)) then
            if ((GetPlayerStat(j, 'Human') = false) and (GetPlayerStat(j, 'Name') = 'Dummy') and (GetPlayerStat(i, 'Team') <> GetPlayerStat(j, 'Team'))) then begin
              GetPlayerXY(j, X2, Y2);
              DX := X2 - X1;
              DY := Y2 - Y1;
              if (DX * DX + DY * DY < Dist2) then begin
                PlaceBot('Witch', GetPlayerStat(j, 'Team'), X2, Y2);
                KickPlayer(j);
              end;
            end;
      end;
end;
compiles, not tested;
should, if a human goes in range of a bot named Dummy; replace Dummy with Witch
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 17, 2009, 12:50:02 pm
Having Max_Players global const instead of doing 1024 loops every second wouldn't be that bad. When Max_Players is 16, there will be only 256 loops.

If I'm not wrong, when we have 16 players on the server, there will be checked about 220 times if string 'Dummy' matches.

Doing this so could help a bit;
Quote
for j := 1 to 32 do
  if (i <> j) and (GetPlayerStat(j, 'Active')) then
    if GetPlayerStat(j, 'Human') = false then
      if GetPlayerStat(i, 'Team') <> GetPlayerStat(j, 'Team') then
        if IDToName(j) = 'Dummy' then begin

What is more, I would recommend using different, simple version of placebot which doesn't create dinamic arrays which may be cause of crashes.
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 17, 2009, 11:57:15 pm
Here use insted of 32 that:
Code: [Select]
function MaxID: Byte;
var i: Byte;
begin
    Result := 0;
    for i := 1 to 32 do
      if GetPlayerStat(i,'Active') = True then
        Result := i;
end;
Not tested, i did just write it so but it shold work.

I found that somewhere in MMod from Avarax sme time ago.
It will return the highest ID so you won't loop for players which aren't there.
Title: Re: How to activate bot on see within a certain area.
Post by: DorkeyDear on May 18, 2009, 05:30:01 am
I suggest putting MaxID into a variable (well, it has no purpose if you call it more than once; would just make the speed slower)
Code: [Select]
IdHigh := MaxId();It could possibly be even better if you got the highest bot named Dummy too for the 2nd loop

Code: [Select]
function MaxId(): byte;
begin
  for Result := 32 downto 1 do
    if (GetPlayerStat(Result, 'Active') = true) then
      exit;
  Result := 0;
end;
I believe would be more efficient.
Title: Re: How to activate bot on see within a certain area.
Post by: danmer on May 18, 2009, 11:40:08 am
yeah just assign it to some var in apponidle and use the var for all your loops
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 18, 2009, 12:00:36 pm
Yes But when doing this you should update the value every time a player joins or quits ;D
Title: Re: How to activate bot on see within a certain area.
Post by: danmer on May 18, 2009, 02:02:58 pm
Yes But when doing this you should update the value every time a player joins or quits ;D
duh, if apponidle isn't enough for you


edit: wait... yeah, makes more sense to just do it on join/quit X.x
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 18, 2009, 02:10:06 pm
OnCommand (addbot) and ActivateServer (recompile) aswell
Title: Re: How to activate bot on see within a certain area.
Post by: shantec on May 18, 2009, 03:46:08 pm
I suggest storing dummys x / y  as variable, so appon loop needs to do only 32 (or less) checks..
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 19, 2009, 07:33:32 am
Quote
OnCommand (addbot)
Why? It will be checked when the bot joins a team. Yes, ActivateServer, but every scripter shoul know its better when reseting all variables on activate server, or not?
Title: Re: How to activate bot on see within a certain area.
Post by: tk on May 19, 2009, 07:58:34 am
OnJoinTeam(), OnJoinGame() is not called while adding the bot.
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 19, 2009, 08:03:50 am
Just tested and yes you're right, that's bad that it doesn't call the two procedures :/
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 19, 2009, 09:50:53 am
Ok, thx for the script :) it works fine!

The only things I need to add are these:

Witch gets kicked when she dies, Dummy respawns after this.
When the dummy get shot (from any distance)the Witch will be activated to (OnPlayerDamage ?)
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 19, 2009, 11:37:54 am
yes, use OnPlayerDamage for this.
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 19, 2009, 12:22:23 pm
yes, use OnPlayerDamage for this.

is it possible to give it kind of a "or"

In like:

When you get near OR when you shoot "dummy" do this;...
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 19, 2009, 01:47:47 pm
Yeah just use apponidle for distance check and onplayer damage if it gets shoot :D
Title: Re: How to activate bot on see within a certain area.
Post by: D4NG3R NL on May 19, 2009, 03:49:52 pm
IDK: I just tryed something, (Besides the two parts I didnt fill-in) is it right :)?
Could someone help me with that part?

Code: [Select]
function OnPlayerDamage(WHAT TO WRITE HERE?): AND HERE;
 
var
  i, j: byte;
  X2, Y2, DX, DY: single;
begin
  for i := 1 to 32 do
    if (GetPlayerStat(i, 'Active') = true) then
      if (GetPlayerStat(i, 'Human') = true) then begin
        for j := 1 to 32 do
          if ((i <> j) and (GetPlayerStat(j, 'Active') = true)) then
            if ((GetPlayerStat(j, 'Human') = false) and (GetPlayerStat(j, 'Name') = 'Dummy') and (GetPlayerStat(i, 'Team') <> GetPlayerStat(j, 'Team'))) then begin
              GetPlayerXY(j, X2, Y2);
                PlaceBot('Witch', GetPlayerStat(j, 'Team'), X2, Y2);
                KickPlayer(j);
              end;
            end;
      end;
end;

& on-kill (based on the taliban script from Shantec)


Code: [Select]
procedure OnPlayerKill(Killer, Victim: byte; Weapon: string);
begin
  if (IdToName(victim) = 'Witch') and (IdToName(killer) <> 'Major') then begin
WriteConsole(Killer,'You killed the Witch!',RGB(200,200,200))
Command('/Kick Witch');
  end;
end;
Title: Re: How to activate bot on see within a certain area.
Post by: y0uRd34th on May 20, 2009, 08:39:43 am
Code: [Select]
function OnPlayerDamage(Victim, Shooter: byte; Damage: integer): integer;
begin
    Result := Damage // You need that otherwise you won't able to do any damage ingame!
end;

Here you go