SRL Archive - Senior Scripts and Snippets

[WiP][v2] NPC Finder


Markus

2008-02-18 14:26:25

Decided to release this, it's not finished yet, but it provides a few lower-level functions you might wish to use :)

Current functions:
  • function GetNPCCount : integer;
  • function GetNameFromObject(npcob : integer) : string;
  • function GetNPCAction(npc : TNPC; ActionNo : integer) : string;
  • function GetAllNPCs : array of TNPC;
  • function FindAllNPCSByName(name : string) : array of TNPC;

I will add more later.

You will need the type TNPC:

type TNPC = record
x, y, height, NPCId : integer;
name : string;
end;

This addition to your fields:

CharHeight = 'V';

//NPC Fields
NPCCount = 'ic.U';
NPCIndexarray = 'hc.e';
NPCArray = 'vh.c';
NPCName = 'zb';
NPCDef = 'Kc';
NPCActions = 'Y';
And the functions:


function GetNPCCount : integer;
begin
result := SmartGetFieldInt(0, NPCCount);
end;

function GetNameFromObject(npcob : integer) : string;
var
bytes: Integer;
begin
bytes:= SmartGetFieldObject(npcob, NPCDef+'.'+NPCName+'.'+StringBytes);
result:= StringFromBytes(bytes);
SmartFreeObject(bytes);
end;

function GetNPCAction(npc : TNPC; ActionNo : integer) : string;
var
c, b, CurNPC : integer;
S : string;
begin
CurNPC := SmartGetFieldArrayObject(0, NPCArray, npc.NPCId);
if (ActionNo > SmartGetFieldArraySize(CurNPC, NPCDef+'.'+NPCActions)) then
exit;
C := SmartGetFieldArrayObject(CurNPC, NPCDef+'.'+NPCActions, ActionNo);
B := SmartGetFieldObject(C, StringBytes);
S := StringFromBytes(B);
result := Trim(s);
SmartFreeObject(CurNPC);
SmartFreeObject(c);
SmartFreeObject(b);
end;

function GetAllNPCs : array of TNPC;
var
i, CurNPC, npcc : integer;
begin
SetArrayLength(result, GetNPCCount);
writeln('amount of NPCs: '+inttostr(length(result)));
npcc := GetNPCCount-1;
For i := 0 to npcc do
begin
Result[i].NPCId := SmartGetFieldArrayInt(0, NPCIndexarray, i);
CurNPC := SmartGetFieldArrayObject(0, NPCArray, result[i].NPCId);
Result[i].name := trim(GetNameFromObject(CurNPC));
Result[i].x := SmartGetFieldInt(0, BaseX)+SmartGetFieldInt(CurNPC, CurrentX) shr 7;
Result[i].y := SmartGetFieldInt(0, BaseY)+SmartGetFieldInt(CurNPC, CurrentY) shr 7;
Result[i].Height := SmartGetFieldInt(CurNPC, NPCDef+'.'+CharHeight); //I doubt this one is right.. Gives weird results =/
SmartFreeObject(CurNPC);
end;
end;

function FindAllNPCSByName(name : string) : array of TNPC;
var
i, j : integer;
TempNPC : array of TNPC;
begin
TempNPC := GetAllNPCs;
name := lowercase(name);
for i := 0 to high(TempNPC) do
begin
if lowercase(TempNPC[i].name) = name then
begin
inc(j);
SetLength(result, j);
result[j-1] := TempNPC[i];
end;
end;
end;

Small testing script:

program NPCCrap;
//RS Build = #487
const
//Global Static Hooks
SettingsArray = 'lh.a'; //int[]
CurrentLevels = 'qg.Z'; //int[]
MaxLevels = 'nd.i'; //int[]
Experiances = 'sb.f'; //int[]
BaseX = 'sb.j'; //int
BaseY = 'gg.R'; //int
MyPlayer = 'ha.p'; //Player
CompassAngle = 'ja.'; //int (MinimapInt1)
MapOffsetAngle = 'aa.ob'; //int (MinimapInt2)
BaseLayer = 'se.b'; //int (plane/terrainInt)
GroundSettings = 'di.tb'; //byte[][][] (groundByteArray)
GroundHeights = 'wa.b'; //int[][][] (groundIntArray)
CamX = 'ce.tb'; //int
CamY = 'ag.h'; //int
CamZ = 'f.L'; //int
CamYaw = 'fd.j'; //int (CamCurveY)
CamPitch = 'm.e'; //int (CamCurveX)

//Character Hooks
CurrentX = 'hc'; //int
CurrentY = 'tc'; //int
CharAnim = 'fb'; //int
CharMotion = 'Rb'; //int
CharName = 'yc'; //StringWrap
CharHeight = 'V';

//StringWrap Hooks
StringBytes = 'y'; //byte[]

