Official Soldat Forums
Server Talk => Scripting Discussions and Help => Topic started 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:
//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;
-
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.
-
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
using Avarax's SpawnBot
-
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
using Avarax's SpawnBot
Hmmm, that actually is a great idea...
//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
-
You have one function right after another.
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;
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 fu >n< ction OnPlayerDamage(Witch, Player: byte; Damage: integer): integer;
.
-
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)
-
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.
-
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?
-
In AppOnIdle check everyone's distance to the bot using the Distance function in the scriptcore.
-
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 // ...
-
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?
-
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.
-
well... isn't it just some powers and sqrt? Or do you mean that even that is heavy already? Oo
-
You could also have:
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)
-
It's a good idea Curt, here's how I'd implement that.
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
-
Faster and working version:
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;
-
Faster and working version:
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.... :(
-
BOTID is the id number of the bot in the game.
-
BOTID is for you to know where to put ID of the witch.
There is BOTID in two places, maybe you missed one
GetPlayerXY(BOTID, x, y);
for i := 1 to Max_Players do
if GetPlayerStat(i,'Active') then
if i <> BOTID then
-
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
-
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;
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.
-
Here use insted of 32 that:
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.
-
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)
IdHigh := MaxId();
It could possibly be even better if you got the highest bot named Dummy too for the 2nd loop
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.
-
yeah just assign it to some var in apponidle and use the var for all your loops
-
Yes But when doing this you should update the value every time a player joins or quits ;D
-
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
-
OnCommand (addbot) and ActivateServer (recompile) aswell
-
I suggest storing dummys x / y as variable, so appon loop needs to do only 32 (or less) checks..
-
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?
-
OnJoinTeam(), OnJoinGame() is not called while adding the bot.
-
Just tested and yes you're right, that's bad that it doesn't call the two procedures :/
-
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 ?)
-
yes, use OnPlayerDamage for this.
-
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;...
-
Yeah just use apponidle for distance check and onplayer damage if it gets shoot :D
-
IDK: I just tryed something, (Besides the two parts I didnt fill-in) is it right :)?
Could someone help me with that part?
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)
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;
-
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