SRL Archive - Senior Scripts and Snippets

Locations.scar


Smartzkid

2009-05-06 09:11:54

This is a 'hot-pluggable' framework for finding a character's location. User-written location detectors are 'registered' with the framework. One function call will go through all registered location detectors, returning the first to return positive. So that it doesn't get bogged down, it automatically sorts individual location detectors by speed, running the fastest ones first. Future advancements will allow for only a few 'essential' detectors (such as lumbridge center) and the detectors located in one city (or for one skill) to be turned on at once. This will further increase the speed and robustness of this framework.

Please note that this framework is still in beta and has great room for optimizations. Also realize that all location detectors must be written by users; this is no magical bean stalk that can survive without others' contributions. I'd also like to point out that once this project reaches functional maturity, it will be relatively painless to extend into a walkable lattice of location nodes.

Included:
-LocationsTestScript.scar: A script I whipped up solely to make sure everything works how it should. Messy code, I apologize.
-Locations.scar: The actual framework. Includes a generic detector function at the end. Should be placed in includes folder.

I plan on commenting the framework soon, and optimizing it afterward. Questions and comments greatly appreciated - particularly on the first 30 lines; ie the SignificanceList and the LocationInfo data structures.

Coming soon:
-Removing locators
-Enabling and disabling locators (both individually and by city, etc)
-'Significance' field - I just have to figure out a proper name for it :p
-One locator triggering other locators
-Might move the searching/sorting into a separate function (ease of optimization)

In the future:
-Some sort of general-area locator that would trigger more specific locators
-A way to relate the positions of specific locators


LocationsTestScript.scar
program New;
{.include SRL/SRL.scar}
{.include Locations.scar}