//NPC Hooks
NPCCount = 'ic.U';
NPCIndexarray = 'hc.e';
NPCArray = 'vh.c';
NPCName = 'zb';
NPCDef = 'Kc';
NPCActions = 'Y';

//Converts a byte[] from StringWrap to a DelphiString (max length 256)
function StringFromBytes(jbytearray: integer): string;
begin
SetLength(result, 256);
SetLength(result, SmartStringFromBytes(jbytearray,result));
end;

type TNPC = record
x, y, height, NPCId : integer;
name : string;
end;

function GetNPCCount : integer;
begin
result := SmartGetFieldInt(0, NPCCount);
end;

function GetNameFromObject(npcob : integer) : string;
var
bytes: Integer;
begin
bytes:= SmartGetFieldObject(npcob, NPCDef+'.'+NPCName+'.'+StringBytes);
result:= StringFromBytes(bytes);
SmartFreeObject(bytes);
end;

function GetNPCAction(npc : TNPC; ActionNo : integer) : string;
var
c, b, CurNPC : integer;
S : string;
begin
CurNPC := SmartGetFieldArrayObject(0, NPCArray, npc.NPCId);
if (ActionNo > SmartGetFieldArraySize(CurNPC, NPCDef+'.'+NPCActions)) then
exit;
C := SmartGetFieldArrayObject(CurNPC, NPCDef+'.'+NPCActions, ActionNo);
B := SmartGetFieldObject(C, StringBytes);
S := StringFromBytes(B);
result := Trim(s);
SmartFreeObject(CurNPC);
SmartFreeObject(c);
SmartFreeObject(b);
end;

function GetAllNPCs : array of TNPC;
var
i, CurNPC, npcc : integer;
begin
SetArrayLength(result, GetNPCCount);
writeln('amount of NPCs: '+inttostr(length(result)));
npcc := GetNPCCount-1;
For i := 0 to npcc do
begin
Result[i].NPCId := SmartGetFieldArrayInt(0, NPCIndexarray, i);
CurNPC := SmartGetFieldArrayObject(0, NPCArray, result[i].NPCId);
Result[i].name := trim(GetNameFromObject(CurNPC));
Result[i].x := SmartGetFieldInt(0, BaseX)+SmartGetFieldInt(CurNPC, CurrentX) shr 7;
Result[i].y := SmartGetFieldInt(0, BaseY)+SmartGetFieldInt(CurNPC, CurrentY) shr 7;
Result[i].Height := SmartGetFieldInt(CurNPC, NPCDef+'.'+CharHeight); //I doubt this one is right.. Gives weird results =/
SmartFreeObject(CurNPC);
end;
end;
function TrimTilSpace(s : string; l : integer) : string;
var i : integer;
begin
result := s;
for i := Length(s) to l do
result := result + ' ';
end;
var i : integer;
npcs : array of TNPC;
begin
SmartSetup('nl3', false, true);
npcs := GetAllNPCs;
for i := 0 to high(npcs) do
begin
writeln(trimtilspace(npcs[i].name, 18)+':'+' X: '+inttostr(npcs[i].x)+' Y: '+inttostr(npcs[i].x) + ' ID: '+inttostr(npcs[i].npcid)+' ACTION: '+ GetNPCAction(npcs[i], 0));
end;
end.

Bobarkinator

2008-02-18 14:31:11

Good job, I am working on an object finder hope to have it done soon.

bullzeye95

2008-02-18 14:32:34

Confusing =/

Good job :D
Will be useful!

nielsie95

2008-02-18 14:32:57

Nice work! :)

Smartzkid

2008-02-18 15:15:19

Two questions:

TNPC - maybe it should have an array of string for Actions?

Height - anyone have any idea what it is/how to use it? Everything I try comes out with -1; Plus, putting higher numbers into TileToWorld doesn't exactly do what one might expect…

Markus

2008-02-18 15:28:40

I tried adding an array for actions, but it lagged way too much! It was like 5000 times slower without, so I made a seperate function for it.

Also made FindAllNPCSByName, will add it to first post :)

Smartzkid

2008-02-18 17:49:29

const
NPCArray = 'vh.c';
NPCIndexArray = 'hc.e';
NPCCount = 'ic.U';
NPCDef = 'Kc';
NPCName = 'zb';
NPCX = 'hc';
NPCY = 'tc';

function FindNPCByName(var x, y: integer; Name: string; CaseSensitive, AllowPartialMatch: Boolean; height: integer): boolean;
var
NPCAmt, curNPCDef, nameBytes, i: Integer;
curNPCName: String;
begin
result := false;
if(not(CaseSensitive))then
Name := Lowercase(Name);
NPCAmt := SmartGetFieldInt(0, npccount)
for i := 0 to NPCAmt - 1 do
begin
curNPCDef := SmartGetFieldArrayObject(0, npcArray, SmartGetFieldArrayInt(0, npcIndexArray, i));
nameBytes := SmartGetFieldObject(curNPCDef, npcdef + '.' + npcName + '.' + StringBytes);
SetLength(CurNPCName, 256);
SetLength(CurNPCName, SmartStringFromBytes(nameBytes, CurNPCName));
SmartFreeObject(nameBytes);
if(not(CaseSensitive))then
curNPCName := LowerCase(curNPCName);
if(((Pos(Name, curNPCName) <> 0) and AllowPartialMatch) or (curNPCName = Name))then
begin
TileToWorld(SmartGetFieldInt(0,BaseX)+SmartGetFieldInt(curNPCDef, npcX) shr 7,SmartGetFieldInt(0,BaseY)+SmartGetFieldInt(curNPCDef, npcY) shr 7, Height, x, y);
SmartFreeObject(curNPCDef);
if (x > MSX1) and (x < MSX2) and (y > MSY1) and (y < MSY2) then
begin
result := true;
Exit;
end;
end;
end;
end;

Self contained (almost; requires TileToWorld and above fields) NPC finder for those who want one. Markus, I can make a new thread for this if you want, posted here to try not to confuse people.

Ruroken

2008-02-19 20:31:27

A working objectfinder would rock.

Not to say that the NPC stuff dosen't :p

Smartzkid

2008-02-19 21:13:33

Heh, tell Ben to tell me how to do it and I will; he wouldn't tell me last we talked, said it would take too long or something along those lines… :p

Ruroken

2008-02-19 22:40:04

I believe it was Ben himself who once said "Just because its impossible dosen't mean that it cant be done." or something along those lines :p

For this just sub in "hard" for "impossible" <_<

Bobarkinator

2008-02-19 23:04:23

From: Smartzkid
Heh, tell Ben to tell me how to do it and I will; he wouldn't tell me last we talked, said it would take too long or something along those lines… :p


Hes right lol, the Type/UID array is a 3D array of object, but ben forgot to put 3DObject Arrays in SMART so you have to do some special stuff to just get that.

Markus

2008-02-20 02:15:01

How about three times SmartGetFieldArrayObject?

Bobarkinator

2008-02-20 08:09:34

Yep thats what I did,

Function GetUID(TileX,TileY: Integer): Integer;
Var
Index1,Index2,Index3,i,UID:Integer;
tmpArray: Array of Integer;
Begin
For i:= 0 To 3 Do
Begin
Index1 := SmartGetFieldArrayObject(0,'ud.xb',i);
Index2 := SmartGetFieldArrayObject(Index1,'',TileX-SmartGetFieldInt(0,BaseX));
Index3 := SmartGetFieldArrayObject(Index2,'',TileY-SmartGetFieldInt(0,BaseY));
If (Index3 <> 0) Then
Begin
UID:= SmartGetFieldObject(Index3,'O')//Object1
If (UID <> 0) Then
Begin
Result:= SmartGetFieldInt(UID,'j');
SmartFreeObject(UID);
SmartFreeObject(Index1);
SmartFreeObject(Index2);
SmartFreeObject(Index3);
Exit;
End;
UID:= SmartGetFieldObject(Index3,'E');//Object2
If (UID <> 0) Then
Begin
Result:= SmartGetFieldInt(UID,'l');
SmartFreeObject(UID);
SmartFreeObject(Index1);
SmartFreeObject(Index2);
SmartFreeObject(Index3);
Exit;
End;
UID:= SmartGetFieldObject(Index3,'L');//Object3
If (UID <> 0) Then
Begin
Result:= SmartGetFieldInt(UID,'b');
SmartFreeObject(UID);
SmartFreeObject(Index1);
SmartFreeObject(Index2);
SmartFreeObject(Index3);
Exit;
End;
UID:= SmartGetFieldObject(Index3,'B');//Object4
If(UID <> 0) Then
Begin
Result:= SmartGetFieldInt(UID,'l');
SmartFreeObject(UID);
SmartFreeObject(Index1);
SmartFreeObject(Index2);
SmartFreeObject(Index3);
Exit;
End;
tmpArray:= SmartGetFieldObject(Index3,'H');//Object5array
For i:= 0 To SmartGetFieldArraySize(tmpArray,'') -1 Do
Begin
UID:= SmartGetFieldArrayObject(tmpArray,'',i);//Object5
If(UID <> 0) Then
Begin
Result:= SmartGetFieldInt(UID,'a');
SmartFreeObject(UID);
SmartFreeObject(Index1);
SmartFreeObject(Index2);
SmartFreeObject(Index3);
SmartFreeObject(tmpArray);
Exit;
End;
End;
End;
End;

Doesn't compile right now. And that is all "in theory" I have no idea if it will work right when I test it.

Ruroken

2008-02-20 15:45:24

Well thats half the fun isn't it? Seeing if your code will compile. :p