function AtGE (getInfo: boolean; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
if(getInfo) then
with info do
begin
Name := 'Grand Exchange';
LegacyName := LEGACY.GE;
City := CITIES.Varrock;
// Significance := SIGNIFICANCE.GrandExchange;
FunctionName := 'AtGE';
Members := false;
Exit;
end;

if(FindSymbol(x, y, 'bank'))then
result := true;

writeln('fast');
end;

function LumbridgeCenter (getInfo: boolean; var info: LocationInfo): Boolean;
var
WaterSymbols: TPointArray;
TempAngle: extended;
i, j, CompassAngle, dx, dy, pieces, ArrayHigh: integer;
begin
if(getInfo) then
with info do
begin
Name := 'Lumbridge Center';
City := CITIES.Lumbridge;
// Significance := SIGNIFICANCE.GrandExchange;
FunctionName := 'LumbridgeCenter';
Members := false;
Exit;
end;

result := false;

CompassAngle := round(360 - rs_GetCompassAngleDegrees);
CompassAngle := CompassAngle mod 180;

FindSymbols(WaterSymbols, 'water');
// SortTPAFrom(WaterSymbols, Point(MMCX, MMCY));
ArrayHigh := high(WaterSymbols);
if(ArrayHigh = 3) then
inc(pieces);
if(ArrayHigh = 4) then //Because quest icon often gets counted as water symbol
inc(pieces);
if(FindSymbol(dx, dy, 'quest')) then
inc(pieces);
if(ArrayHigh >= 1) then
for i := 0 to ArrayHigh do
for j := i + 1 to ArrayHigh do
begin
dx := WaterSymbols[i].x - WaterSymbols[j].x;
dy := WaterSymbols[i].y - WaterSymbols[j].y;

if(dx = 0) then
dx := 1;

TempAngle := round(90 + Degrees(ArcTan(dy/dx)));
TempAngle := TempAngle + CompassAngle;

if((abs(TempAngle - 180) < 15) or (abs(TempAngle) < 10))then
if(abs(Distance(0, 0, dx, dy) - 60) < 7) then
IncEx(pieces, 3);
end;

writeln(inttostr(pieces));
if(pieces > 4) then
result := true;

Writeln('Middle-fast');
end;

function Wait2000 (getInfo: boolean; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
if(getInfo) then
with info do
begin
Name := 'Slowest';
FunctionName := 'Wait2000';
Members := false;
Exit;
end;

wait(2000);
writeln('Slowest');
end;

function Fastest (getInfo: boolean; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
if(getInfo) then
with info do
begin
Name := 'Fastest';
FunctionName := 'Fastest';
Members := false;
Exit;
end;

writeln('Fastest');
end;


procedure RegisterLocators;
begin
registerLocator(@AtGE);
registerLocator(@LumbridgeCenter);
registerLocator(@Fastest);
registerLocator(@Wait2000);
end;

var
currentLocation: LocationInfo;

begin
SetupSRL;
RegisterLocators;
cleardebug;
// getLocation.FunctionName; //'Line 176: [Error] (16933:25): Internal error (20) in script '
currentLocation := getLocation;
writeln(currentLocation.Name);
writeln(currentLocation.City.Name);
end.


Locations.scar:
type TCity = record
Name: String;
ID: Integer;
end;

type TCityList = record
Lumbridge, Varrock, Falador, Draynor, AlKharid, Edgeville, BarbarianVillage,
Rimmington, PortSarim, Karajama: TCity;
end;


type TSignificanceList = record
Woodcutting, Fishing, Banking, Mining, Crafting, GeneralStore, SpecialStore,
TownCenter, WalkPoint, Fighting, GrandExchange: String;
end;

type TLegacyList = record
VEM, VEB, VWB, FEB, FWB, GE: String;
end;


type LocationInfo = record
City: TCity;
Name, Significance, LegacyName, FunctionName: String;
Members: Boolean;
end;

type LocatorFunction = Function(getInfo: boolean; var info: LocationInfo): Boolean;

var
LocationFinders: Array of LocatorFunction;
LocatorInformation: Array of LocationInfo;
Benchmarks: Array of Integer;
SpeedList: Array of Integer;
CITIES: TCityList;
SIGNIFICANCE: TSignificanceList;
LEGACY: TLegacyList;
LocationsInitialized: Boolean;

procedure SetupLocations;
begin
if(LocationsInitialized)then
Exit;

with CITIES do
begin
AlKharid.Name := 'Al Kharid';
AlKharid.ID := 1;

BarbarianVillage.Name := 'Barbarian Village';
BarbarianVillage.ID := 2;

Draynor.Name := 'Draynor';
Draynor.ID := 3;

Edgeville.Name := 'Edgeville';
Edgeville.ID := 4;

Falador.Name := 'Falador';
Falador.ID := 5;

Karajama.Name := 'Karajama';
Karajama.ID := 6;

Lumbridge.Name := 'Lumbridge';
Lumbridge.ID := 7;

PortSarim.Name := 'Port Sarim';
PortSarim.ID := 8;

Rimmington.Name := 'Rimmington';
Rimmington.ID := 9;

Varrock.Name := 'Varrock';
Varrock.ID := 10;
end;

with SIGNIFICANCE do
begin
Banking := 'Banking';
Crafting := 'Crafting';
Fighting := 'Fighting';
Fishing := 'Fishing';
GeneralStore := 'General Store';
GrandExchange := 'Grand Exchange';
Mining := 'Mining';
SpecialStore := 'Special Store';
TownCenter := 'Town Center';
Walkpoint := 'Walkpoint';
Woodcutting := 'Woodcutting';
end;

with LEGACY do
begin
FEB := 'FEB';
FWB := 'FWB';
GE := 'GE';
VEB := 'VEB';
VEM := 'VEM';
VWB := 'VWB';
end;

LocationsInitialized := true;
end;

function getLocation: LocationInfo;
var
i, j, k, bench: integer;
found: boolean;
begin
found := false;
for i := 0 to High(LocationFinders) do
for j := 0 to high(benchmarks) do
if(Benchmarks[SpeedList[i]] < Benchmarks[SpeedList[j]])then
begin
bench := SpeedList[i];

for k := i to high(benchmarks) - 1 do
SpeedList[k] := SpeedList[k + 1];

for k := high(benchmarks) downto j + 1 do
SpeedList[k] := SpeedList[k - 1];

SpeedList[k] := bench;

break;
end;

for i := 0 to High(LocationFinders) do
begin
bench := GetSystemTime;

if(LocationFinders[SpeedList[i]](false, result) = true)then
begin
found := true;
break;
end;

Benchmarks[SpeedList[i]] := GetSystemTime - bench;
end;
if(found) then
result := LocatorInformation[SpeedList[i]];
end;

procedure registerLocator(Locator: LocatorFunction);
var
LocatorInfo: LocationInfo;
ArrayLength: Integer;
begin
if(not(LocationsInitialized)) then
SetupLocations;
Locator(true, LocatorInfo);
ArrayLength := Length(LocationFinders);
SetLength(LocationFinders, ArrayLength + 1);
SetLength(LocatorInformation, ArrayLength + 1);
SetLength(Benchmarks, ArrayLength + 1);
SetLength(SpeedList, ArrayLength + 1);
LocationFinders[ArrayLength] := Locator;
LocatorInformation[ArrayLength] := LocatorInfo;
Benchmarks[ArrayLength] := -1;
SpeedList[ArrayLength] := ArrayLength;
end;

{function Generic (getInfo: boolean; var info: LocationInfo): Boolean;
//Locator template
begin
if(getInfo) then
with info do
begin
Name := '';
LegacyName := LEGACY.; //This field is for the 'traditional'/abbreviated name of a location
City := CITIES.; //City or nearest city
Significance := SIGNIFICANCE.; //What would one want to do at this location?
FunctionName := 'Generic'; //Name of this function
Members := false; //Is this location in a members-only area?
end;

//Location detection goes here
result := true;
end;}

Smartzkid

2009-05-06 09:19:09

Version 2:

TestScript.scar
program TestScript;
{.include SRL/SRL.scar}
{.include Locations.scar}

function AtGE (calltype: integer; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
case calltype of
DM_REGISTER:
begin
with info do
begin
Name := 'Grand Exchange';
LegacyName := LEGACY.GE;
City := CITIES.Varrock;
// Significance := SIGNIFICANCE.GrandExchange;
FunctionName := 'AtGE';
Members := false;
Exit;
end;
end;

DM_MINIMAP:
begin
if(FindSymbol(x, y, 'bank'))then
result := true;
end;

DM_WORLDMAP:
begin
end;
end;

wait(50);
writeln('fast');
end;

function LumbridgeCenter (calltype: integer; var info: LocationInfo): Boolean;
var
WaterSymbols: TPointArray;
TempAngle: extended;
i, j, CompassAngle, dx, dy, pieces, ArrayHigh: integer;
begin
case calltype of
DM_REGISTER:
begin
with info do
begin
Name := 'Lumbridge Center';
City := CITIES.Lumbridge;
// Significance := SIGNIFICANCE.GrandExchange;
FunctionName := 'LumbridgeCenter';
Members := false;
Exit;
end;
end;

DM_MINIMAP:
begin

result := false;

CompassAngle := round(360 - rs_GetCompassAngleDegrees);
CompassAngle := CompassAngle mod 180;

FindSymbols(WaterSymbols, 'water');
// SortTPAFrom(WaterSymbols, Point(MMCX, MMCY));
ArrayHigh := high(WaterSymbols);
if(ArrayHigh = 3) then
inc(pieces);
if(ArrayHigh = 4) then //Because quest icon often gets counted as water symbol
inc(pieces);
if(FindSymbol(dx, dy, 'quest')) then
inc(pieces);
if(ArrayHigh >= 1) then
for i := 0 to ArrayHigh do
for j := i + 1 to ArrayHigh do
begin
dx := WaterSymbols[i].x - WaterSymbols[j].x;
dy := WaterSymbols[i].y - WaterSymbols[j].y;

if(dx = 0) then
dx := 1;

TempAngle := round(90 + Degrees(ArcTan(dy/dx)));
TempAngle := TempAngle + CompassAngle;

if((abs(TempAngle - 180) < 15) or (abs(TempAngle) < 10))then
if(abs(Distance(0, 0, dx, dy) - 60) < 7) then
IncEx(pieces, 3);
end;

writeln(inttostr(pieces));
if(pieces > 4) then
result := true;
end;

DM_WORLDMAP:
begin
end;
end;

wait(300);
Writeln('Middle-fast');
end;

function Wait500 (calltype: integer; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
if(calltype = DM_REGISTER) then
with info do
begin
Name := 'Slowest';
FunctionName := 'Wait500';
Members := false;
Exit;
end;

wait(500);
writeln('Slowest');
end;

function Fastest (calltype: integer; var info: LocationInfo): Boolean;
var
x, y: integer;
begin
case calltype of
DM_REGISTER:
with info do
begin
Name := 'Fastest';
FunctionName := 'Fastest';
Members := false;
Exit;
end;

DM_MINIMAP:
begin
end;

DM_WORLDMAP:
begin
end;
end;

writeln('Fastest');
end;


procedure RegisterLocators;
begin
registerLocator(@AtGE);
registerLocator(@LumbridgeCenter);
registerLocator(@Fastest);
registerLocator(@Wait500);
end;

var
currentLocation: LocationInfo;

begin
SetupSRL;
RegisterLocators;
cleardebug;
// getLocation.FunctionName; //'Line 176: [Error] (16933:25): Internal error (20) in script '
currentLocation := getLocation;
writeln(currentLocation.Name);
writeln(currentLocation.City.Name);
getLocation;
end.

Locations.scar
const
DM_REGISTER = $00;
DM_MINIMAP = $01;
DM_WORLDMAP = $02;

type TCity = record
Name: String;
ID: Integer;
end;

type TCityList = record
Lumbridge, Varrock, Falador, Draynor, AlKharid, Edgeville, BarbarianVillage,
Rimmington, PortSarim, Karajama: TCity;
end;


//TODO: rename SIGNIFICANCE
type TSignificanceList = record
Woodcutting, Fishing, Banking, Mining, Crafting, GeneralStore, SpecialStore,
TownCenter, WalkPoint, Fighting, GrandExchange: String;
end;


type TLegacyList = record
VEM, VEB, VWB, FEB, FWB, GE: String;
end;


type LocationInfo = record
City: TCity; //TODO: rename SIGNIFICANCE
Name, Significance, LegacyName, FunctionName: String;
Members: Boolean;
end;

type LocatorFunction = Function(calltype: integer; var info: LocationInfo): Boolean;

var
LocationFinders: Array of LocatorFunction;
LocatorInformation: Array of LocationInfo;
Benchmarks: Array of Integer;
SpeedOrder: Array of Byte;
CITIES: TCityList;
SIGNIFICANCE: TSignificanceList;
LEGACY: TLegacyList;
LocationsInitialized: Boolean;

procedure SetupLocations;
begin
if(LocationsInitialized)then
Exit;

with CITIES do
begin
AlKharid.Name := 'Al Kharid';
AlKharid.ID := 1;

BarbarianVillage.Name := 'Barbarian Village';
BarbarianVillage.ID := 2;

Draynor.Name := 'Draynor';
Draynor.ID := 3;

Edgeville.Name := 'Edgeville';
Edgeville.ID := 4;

Falador.Name := 'Falador';
Falador.ID := 5;

Karajama.Name := 'Karajama';
Karajama.ID := 6;

Lumbridge.Name := 'Lumbridge';
Lumbridge.ID := 7;

PortSarim.Name := 'Port Sarim';
PortSarim.ID := 8;

Rimmington.Name := 'Rimmington';
Rimmington.ID := 9;

Varrock.Name := 'Varrock';
Varrock.ID := 10;
end;

with SIGNIFICANCE do
begin
Banking := 'Banking';
Crafting := 'Crafting';
Fighting := 'Fighting';
Fishing := 'Fishing';
GeneralStore := 'General Store';
GrandExchange := 'Grand Exchange';
Mining := 'Mining';
SpecialStore := 'Special Store';
TownCenter := 'Town Center';
Walkpoint := 'Walkpoint';
Woodcutting := 'Woodcutting';
end;

with LEGACY do
begin
FEB := 'FEB';
FWB := 'FWB';
GE := 'GE';
VEB := 'VEB';
VEM := 'VEM';
VWB := 'VWB';
end;

LocationsInitialized := true;
end;

function getLocation: LocationInfo;
var
i, j, k, bench: integer;
found: boolean;
begin
found := false;

//TODO: Speed up search/sort time [split to separate functions?]
for i := 0 to High(LocationFinders) do
for j := 0 to high(benchmarks) do
if(Benchmarks[SpeedOrder[i]] < Benchmarks[SpeedOrder[j]])then
begin
bench := SpeedOrder[i];

for k := i to high(benchmarks) - 1 do
SpeedOrder[k] := SpeedOrder[k + 1];

for k := high(benchmarks) downto j + 1 do
SpeedOrder[k] := SpeedOrder[k - 1];

SpeedOrder[k] := bench;

break;
end;

for i := 0 to High(LocationFinders) do
begin
bench := GetSystemTime;

if(LocationFinders[SpeedOrder[i]](DM_MINIMAP, result) = true)then
begin
found := true;
break;
end;

Benchmarks[SpeedOrder[i]] := GetSystemTime - bench;
end;

if(found) then
result := LocatorInformation[SpeedOrder[i]];
//else
//TODO: Implement world map fallback
end;

procedure registerLocator(Locator: LocatorFunction);
var
LocatorInfo: LocationInfo;
LocatorsLength: Integer;
begin
if(not(LocationsInitialized)) then
SetupLocations;
Locator(DM_REGISTER, LocatorInfo);

LocatorsLength := Length(LocationFinders);
SetLength(LocationFinders, LocatorsLength + 1);
SetLength(LocatorInformation, LocatorsLength + 1);
SetLength(Benchmarks, LocatorsLength + 1);
SetLength(SpeedOrder, LocatorsLength + 1);
Inc(LocatorsLength);

LocationFinders[LocatorsLength - 1] := Locator;
LocatorInformation[LocatorsLength - 1] := LocatorInfo;
Benchmarks[LocatorsLength - 1] := -1;
SpeedOrder[LocatorsLength - 1] := LocatorsLength - 1;
end;

//TODO: Add enabling/disabling of locators
//TODO: Add global 'current city' variable & search locators from that city first

{function Generic (calltype: integer; var info: LocationInfo): Boolean;
//Locator template
begin
case calltype of
DM_REGISTER:
begin
with info do
begin
Name := '';
LegacyName := LEGACY.; //This field is for the 'traditional'/abbreviated name of a location
City := CITIES.; //City or nearest city
// Significance := SIGNIFICANCE.GeneralStore; //What would one want to do at this location?
FunctionName := 'Generic'; //Name of this function
Members := false; //Is this location in a members-only area?
end;
end;

DM_MINIMAP:
begin
//Location detection goes here
result := true;
end;

DM_WORLDMAP:
begin
//Large-size map detection (usually a single DDTM) goes here
result := true;
end;
end;
end;}

nielsie95

2009-05-06 20:33:27

Nice, but how could this be used in a script?

Jason2gs

2009-05-06 20:38:29

I'm thinking you picked kinda the worst possible time to release this.

Smartzkid

2009-05-06 23:02:12

From: nielsie95
Nice, but how could this be used in a script?


Well, any location detection. Walking, teleporting, or simply figuring out where a character is when the script starts.

From: Jason2gs
I'm thinking you picked kinda the worst possible time to release this.


Why's that?

n3ss3s

2009-05-07 16:00:44

From: Smartzkid
Well, any location detection. Walking, teleporting, or simply figuring out where a character is when the script starts.



Why's that?


Because some guy has access to SSRL, read the "This is bullshit!" topic…

Smartzkid

2009-05-07 23:19:32

I don't mind. haha this code is useless without detectors, and I doubt he wants to write a whole bunch of them just to use it.

I plan on writing a script using this, to demonstrate its use and all that. Should give a good base of locators to use as well.