View Poll Results: 36-RFC and Blood Wraith model

Voters
32. This poll is closed
  • Blood wraith does not get identified as blood wraith

    7 21.88%
  • Blood wraith is identified correctly but other items match "blood wraith"

    4 12.50%
  • Blood wraith is identified correctly with no false hits but is slow

    12 37.50%
  • Blood wraith is identified correctly and has no false matches and is very fast

    9 28.13%
Page 52 of 57 FirstFirst ... 2425051525354 ... LastLast
Results 1,276 to 1,300 of 1421

Thread: Preemptible RuneSpan

  1. #1276
    Join Date
    Feb 2007
    Location
    PA, USA
    Posts
    5,240
    Mentioned
    36 Post(s)
    Quoted
    496 Post(s)

    Default

    Quote Originally Posted by usingthisname View Post
    Yes, now i farmed 5.9k essence myself, but still it doesn't work.
    well this script is quite outdated...

  2. #1277
    Join Date
    Jul 2012
    Location
    Dänemark
    Posts
    14
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by footballjds View Post
    well this script is quite outdated...
    Maybe you could paste your script to pastebin, then I'll copy it and see if it works?

  3. #1278
    Join Date
    Feb 2007
    Location
    PA, USA
    Posts
    5,240
    Mentioned
    36 Post(s)
    Quoted
    496 Post(s)

    Default

    Quote Originally Posted by usingthisname View Post
    Maybe you could paste your script to pastebin, then I'll copy it and see if it works?
    maybe i could, but then again maybe i don't have the time to help a leech.

  4. #1279
    Join Date
    Jul 2012
    Location
    Dänemark
    Posts
    14
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by footballjds View Post
    maybe i could, but then again maybe i don't have the time to help a leech.
    But you spent all night replying to this thread. And it will take about 15 seconds pasting it to pastebin.

  5. #1280
    Join Date
    Dec 2006
    Location
    UK!!
    Posts
    910
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by usingthisname View Post
    But you spent all night replying to this thread. And it will take about 15 seconds pasting it to pastebin.
    This attitude is what drags, not only this community but, society down.

  6. #1281
    Join Date
    Nov 2011
    Posts
    1,589
    Mentioned
    9 Post(s)
    Quoted
    17 Post(s)

    Default

    Lechers gonna Leech.
    We can't do anything about it but, not help them. Unless they are willing to have a go and coding them selves.
    Mat



    ^^

  7. #1282
    Join Date
    Jul 2012
    Location
    Dänemark
    Posts
    14
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by [S]paz View Post
    This attitude is what drags, not only this community but, society down.
    I can just paste my script then you can copy it and see if you get the same results as me then.

    My script

    Also i don't want to be a leech, but i just don't have time to learn coding.

  8. #1283
    Join Date
    Mar 2012
    Location
    Runescape, G.E
    Posts
    137
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    looks good going to test it out now.

  9. #1284
    Join Date
    Jan 2012
    Posts
    8
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    i got a error



    [Error] C:\Simba\Includes\SRL/SRL/misc/paintsmart.simba(42:33): Unknown identifier 'SmartGetDebugDC' at line 41
    Compiling failed.

  10. #1285
    Join Date
    Mar 2012
    Location
    Runescape, G.E
    Posts
    137
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by SkateboarderT3 View Post
    i got a error



    [Error] C:\Simba\Includes\SRL/SRL/misc/paintsmart.simba(42:33): Unknown identifier 'SmartGetDebugDC' at line 41
    Compiling failed.
    its working fine for me,

  11. #1286
    Join Date
    Mar 2012
    Location
    Canada
    Posts
    442
    Mentioned
    4 Post(s)
    Quoted
    67 Post(s)

    Default

    Hey Guys because I used this script a while back and loved it, I have brought it back from the dead yes i fixed it (the code will be in 2 posts because it wont all fit) enjoy

    Code:
    program PreemptibleRuneSpan;
    {$DEFINE SMART}
    {$include_once SRL/SRL.simba}
    {$include_once SRL/SRL/Misc/Debug.simba}
    {$include_once SRL/SRL/core/animation.simba}
    {$include_once SRL/SRL/skill/magic.simba}
    const SRLStats_User = 'Anonymous'; // Your SRL Stats ID (If you dont have one then just leave it as it is)
    const SRLStats_Password = 'anon1337'; // Your SRL Stats Password (If you dont have one then just leave it as it is)
    
    
    const CAN_USE_FAIRY_RINGS = False;
    const ONSCREEN_STATS = False; // set to false if it bothers you, or click Disable Debug button
    const ONSCREEN_FOLLOW_MODELS = True; // set to false if you don't want to see models being followed
    const ONSCREEN_TRACK_BOUNDARY = True; // turn off if you don't care to see boundary cube-like map projection points
    
    
    const CLICKBAN_ENABLED = True; // essentially if we click on something and nothing happens, we shouldn't click on it again
    
    const TAKES_BREAKS  = True; // do we take breaks
    const BREAK_TIME = 190; // number of minutes before breaks, midpoint
    const BREAK_TIME_SPREAD = 10; // means +/- 10 minutes
    const BREAK_DURATION = 8; // break time is always +/- 25% just because we don't need more input
    const SWITCH_WORLDS_BREAK = True; // switch worlds after a break
    const KEEP_DEBUG = False; // if you set it to true, the little window will not clear on each stat report
    
    const SMART_SERVER_TO_USE = 63; // so that you can change it quickly if your
    // cache is messed up and you get "no applet" message
    
    //////////////////////////////////////
    // SERIOUSLY only one supported atm //
    //////////////////////////////////////
    
    procedure DeclarePlayers;
    begin
      HowManyPlayers := 1;
      NumberOfPlayers(HowManyPlayers);
      CurrentPlayer  := 0;
    
      with Players[0] do
      begin
        Name := '';
        Pass := '';
        Pin := ''; // right now banking for hood not supported, might be in future
                   // so you don't need to fill it just yet
        BoxRewards := ['XP', 'Lamp', 'Book', 'mote', 'ostume', 'ssence'];
        LampSkill := SKILL_RUNECRAFTING;
        Member := True;
        Active := True;
      end;
    end;
    
    
    
    
    type TModelTarget = (TARGET_UNDEAD_SOUL, TARGET_LIVING_SOUL,
      TARGET_BLOODY_SKULLS, TARGET_BLOOD_POOL, TARGET_SKULLS, TARGET_JUMPER,
      TARGET_SHIFTER, TARGET_NEBULA, TARGET_CHAOTIC_CLOUD, TARGET_FIRE_STORM,
      TARGET_FLESHY_GROWTH, TARGET_VINE, TARGET_FIREBALL, TARGET_ROCK_FRAGMENT,
      TARGET_WATER_POOL, TARGET_MIND_STORM, TARGET_CYCLONE, TARGET_SOUL_ESSWRAITH,
      TARGET_BLOOD_ESSWRAITH, TARGET_DEATH_ESSWRAITH, TARGET_LAW_ESSHOUND,
      TARGET_NATURE_ESSHOUND, TARGET_ASTRAL_ESSHOUND, TARGET_CHAOS_ESSHOUND,
      TARGET_COSMIC_ESSHOUND, TARGET_BODY_ESSHOUND, TARGET_FIRE_ESSLING,
      TARGET_EARTH_ESSLING, TARGET_WATER_ESSLING, TARGET_MIND_ESSLING,
      TARGET_AIR_ESSLING,
    
      TARGET_GENERIC_ESSHOUND,
    
      TARGET_NONE);
    
    
    var USER_SELECTED_ORDER : array of TModelTarget;
    procedure InitializeUserSelection;
    begin
    
      USER_SELECTED_ORDER := [];
      (* Example of order:
      [ TARGET_UNDEAD_SOUL, TARGET_LIVING_SOUL, TARGET_BLOODY_SKULLS,
        TARGET_BLOOD_POOL, TARGET_SKULLS, TARGET_JUMPER,
        TARGET_SHIFTER, TARGET_NEBULA, TARGET_CHAOTIC_CLOUD,
        TARGET_SOUL_ESSWRAITH, TARGET_BLOOD_ESSWRAITH, TARGET_DEATH_ESSWRAITH,
        TARGET_LAW_ESSHOUND, TARGET_FLESHY_GROWTH, TARGET_NATURE_ESSHOUND,
        TARGET_ASTRAL_ESSHOUND, TARGET_FIREBALL, TARGET_VINE,
        TARGET_FIRE_STORM, TARGET_CHAOS_ESSHOUND, TARGET_COSMIC_ESSHOUND,
        TARGET_ROCK_FRAGMENT, TARGET_WATER_POOL, TARGET_BODY_ESSHOUND,
        TARGET_GENERIC_ESSHOUND, TARGET_MIND_STORM, TARGET_CYCLONE,
        TARGET_FIRE_ESSLING, TARGET_EARTH_ESSLING, TARGET_WATER_ESSLING,
        TARGET_MIND_ESSLING, TARGET_AIR_ESSLING];
      *)
    end;
    
    
    const USER_IS_ADD = False;
    
    const SMART_THIRD_LEVEL = True;
    
    
    const ONSCREEN_TRACK_SECONDARY = False;
    
    const TEST_MODELS = False;
    
    const OMIT_MODEL_NAMES = False;
    
    const LOG_LONG_SEARCHES = False;
    
    const CLICKBAN_MAX_AREAS = 60;
    
    const VERSION = '4.0.43';
    
    var DEFAULT_TARGET_ORDER : array of TModelTarget;
    var SELECTED_TARGET_ORDER : array of TModelTarget;
    procedure InitializeTargetOrder;
    begin
      DEFAULT_TARGET_ORDER :=
      [ TARGET_UNDEAD_SOUL, TARGET_LIVING_SOUL, TARGET_BLOODY_SKULLS,
        TARGET_BLOOD_POOL, TARGET_SKULLS, TARGET_JUMPER,
        TARGET_SHIFTER, TARGET_NEBULA, TARGET_CHAOTIC_CLOUD,
        TARGET_SOUL_ESSWRAITH, TARGET_BLOOD_ESSWRAITH, TARGET_DEATH_ESSWRAITH,
        TARGET_LAW_ESSHOUND, TARGET_FLESHY_GROWTH, TARGET_NATURE_ESSHOUND,
        TARGET_ASTRAL_ESSHOUND, TARGET_FIREBALL, TARGET_VINE,
        TARGET_FIRE_STORM, TARGET_CHAOS_ESSHOUND, TARGET_COSMIC_ESSHOUND,
        TARGET_ROCK_FRAGMENT, TARGET_WATER_POOL, TARGET_BODY_ESSHOUND,
        TARGET_GENERIC_ESSHOUND, TARGET_MIND_STORM, TARGET_CYCLONE,
        TARGET_FIRE_ESSLING, TARGET_EARTH_ESSLING, TARGET_WATER_ESSLING,
        TARGET_MIND_ESSLING, TARGET_AIR_ESSLING];
    
      InitializeUserSelection;
      if Length(USER_SELECTED_ORDER) > 0 then
      begin
        SELECTED_TARGET_ORDER := USER_SELECTED_ORDER;
      end
      else
      begin
        SELECTED_TARGET_ORDER := DEFAULT_TARGET_ORDER;
      end;
    end;
    
    type TMatchDegree = (MATCH_EXACT, MATCH_CASE_INSENSITIVE, MATCH_GOOD, MATCH_LOOSE);
    
    function SubStringMatchFuzzy(ONE, TWO : String) : Integer; Forward;
    function SubStringMatchFuzzyEx(ONE, TWO : String; DegreeOfMatch : TMatchDegree)
      : Integer; Forward;
    function MatchTextFuzzyEx(MATCHSTRING : String; TARGETTEXT : String;
      DegreeOfMatch : TMatchDegree) : Boolean; Forward;
    function MatchUpTextFuzzy(MatchString : String; DegreeOfMatch : TMatchDegree):
      Boolean; Forward;
    
    function WaitOptionFuzzyEx(Option : String; DegreeOfMatch : TMatchDegree;
      Action: fnct_ActionOptions; Time: Integer) : Boolean; Forward;
    function WaitOptionFuzzy(Option : String; DegreeOfMatch : TMatchDegree;
      Time: Integer) : Boolean; Forward;
    function WaitUpTextFuzzy(Uptext : String; DegreeOfMatch : TMatchDegree;
      Time: Integer) : Boolean; Forward;
    
    
    const MAP_TYPE_OUTSIDE = -1; // not an area to click
    const MAP_TYPE_UNTESTED = 0; // beyond edge of island or not yet tested
    const MAP_TYPE_EDGE = 1;     // edge of an island
    const MAP_TYPE_ISLAND = 2;   // this square lies inside current island
    
    const STATS_EXP_RUNECRAFTING = 'Runecrafting EXP (Gained)';
    const STATS_EXP_TOTAL = 'Total EXP Gained';
    const STATS_LEVEL_RUNECRAFTING = 'Runecrafting Levels (Gained)';
    const STATS_RUNE_ESSENCE = 'Rune Essence (Mined)';
    const STATS_RUNE_AIR = 'Air Runes (Crafted)';
    const STATS_RUNE_MIND = 'Mind Runes (Crafted)';
    const STATS_RUNE_WATER = 'Water Runes (Crafted)';
    const STATS_RUNE_EARTH = 'Earth Runes (Crafted)';
    const STATS_RUNE_FIRE = 'Fire Runes (Crafted)';
    const STATS_RUNE_BODY = 'Body Runes (Crafted)';
    const STATS_RUNE_COSMIC = 'Cosmic Runes (Crafted)';
    const STATS_RUNE_CHAOS = 'Chaos Runes (Crafted)';
    const STATS_RUNE_ASTRAL = 'Astral Runes (Crafted)';
    const STATS_RUNE_NATURE = 'Nature Runes (Crafted)';
    const STATS_RUNE_LAW = 'Law Runes (Crafted)';
    const STATS_RUNE_DEATH = 'Death Runes (Crafted)';
    const STATS_RUNE_BLOOD = 'Blood Runes (Crafted)';
    const STATS_RUNE_SOUL = 'Soul Runes (Crafted)';
    
    const TYPE_RUNE_NONE = 0;
    const TYPE_RUNE_AIR = 1;
    const TYPE_RUNE_MIND = 2;
    const TYPE_RUNE_WATER = 3;
    const TYPE_RUNE_EARTH = 4;
    const TYPE_RUNE_FIRE = 5;
    const TYPE_RUNE_BODY = 6;
    const TYPE_RUNE_COSMIC = 7;
    const TYPE_RUNE_CHAOS = 8;
    const TYPE_RUNE_ASTRAL = 9;
    const TYPE_RUNE_NATURE = 10;
    const TYPE_RUNE_LAW = 11;
    const TYPE_RUNE_DEATH = 12;
    const TYPE_RUNE_BLOOD = 13;
    const TYPE_RUNE_SOUL = 14;
    const TYPE_RUNE_ESSENCE = 15;
    const TYPE_RUNE_MIN = TYPE_RUNE_AIR;
    const TYPE_RUNE_MAX = TYPE_RUNE_ESSENCE;
    
    var STATS_RUNE : array [TYPE_RUNE_MIN..TYPE_RUNE_MAX] of String;
    var CLICKBANS : TPointArray;
    
    procedure ClearClickban; forward;
    procedure RecordClickban(X, Y : Integer); forward;
    procedure CalculateScreenPoints; forward;
    function CheckIfWeCare(X1, Y1, X2, Y2 : Integer) : Boolean; forward;
    procedure WriteAccountingForCurrentModel(XPDiff : Integer; TimeDiff : Integer);
      forward;
    
    procedure InitializeStatsArray;
    begin
      STATS_RUNE[TYPE_RUNE_AIR] := STATS_RUNE_AIR;
      STATS_RUNE[TYPE_RUNE_MIND] := STATS_RUNE_MIND;
      STATS_RUNE[TYPE_RUNE_WATER] := STATS_RUNE_WATER;
      STATS_RUNE[TYPE_RUNE_EARTH] := STATS_RUNE_EARTH;
      STATS_RUNE[TYPE_RUNE_FIRE] := STATS_RUNE_FIRE;
      STATS_RUNE[TYPE_RUNE_BODY] := STATS_RUNE_BODY;
      STATS_RUNE[TYPE_RUNE_COSMIC] := STATS_RUNE_COSMIC;
      STATS_RUNE[TYPE_RUNE_CHAOS] := STATS_RUNE_CHAOS;
      STATS_RUNE[TYPE_RUNE_ASTRAL] := STATS_RUNE_ASTRAL;
      STATS_RUNE[TYPE_RUNE_NATURE] := STATS_RUNE_NATURE;
      STATS_RUNE[TYPE_RUNE_LAW] := STATS_RUNE_LAW;
      STATS_RUNE[TYPE_RUNE_DEATH] := STATS_RUNE_DEATH;
      STATS_RUNE[TYPE_RUNE_BLOOD] := STATS_RUNE_BLOOD;
      STATS_RUNE[TYPE_RUNE_SOUL] := STATS_RUNE_SOUL;
      STATS_RUNE[TYPE_RUNE_ESSENCE] := STATS_RUNE_ESSENCE;
    end;
    
    const GENERIC_ESSHOUND = 16628392;
    
    type TColorTolerance = record
      Color : Integer;
      Tolerance : Integer;
      MinHitsRequired : Integer;
      ToleranceSpeed : Integer;
    end;
    
    type TColorToleranceArray = array of TColorTolerance;
    
    type TPAModel = record
      ModelType : TModelTarget;
      Descriptor : String;
      ColorToleranceDefinition : TColorToleranceArray;
      ColorToleranceExcludes : TColorToleranceArray;
      Hound : Boolean;
      XP : Integer;
      MinimumFloor : Integer;
      MinimumLevel : Integer;
      MemberOnly : Boolean;
      Runes : TIntegerArray;
      ChipRune : Integer;
    end;
    
    type TPAModelArray = array of TPAModel;
    
    type TDenseColorInfo = record
      SumX : Integer;
      SumY : Integer;
      Pointcount : Integer;
      FoundPointCount : Integer;
      Matching : Boolean;
    end;
    
    type TTemporaryClickBanArea = record
      LastValid : Integer;
      AreaLocation : TPointArray;
    end;
    
    type TDenseColorInfoArray = array of TDenseColorInfo;
    
    type TModelAccounting = record
      Description : string;
      XPAcquired : Integer;
      Timespan : Integer;
    end;
    
    type TModelAccountingArray = array of TModelAccounting;
    
    var MAP : array [0..((MMX2 - MMX1) shr 3)] of array [0..((MMY2 - MMY1) shr 3)] of Integer;
    var MAP3D : array [0..((MMX2 - MMX1) shr 3)] of array [0..((MMY2 - MMY1) shr 3)] of TBox;
    var MAPPOINTS : TPointArray;
    var SCREENPOINTS : TPointArray;
    
    var RUNE_LastKnownPosition : array [TYPE_RUNE_MIN..TYPE_RUNE_MAX] of Integer;
    
    var TPAModelList : TPAModelArray;
    var ModelAccounting : TModelAccountingArray;
    var EnvironmentalExcludes : array [0..3] of TColorToleranceArray;
    var SmartLines: TStringArray;
    
    // totally stealing this because it is implemented well
    const LINEHEIGHT = 12;
    {*******************************************************************************
    Function PrintonSmart;
    By: Shuttleu
    Edited By: Ashaman88
    Description: Will put progress report on screen.
    *******************************************************************************}
    Procedure PrintOnSmart(TP: TStringArray; Placement: TPoint; Color: integer);
    var
      mx, my, Pic, I, E, H, TPH, Numb: Integer;
      TTP: TPointArray;
      Canvas: TCanvas;
    begin
      GetClientDimensions(mx, my);
      Pic := BitmapFromString(mx, Length(TP) * (LINEHEIGHT + 1) + 5, ''); // my, '');
      TPH := High(TP);
      for I := 0 to TPH do
      begin
        TTP := LoadTextTPA(TP[i], UpChars, H);
        for E := 0 to High(TTP) do
        begin
          Numb := (I * (LINEHEIGHT - 1));
          FastSetPixel(Pic, TTP[E].x + 1, TTP[E].y + Numb + 1,131072);
          FastSetPixel(Pic, TTP[E].x, TTP[E].y + Numb, Color);
        end;
      end;
      Canvas := TCANVAS.Create;
     // Canvas.Handle := SmartGetDebugDC;
      //DrawBitmap(Pic, Canvas, Placement.x, Placement.y);
      FreeBitmap(Pic);
    end;
    
    // make uptext and choose type of match
    function MakeUptext(Model : String; Hound : Boolean) : string;
    var HOUNDTYPE : string;
    var posOfSpace : Integer;
    begin
      // first of all - hounds tend to have very similar name so let's worry about
      // non hounds first
      if (not Hound) then
      begin
        Result := 'Siphon ' + Model;
        Exit;
      end;
      // shortest is AIR, or LAW so we will create Spxxxss, where xxx is hound name
      // because that will have at minimum 7 characters, so 75% match will require
      // 6 character match, Sp of course will match Siphon, while ss will match
      // ess(wraith/hound/ling)
      posOfSpace := Pos(' ', Model);
      // well we kinda need say... 3 or something of the sort
      if (posOfSpace > 2) then
      begin
        HOUNDTYPE := Copy(Model, 1, posOfSpace - 1);
      end
      else
      begin
        HOUNDTYPE := Model;
      end;
      // do it
      Result := 'Sp' + HOUNDTYPE + 'ss';
    end;
    
    // modified ChooseOptionMultiEx
    function ChooseOptionFuzzyEx(Option: String; DegreeOfMatch : TMatchDegree;
      Action: fnct_ActionOptions): Boolean;
    var
       B: TBox;
       H, ii, x: Integer;
       T: TPoint;
       Options: array of TOptions;
    begin
      Result := False;
      Options := GetChooseOptions('all');
      if (Length(Options) < 1) then
        Exit;
      H := High(Options);
      for ii := 0 to H do
      begin
        If MatchTextFuzzyEx(Option, Options[ii].Str, DegreeOfMatch) Then
        begin
          Result := True;
          B := Options[ii].Bounds;
          GetMousePos(T.x, T.y);
          case Action of
            ClickLeft: if PointInBox(T, B) then
                           ClickMouse2(true)
                         else
                           MouseBoxEx(B.x1 + 5, B.Y1, B.x2 - 5, B.Y1 + 5,5, 1);
            Move: if not PointInBox(T, B) then
                    MouseBoxEx(B.x1 + 5, B.Y1, B.x2 - 5, B.Y1 + 5,5, 3);
            Nothing:   begin end;
            else
              srl_warn('ChooseOptionMultiEx', 'ClickRight not a valid click for RS menus!', warn_AllVersions);
          end;
          Exit;
        end;
      end;
      B := Options[0].BigBox;//to mmouse away
      if Action <> Nothing then
      begin
        x := Max(B.X1 - 52, 0);
        if x = 0 then
          x := B.X2+10;
        MMouse(x, Max(B.Y1 - 50, 0), 40, B.Y2-B.Y1);
        Wait(200 + Random(100));
      end;
    end;
    function WaitOptionFuzzy(Option : String; DegreeOfMatch : TMatchDegree;
      Time: Integer) : Boolean;
    begin
      Result := WaitOptionFuzzyEx(Option, DegreeOfMatch, ClickLeft, Time);
    end;
    
    // why use WaitOptionMultiEx when we can just specify type of match
    function WaitOptionFuzzyEx(Option : String; DegreeOfMatch : TMatchDegree;
      Action: fnct_ActionOptions; Time: Integer) : Boolean;
    var TargetTime : Integer;
    begin
      Result := False;
      TargetTime := GetSystemTime + Time;
      while (GetSystemTime < TargetTime) do
      begin
        if (ChooseOptionFuzzyEx(Option, DegreeOfMatch, Action)) then
        begin
          Result := True;
          Exit;
        end;
        if (GetSystemTime < TargetTime) then
        begin
          // generally speaking, on windows, timer precision is about 1/100 of second
          // so you can't really sleep 1ms, it will end up being about 9-10
          Sleep(1);
        end
        else
        begin
          // no need to sleep and try again
          Exit;
        end;
      end;
    end;
    
    function WaitUpTextFuzzy(Uptext : String; DegreeOfMatch : TMatchDegree;
      Time: Integer) : Boolean;
    var TargetTime : Integer;
    begin
      Result := False;
      TargetTime := GetSystemTime + Time;
      while (GetSystemTime < TargetTime) do
      begin
        if (MatchTextFuzzyEx(Uptext, GetUpText, DegreeOfMatch)) then
        begin
          // done
          Result := True;
          Exit;
        end;
        if (GetSystemTime < TargetTime) then
        begin
          // generally speaking, on windows, timer precision is about 1/100 of second
          // so you can't really sleep 1ms, it will end up being about 9-10
          Sleep(1);
        end
        else
        begin
          // no need to sleep and try again
          Exit;
        end;
      end;
    end;
    
    function MatchTextFuzzyEx(MATCHSTRING : String; TARGETTEXT : String;
      DegreeOfMatch : TMatchDegree) : Boolean;
    var COUNT : Integer;
    begin
      Result := False;
      if (Pos(MATCHSTRING, TARGETTEXT) > 0) then
      begin
        Result := True;
        Exit;
      end;
      if DegreeOfMatch = MATCH_EXACT then Exit;
      if DegreeOfMatch = MATCH_CASE_INSENSITIVE then
      begin
        if (Pos(Uppercase(MATCHSTRING), Uppercase(TARGETTEXT)) > 0) then
        begin
          Result := True;
          Exit;
        end;
      end;
      if (DegreeOfMatch = MATCH_CASE_INSENSITIVE) then Exit;
      // check fuzzy - much more difficult - use recursion
      COUNT := SubStringMatchFuzzyEx(MATCHSTRING, TARGETTEXT, DegreeOfMatch);
      if (COUNT = 0) then Exit; // no match
      // determine if good match
      if (COUNT * 4 >= Length(MATCHSTRING) * 3) then
      begin
        // 75% matched
        Result := True;
        Exit;
      end;
      if (DegreeOfMatch = MATCH_GOOD) then Exit;
      if (COUNT * 2 >= Length(MATCHSTRING)) then
      begin
        // 50% matched
        Result := True;
      end;
    end;
    
    // counts number of matches in ordered fashion, i.e. ThIS matches aTIS in 3
    // but ThIS matches aTSI in 2
    // this is near impossible to take out of recursion because we need to do full
    // search so rather than trying to get out of recursion, let's optimize with
    // some globals
    var ___ACC : Integer;
    // let us add deadstop - if we need 50% of matches but we only have 47% of string
    // with 0 matches, we might as well throw in the towel
    function SubStringMatchFuzzyRec(__ONE, __TWO: String; OffsetOne, OffsetTwo : Integer; DeadStop : Integer)
     : Integer;
    var MatchCountOne : Integer;
    var MatchCountTwo : Integer;
    var Index : Integer;
    var ADJUSTMENT : Integer;
    var ADJOffsetOne : Integer;
    var ADJOffsetTwo : Integer;
    begin
      Inc(___ACC);
      Result := 0;
      ADJUSTMENT := 0;
      ADJOffsetOne := OffsetOne;
      ADJOffsetTwo := OffsetTwo;
      // length of string is stored in the string, so it is fast to retrieve
      if (Length(__ONE) <= ADJOffsetOne) then Exit;
      if (Length(__TWO) <= ADJOffsetTwo) then Exit;
      if (Length(__ONE) - ADJOffsetOne < DeadStop) then Exit;
      if (Length(__TWO) - ADJOffsetTwo < DeadStop) then Exit;
      while (__ONE[ADJOffsetOne + 1] = __TWO[ADJOffsetTwo + 1]) do
      begin
        Inc(ADJUSTMENT);
        Inc(ADJOffsetOne);
        Inc(ADJOffsetTwo);
        // test for end of string
        if (Length(__ONE) <= ADJOffsetOne) or (Length(__TWO) <= ADJOffsetTwo) then
        begin
          // done
          Result := ADJUSTMENT;
          Exit;
        end;
      end;
    //  if (ADJUSTMENT > 1) then Writeln('ADJRun: ' + IntToStr(ADJUSTMENT));
      // short of that split into two cases - recursive shorter first string
      MatchCountOne := SubStringMatchFuzzyRec(__ONE, __TWO, ADJOffsetOne + 1,
        ADJOffsetTwo, DeadStop - ADJUSTMENT);
      // and non recusive shorter second string until match
      MatchCountTwo := 0;
      for Index := ADJOffsetTwo + 2 to Length(__TWO) do
      begin
        if (__ONE[ADJOffsetOne + 1] = __TWO[Index]) then
        begin
          if (Index < Length(__TWO)) then
          begin
            if (Length(__ONE) > ADJOffsetOne + 1) then
            begin
              MatchCountTwo := SubStringMatchFuzzyRec(__ONE, __TWO,
                ADJOffsetOne + 1, Index, DeadStop - ADJUSTMENT);
            end;
          end;
          // and one for current match
          Inc(MatchCountTwo);
          break;
        end;
      end;
      // check
      Result := MatchCountOne;
      if (MatchCountTwo > Result) then Result := MatchCountTwo;
      Result := Result + ADJUSTMENT;
    end;
    
    function SubStringMatchFuzzy(ONE, TWO : String) : Integer;
    begin
      // faster if first param is shorter as it will do less recursive calls
      Result := SubStringMatchFuzzyEx(ONE, TWO, MATCH_LOOSE);
    end;
    
    function SubStringMatchFuzzyEx(ONE, TWO : String; DegreeOfMatch : TMatchDegree) : Integer;
    var DeadStop : Integer;
    var __ONE : String;
    var __TWO : String;
    var __LONE : Integer;
    begin
      // recursion is unbalanced as one branch was optimize to not use recursion
      // so result is faster if first string is shorter
      if (Length(ONE) > Length(TWO)) then
      begin
        __ONE := TWO;
        __TWO := ONE;
      end
      else
      begin
        __ONE := ONE;
        __TWO := TWO;
      end;
      __LONE := Length(__ONE);
      if (DegreeOfMatch = MATCH_LOOSE) then
      begin
        // divide by two and round up without using floating point
        DeadStop := (__LONE + 1) shr 1;
      end
      else
      begin
        // round up multiply by 0.75 without using floating point
        DeadStop := ((__LONE shl 1) + __LONE + 3) shr 2;
      end;
      Result := SubStringMatchFuzzyRec(__ONE, __TWO, 0, 0, DeadStop);
    end;
    
    function MatchUpTextFuzzy(MatchString : String; DegreeOfMatch : TMatchDegree): Boolean;
    begin
      Result := MatchTextFuzzyEx(GetUpText, MatchString, DegreeOfMatch);
    end;
    
    procedure LogSmartLine(NewLine : String);
    var Index : Integer;
    begin
      if Length(SmartLines) <> 10 then
      begin
        SetLength(SmartLines, 10);
      end;
      // rotate out
      for Index := 9 downto 1 do
      begin
        SmartLines[Index] := SmartLines[Index - 1];
      end;
      SmartLines[0] := NewLine;
      WriteLn(NewLine);
      // show
      PrintOnSmart(SmartLines, Point(15,15), clLime);
    end;
    
    procedure AppendSmartLine(AppendLine : String; Done : Boolean);
    begin
      if Length(SmartLines) <> 10 then
      begin
        SetLength(SmartLines, 10);
      end;
      SmartLines[0] := SmartLines[0] + AppendLine;
      if (Done) then
      begin
        WriteLn(SmartLines[0]);
      end;
      // show
      PrintOnSmart(SmartLines, Point(15,15), clLime);
    end;
    
    procedure ClearMap;
    var MAXX : Integer;
    var MAXY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var midX : Integer;
    var midY : Integer;
    var radius : Integer;
    var radius2 : Extended;
    var XPART : Extended;
    begin
      MAXX := Length(MAP);
      MAXY := Length(MAP[0]);
      midX := MAXX shr 1;
      midY := MAXY shr 1;
      SetLength(SCREENPOINTS, 0); // we are redoing map, will change screenpoints
      radius := midX;
      if (radius > midY) then radius := midY;
      Dec(radius);
      radius2 := Sqr(radius - 1);
      for indexX := 0 to MAXX - 1 do
      begin
        XPART := Sqr(midX - indexX - 1);
        for indexY := 0 to MAXY - 1 do
        begin
          // check
          if (midX = indexX + 1) and (midY = indexY + 1) then
          begin
            MAP[indexX][indexY] := MAP_TYPE_ISLAND;
            continue;
          end;
          if (Sqr(midY - indexY - 1) + XPART) > radius2 then
          begin
            MAP[indexX][indexY] := MAP_TYPE_OUTSIDE;
          end
          else
          begin
            MAP[indexX][indexY] := MAP_TYPE_UNTESTED;
          end;
        end;
      end;
    end;
    
    // detect edge of an island based on excessive amount of black pixels
    function TestMapSquare(indexX, indexY : Integer) : Integer;
    var X1, X2, Y1, Y2 : Integer;
    var Total : Integer;
    var ToleranceOfBlack : Integer;
    begin
      // until proven otherwise
      Result := MAP_TYPE_ISLAND;
      X1 := MMX1 + (indexX shl 3);
      X2 := X1 + 8;
      Y1 := MMY1 + (indexY shl 3);
      Y2 := Y1 + 8;
      Total := CountColorTolerance(clBlack, X1, Y1, X2, Y2, 30);
      ToleranceOfBlack := 8;
      if (Total <= ToleranceOfBlack) then Exit;
      // we should allow for more black if dots present
      ToleranceOfBlack := ToleranceOfBlack +
        ((CountColorTolerance(clWhite, X1, Y1, X2, Y2, 30)) shr 1);
      if (Total <= ToleranceOfBlack) then Exit;
      ToleranceOfBlack := ToleranceOfBlack +
        ((CountColorTolerance($e3fb0a, X1, Y1, X2, Y2, 30)) shr 1);
      if (Total <= ToleranceOfBlack) then Exit;
      // we found too much black, this is where the island ends
      Result := MAP_TYPE_EDGE;
    end;
    
    procedure UpdateMap;
    var Done : Boolean;
    var MAXX : Integer;
    var MAXY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var NeedTesting : Boolean;
    var TestResult : Integer;
    begin
      MAXX := Length(MAP);
      MAXY := Length(MAP[0]);
      // clear map and mark center point
      ClearMap();
      ClearClickban();
      repeat
        // assume done until proven otherwise
        Done := true;
        // go through the map
        for indexX := 0 to MAXX - 1 do
        begin
          for indexY := 0 to MAXY - 1 do
          begin
            if (MAP[indexX][indexY] <> MAP_TYPE_UNTESTED) then continue;
            NeedTesting := false;
            if (indexX > 0) then
            begin
              if (MAP[indexX - 1][indexY] = MAP_TYPE_ISLAND) then NeedTesting := true;
            end;
            if (indexY > 0) then
            begin
              if (MAP[indexX][indexY - 1] = MAP_TYPE_ISLAND) then NeedTesting := true;
            end;
            if (indexX < MAXX - 1) then
            begin
              if (MAP[indexX + 1][indexY] = MAP_TYPE_ISLAND) then NeedTesting := true;
            end;
            if (indexY < MAXY - 1) then
            begin
              if (MAP[indexX][indexY + 1] = MAP_TYPE_ISLAND) then NeedTesting := true;
            end;
            if not NeedTesting then continue;
            // test
            TestResult := TestMapSquare(indexX, indexY);
            MAP[indexX][indexY] := TestResult;
            if (TestResult = MAP_TYPE_ISLAND) then Done := false;
          end;
        end;
      until Done;
    end;
    
    // camera angle high
    const PERSPECTIVE_MM_MAP_SQUARE = 10.5;
    const PERSPECTIVE_MM_MAP_ASPECT_RATIO = 1;
    const PERSPECTIVE_MM_CAMERA_Z = 24;
    const PERSPECTIVE_MM_CAMERA_Y = 40;
    const PERSPECTIVE_MM_DEPTH_Z = 0;
    const AXIS_Y_ZERO_OFFSET = 1.5;
    
    var RUNESCAPE_MM_ANGLE_HIGH_TRANSFORM : Extended;
    var RUNESCAPE_MM_DISTANCE_Z : Extended;
    procedure CalculateTransformAngleMM;
    var DistanceZ : Extended;
    begin
      RUNESCAPE_MM_ANGLE_HIGH_TRANSFORM := ArcCos(PERSPECTIVE_MM_CAMERA_Z /
        Sqrt(Sqr(PERSPECTIVE_MM_CAMERA_Y) + Sqr(PERSPECTIVE_MM_CAMERA_Z)));
      // make runescape transform
      DistanceZ := PERSPECTIVE_MM_CAMERA_Z + PERSPECTIVE_MM_DEPTH_Z;
      RUNESCAPE_MM_DISTANCE_Z := Sqrt(Sqr(PERSPECTIVE_MM_CAMERA_Y) + Sqr(DistanceZ));
    end;
    
    // based on minimap offsets
    function GetPerspectivePointExactMM(X,Y,Z : Extended) : TPoint;
    var CAMERA_Z_DISTANCE : Extended;
    var CAMERA_Y_DISTANCE : Extended;
    var CAMERA_Z_DISTANCE2 : Extended;
    var CAMERA_Y_DISTANCE2 : Extended;
    var CAMERA_X_DISTANCE2 : Extended;
    var CAMERA_XZ_DISTANCE2 : Extended;
    var CAMERA_YZ_DISTANCE2 : Extended;
    var CAMERA_XZ_DISTANCE : Extended;
    var CAMERA_YZ_DISTANCE : Extended;
    var CAMERA_XYZ_DISTANCE : Extended;
    var IntegralX : Extended;
    var IntegralY : Extended;
    var ModZ : Extended;
    begin
      Result.X := MSCX;
      Result.Y := MSCY;
      CAMERA_Z_DISTANCE := PERSPECTIVE_MM_CAMERA_Z - Z;
      CAMERA_Y_DISTANCE := Y;
      if (CAMERA_Z_DISTANCE < 1) then Exit;
    
      CAMERA_Z_DISTANCE2 := Sqr(CAMERA_Z_DISTANCE);
      CAMERA_Y_DISTANCE2 := Sqr(CAMERA_Y_DISTANCE);
      CAMERA_X_DISTANCE2 := Sqr(X);
      CAMERA_XZ_DISTANCE2 := CAMERA_X_DISTANCE2 + CAMERA_Z_DISTANCE2;
      CAMERA_YZ_DISTANCE2 := CAMERA_Y_DISTANCE2 + CAMERA_Z_DISTANCE2;
      CAMERA_XZ_DISTANCE := Sqrt(CAMERA_XZ_DISTANCE2);
      CAMERA_YZ_DISTANCE := Sqrt(CAMERA_YZ_DISTANCE2);
      CAMERA_XYZ_DISTANCE := Sqrt(CAMERA_X_DISTANCE2 + CAMERA_YZ_DISTANCE2);
      // we do have asinh after all, it's called ArcSinh
      IntegralX := CAMERA_YZ_DISTANCE * ArcSinh(X / CAMERA_YZ_DISTANCE);
      IntegralY := CAMERA_XZ_DISTANCE * ArcSinh(Y / CAMERA_XZ_DISTANCE);
    
      // do not fret, asinh(x) = ln(x + sqrt(sqr(x) + 1))
      //IntegralX := CAMERA_YZ_DISTANCE * ln((X + sqrt(CAMERA_X_DISTANCE2 + CAMERA_YZ_DISTANCE2))/CAMERA_YZ_DISTANCE);
      //IntegralY := CAMERA_XZ_DISTANCE * ln((Y + sqrt(CAMERA_Y_DISTANCE2 + CAMERA_XZ_DISTANCE2))/CAMERA_XZ_DISTANCE);
      // this one is the simple one
      ModZ := RUNESCAPE_MM_DISTANCE_Z / CAMERA_Z_DISTANCE;
      Result.X := MSCX + Round(PERSPECTIVE_MM_MAP_SQUARE * PERSPECTIVE_MM_MAP_ASPECT_RATIO * IntegralX * ModZ);
      Result.Y := MSCY - Round(PERSPECTIVE_MM_MAP_SQUARE * IntegralY * ModZ);
    end;
    
    // this will multiply all axes by 8 to get MM transform
    // exact value is 8.0 but we want to be on the safe side
    // by "shrinking" space around player
    const MM_MULTIPLIER = 7.0;
    function GetPerspectivePoint(X,Y,Z : Extended) : TPoint;
    var Y1, Z1 : Extended;
    var PLAYERSHIFTEDY : Extended;
    var DistanceZ : Extended;
    var DistanceYZ : Extended;
    var POINTEXACT : TPoint;
    var ANGLE : Extended;
    begin
      // make runescape transform but in minimap points
      DistanceZ := PERSPECTIVE_MM_CAMERA_Z - (Z * MM_MULTIPLIER) + PERSPECTIVE_MM_DEPTH_Z;
      PLAYERSHIFTEDY := (Y * MM_MULTIPLIER) - AXIS_Y_ZERO_OFFSET;
      DistanceYZ := Sqrt(Sqr(PERSPECTIVE_MM_CAMERA_Y - PLAYERSHIFTEDY) + Sqr(DistanceZ));
      ANGLE := ArcCos(DistanceZ / DistanceYZ);
      Y1 := Sin(RUNESCAPE_MM_ANGLE_HIGH_TRANSFORM - ANGLE) * DistanceYZ;
      Z1 := PERSPECTIVE_MM_CAMERA_Z - Cos(RUNESCAPE_MM_ANGLE_HIGH_TRANSFORM - ANGLE) * DistanceYZ;
      POINTEXACT := GetPerspectivePointExactMM(X * MM_MULTIPLIER,Y1,Z1);
      Result.x := POINTEXACT.x;
      Result.y := POINTEXACT.y;
    end;
    
    const MAP_CUBES = 0.45;
    procedure DrawMapCube(X, Y : Extended; Color : TColor);
    var PERSPECTIVE : array [0..7] of TPoint;
    begin
      if (Y > (PERSPECTIVE_MM_CAMERA_Y - 1) * 8) then Exit;
    
      PERSPECTIVE[0] := GetPerspectivePoint(X - MAP_CUBES, 0.5 - MAP_CUBES, Y - MAP_CUBES);
      PERSPECTIVE[1] := GetPerspectivePoint(X - MAP_CUBES, 0.5 - MAP_CUBES, Y + MAP_CUBES);
      PERSPECTIVE[2] := GetPerspectivePoint(X + MAP_CUBES, 0.5 - MAP_CUBES, Y + MAP_CUBES);
      PERSPECTIVE[3] := GetPerspectivePoint(X + MAP_CUBES, 0.5 - MAP_CUBES, Y - MAP_CUBES);
      PERSPECTIVE[4] := GetPerspectivePoint(X - MAP_CUBES, 0.5 + MAP_CUBES, Y - MAP_CUBES);
      PERSPECTIVE[5] := GetPerspectivePoint(X - MAP_CUBES, 0.5 + MAP_CUBES, Y + MAP_CUBES);
      PERSPECTIVE[6] := GetPerspectivePoint(X + MAP_CUBES, 0.5 + MAP_CUBES, Y + MAP_CUBES);
      PERSPECTIVE[7] := GetPerspectivePoint(X + MAP_CUBES, 0.5 + MAP_CUBES, Y - MAP_CUBES);
    
    
    end;
    
    const MAXHEIGHT = 1.25;
    procedure Initialize3DModelFromMap;
    var MAXX : Integer;
    var MAXY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var X1,X2,Y1,Y2 : Integer;
    var BOX : TBox;
    var MidX, MidY : Extended;
    var ChosenColor : Integer;
    var XCenter, ZCenterFromY : Extended;
    var PointOfInterest : TPoint;
    var TotalPointCount : Integer;
    begin
      MAXX := Length(MAP);
      MAXY := Length(MAP[0]);
      SetLength(SCREENPOINTS, 0);
      SetLength(MAPPOINTS, MAXX * MAXY);
      TotalPointCount := 0;
      MidX := (MAXX shr 1) - 1.0;
      MidY := (MAXY shr 1) - 0.75;
      for indexX := 0 to MAXX - 1 do
      begin
        X1 := MMX1 + (indexX shl 3) + 2;
        X2 := X1 + 4;
        for indexY := 0 to MAXY - 1 do
        begin
          Y1 := MMY1 + (indexY shl 3) + 2;
          Y2 := Y1 + 4;
          BOX.X1 := X1;
          BOX.X2 := X2;
          BOX.Y1 := Y1;
          BOX.Y2 := Y2;
          ChosenColor := clGray;
          // check
          case (MAP[indexX][indexY]) of
            MAP_TYPE_OUTSIDE: ChosenColor := clPurple;
            MAP_TYPE_UNTESTED: ChosenColor := clGray;
            MAP_TYPE_EDGE: ChosenColor := clRed;
            MAP_TYPE_ISLAND: ChosenColor := clGreen;
          end;
          XCenter := indexX - MidX;
          ZCenterFromY := indexY - MidY;
          // calculate
          if (ZCenterFromY + 1.5 >= PERSPECTIVE_MM_CAMERA_Z * 8) then
          begin
            // too close to camera, not on screen, discard
            MAP3D[indexX][indexY].X1 := -1;
            MAP3D[indexX][indexY].Y1 := -1;
            MAP3D[indexX][indexY].X2 := -1;
            MAP3D[indexX][indexY].Y2 := -1;
            continue;
          end
          else
          begin
            if (XCenter > -0.5) then
            begin
              // right edge is determined by front right top
              PointOfInterest :=  GetPerspectivePoint(XCenter + 0.5, MAXHEIGHT, ZCenterFromY + 0.5);
              MAP3D[indexX][indexY].X2 := PointOfInterest.x;
            end
            else
            begin
              // right edge is determined by back right bottom
              PointOfInterest :=  GetPerspectivePoint(XCenter + 0.5, 0, ZCenterFromY - 0.5);
              MAP3D[indexX][indexY].X2 := PointOfInterest.x;
            end;
            // bottom edge is determined by front right bottom
            PointOfInterest :=  GetPerspectivePoint(XCenter + 0.5, 0, ZCenterFromY + 0.5);
            MAP3D[indexX][indexY].Y2 := PointOfInterest.y;
    
            if (XCenter < 0.5) then
            begin
              // left edge is determined by front left top
              PointOfInterest :=  GetPerspectivePoint(XCenter - 0.5, MAXHEIGHT, ZCenterFromY + 0.5);
              MAP3D[indexX][indexY].X1 := PointOfInterest.x;
            end
            else
            begin
              // left edge is determined by front left top
              PointOfInterest :=  GetPerspectivePoint(XCenter - 0.5, MAXHEIGHT, ZCenterFromY + 0.5);
              MAP3D[indexX][indexY].X1 := PointOfInterest.x;
            end;
            // top edge is determined by back left top
            PointOfInterest :=  GetPerspectivePoint(XCenter - 0.5, MAXHEIGHT, ZCenterFromY - 0.5);
            MAP3D[indexX][indexY].Y1 := PointOfInterest.y;
    
            // check if in area
            if (MAP3D[indexX][indexY].X1 >= MSX2) or (MAP3D[indexX][indexY].Y1 >= MSY2)
              or (MAP3D[indexX][indexY].X2 <= MSX1) or (MAP3D[indexX][indexY].Y2 <= MSY1) then
            begin
              // outside of main screen
              MAP3D[indexX][indexY].X1 := -1;
              MAP3D[indexX][indexY].Y1 := -1;
              MAP3D[indexX][indexY].X2 := -1;
              MAP3D[indexX][indexY].Y2 := -1;
              continue;
            end;
          end;
          MAPPOINTS[TotalPointCount].X := IndexX;
          MAPPOINTS[TotalPointCount].Y := IndexY;
          Inc(TotalPointCount);
    
          DrawMapCube(XCenter, ZCenterFromY, ChosenColor);
    
        end;
      end;
      SetLength(MAPPOINTS, TotalPointCount);
    end;
    
    procedure DrawMap;
    var MAXX : Integer;
    var MAXY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var X1,X2,Y1,Y2 : Integer;
    var BOX : TBox;
    var MidX, MidY : Extended;
    begin
      MAXX := Length(MAP);
      MAXY := Length(MAP[0]);
      MidX := (MAXX shr 1) - 1.0;
      MidY := (MAXY shr 1) - 0.75;
      for indexX := 0 to MAXX - 1 do
      begin
        X1 := MMX1 + (indexX shl 3) + 2;
        X2 := X1 + 4;
        for indexY := 0 to MAXY - 1 do
        begin
          Y1 := MMY1 + (indexY shl 3) + 2;
          Y2 := Y1 + 4;
          BOX.X1 := X1;
          BOX.X2 := X2;
          BOX.Y1 := Y1;
          BOX.Y2 := Y2;

  12. #1287
    Join Date
    Mar 2012
    Location
    Canada
    Posts
    442
    Mentioned
    4 Post(s)
    Quoted
    67 Post(s)

    Default

    Cont....
    Code:
    // check
          case (MAP[indexX][indexY]) of
            MAP_TYPE_UNTESTED:
              begin
    
              end;
            MAP_TYPE_EDGE:
              begin
    
                if ONSCREEN_TRACK_BOUNDARY then
                  if ((IndexY >= 6) and (IndexY <= 11) and (indexX >= 6) and (indexX <= 12)) then
                    DrawMapCube(indexX - MidX, indexY - MidY, clRed);
              end;
            MAP_TYPE_ISLAND:
              begin
    
                if ONSCREEN_TRACK_BOUNDARY then
                  if (IndexY = 9) and (indexX = 9) then DrawMapCube(indexX - MidX, indexY - MidY, clGreen);
              end;
          end;
        end;
      end;
    end;
    
    procedure Initialize3D();
    begin
      CalculateTransformAngleMM;
    end;
    
    procedure InitializeTPAModels();
    begin
      SetLength(TPAModelList, 0);
      SetLength(ModelAccounting, 0);
      InitializeTargetOrder;
    end;
    
    function MakeTPAModelCopy(Model : TPAModel) : TPAModel;
    var Index : Integer;
    begin
      Result.ModelType := Model.ModelType;
      Result.Descriptor := Model.Descriptor;
      SetLength(Result.ColorToleranceDefinition, Length(Model.ColorToleranceDefinition));
      for Index := 0 to Length(Model.ColorToleranceDefinition) - 1 do
      begin
        Result.ColorToleranceDefinition[Index].Color := Model.ColorToleranceDefinition[Index].Color;
        Result.ColorToleranceDefinition[Index].Tolerance := Model.ColorToleranceDefinition[Index].Tolerance;
        Result.ColorToleranceDefinition[Index].MinHitsRequired := Model.ColorToleranceDefinition[Index].MinHitsRequired;
        Result.ColorToleranceDefinition[Index].ToleranceSpeed := Model.ColorToleranceDefinition[Index].ToleranceSpeed;
      end;
      SetLength(Result.ColorToleranceExcludes, Length(Model.ColorToleranceExcludes));
      for Index := 0 to Length(Model.ColorToleranceExcludes) - 1 do
      begin
        Result.ColorToleranceExcludes[Index].Color := Model.ColorToleranceExcludes[Index].Color;
        Result.ColorToleranceExcludes[Index].Tolerance := Model.ColorToleranceExcludes[Index].Tolerance;
        Result.ColorToleranceExcludes[Index].MinHitsRequired := Model.ColorToleranceExcludes[Index].MinHitsRequired;
        Result.ColorToleranceExcludes[Index].ToleranceSpeed := Model.ColorToleranceExcludes[Index].ToleranceSpeed;
      end;
      Result.Hound := Model.Hound;
      Result.XP := Model.XP;
      Result.MinimumFloor := Model.MinimumFloor;
      Result.MinimumLevel := Model.MinimumLevel;
      Result.MemberOnly := Model.MemberOnly;
      SetLength(Result.Runes, Length(Model.Runes));
      for Index := 0 to Length(Model.Runes) - 1 do
      begin
        Result.Runes[Index] := Model.Runes[Index];
      end;
      Result.ChipRune := Model.ChipRune;
    end;
    
    procedure AddTPAModel(NewModel : TPAModel);
    var Index : Integer;
    begin
      Index := Length(TPAModelList);
      SetLength(TPAModelList, Index + 1);
      TPAModelList[Index] := MakeTPAModelCopy(NewModel);
    end;
    
    const STANDARD_TOLERANCE = 10;
    const WIDER_TOLERANCE = 20;
    (*
      COMPLETED: FULL ENVIRONMENTALS FOR ALL FLOORS
    
      COMPLETED MODELS:
        3RD ALL ESSWRAITHS
        3RD ALL SINGLE RUNE NODES: BLOOD POOL (little loose), SKULLS
        2ND:
          DOUBLE RUNE NODES:
          SINGLE RUNE NODES:
          F2P NODES:
          HOUNDS:
          F2P HOUNDS
        1ST:
          DOUBLE RUNE NODES:
          SINGLE RUNE NODES:
          ESSLINGS:
    
      NOT YET COMPLETED MODELS:
        3RD DOUBLE RUNE NODES: UNDEAD SOULS (NOT WELL TESTED YET), LIVING SOUL (TESTING)
        2ND:
          DOUBLE RUNE NODES:
          SINGLE RUNE NODES:
          F2P NODES:
          HOUNDS:
          F2P HOUNDS
        1ST:
          DOUBLE RUNE NODES:
          SINGLE RUNE NODES:
          ESSLINGS:
    *)
    procedure RegisterTPAModels;
    var Model : TPAModel;
    begin
      InitializeTPAModels;
    
      (* Ok before i get crap for putting begin/end around blocks that do not need
         them - i use it so i can collapse code, if you tell me how to get same
         functionality as #region/#endregion, i'll be happy to change that *)
    
      // environmental excludes
      begin // this is stub and will not contain excludes
      SetLength(EnvironmentalExcludes[0], 0);
      end;
      begin // first floor environmentals
      SetLength(EnvironmentalExcludes[1], 3);
      EnvironmentalExcludes[1][0].Color := $286060;
      EnvironmentalExcludes[1][0].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][0].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][0].MinHitsRequired := 900;
      EnvironmentalExcludes[1][1].Color := $489898;
      EnvironmentalExcludes[1][1].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][1].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][1].MinHitsRequired := 700;
      EnvironmentalExcludes[1][2].Color := $68A8F8;
      EnvironmentalExcludes[1][2].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][2].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][2].MinHitsRequired := 700;
      end;
      begin // second floor environmentals
      SetLength(EnvironmentalExcludes[2], 3);
      EnvironmentalExcludes[1][0].Color := $485068;
      EnvironmentalExcludes[1][0].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][0].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][0].MinHitsRequired := 900;
      EnvironmentalExcludes[1][1].Color := $7068A0;
      EnvironmentalExcludes[1][1].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][1].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][1].MinHitsRequired := 800;
      EnvironmentalExcludes[1][2].Color := $D080E0;
      EnvironmentalExcludes[1][2].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[1][2].ToleranceSpeed := 0;
      EnvironmentalExcludes[1][2].MinHitsRequired := 700;
      end;
      begin // third floor environmentals
      SetLength(EnvironmentalExcludes[3], 3);
      EnvironmentalExcludes[3][0].Color := $787888;
      EnvironmentalExcludes[3][0].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[3][0].ToleranceSpeed := 0;
      EnvironmentalExcludes[3][0].MinHitsRequired := 700;
      EnvironmentalExcludes[3][1].Color := $4850A8;
      EnvironmentalExcludes[3][1].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[3][1].ToleranceSpeed := 0;
      EnvironmentalExcludes[3][1].MinHitsRequired := 700;
      EnvironmentalExcludes[3][2].Color := $181850;
      EnvironmentalExcludes[3][2].Tolerance := WIDER_TOLERANCE;
      EnvironmentalExcludes[3][2].ToleranceSpeed := 0;
      EnvironmentalExcludes[3][2].MinHitsRequired := 400;
      end;
    
      begin // third floor hounds
      SetLength(Model.ColorToleranceDefinition, 0);
      SetLength(Model.ColorToleranceExcludes, 0);
      Model.MinimumFloor := 3;
      Model.MemberOnly := True;
      Model.Hound := True;
      SetLength(Model.Runes, 1);
      end;
      begin // soul wraith
      Model.Descriptor := 'Soul esswraith';
      Model.ModelType := TARGET_SOUL_ESSWRAITH;
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin // colors by palette
      Model.ColorToleranceDefinition[0].Color := $70B0F8; //C0C0A8;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $5890b8;
      Model.ColorToleranceDefinition[1].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $A08030;
      Model.ColorToleranceDefinition[2].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      end;
      Model.ColorToleranceDefinition[0].Color := 9008683; //8618097;
      Model.ColorToleranceDefinition[1].Color := 3096660; //8351271;
      Model.XP := 107;
      Model.MinimumLevel := 90;
      Model.Runes[0] := TYPE_RUNE_SOUL;
      Model.ChipRune := TYPE_RUNE_BLOOD;
      AddTPAModel(Model);
      end;
      begin // blood wraith
      Model.ModelType := TARGET_BLOOD_ESSWRAITH;
      Model.Descriptor := 'Blood esswraith';
      SetLength(Model.ColorToleranceExcludes, 1);
      begin
      // prevent confusion with death
      Model.ColorToleranceExcludes[0].Color := $688880;
      Model.ColorToleranceExcludes[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceExcludes[0].MinHitsRequired := 40;
      Model.ColorToleranceExcludes[0].ToleranceSpeed := 0;
      end;
      SetLength(Model.ColorToleranceDefinition, 4);
      begin // colors by palette analysis - first color done
      Model.ColorToleranceDefinition[0].Color := $B8A0B8;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := 4340806;
      Model.ColorToleranceDefinition[1].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 1; // auto for now
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := 7364718;
      Model.ColorToleranceDefinition[2].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 150; // auto for now
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[3].Color := 4408899;
      Model.ColorToleranceDefinition[3].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[3].MinHitsRequired := 110; // auto for now
      Model.ColorToleranceDefinition[3].ToleranceSpeed := 0;
      end;
      Model.XP := 73;
      Model.MinimumLevel := 77;
      Model.Runes[0] := TYPE_RUNE_BLOOD;
      Model.ChipRune := TYPE_RUNE_LAW;
      AddTPAModel(Model);
      end;
      begin // death wraith
      Model.ModelType := TARGET_DEATH_ESSWRAITH;
      Model.Descriptor := 'Death esswraith';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 2);
      begin
      Model.ColorToleranceDefinition[0].Color := $688880;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $304840;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 10;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      end;
      Model.XP := 60;
      Model.MinimumLevel := 65;
      Model.Runes[0] := TYPE_RUNE_DEATH;
      Model.ChipRune := TYPE_RUNE_NATURE;
      AddTPAModel(Model);
      end;
    
      // third floor nodes double runes
      begin
      SetLength(Model.ColorToleranceDefinition, 0);
      SetLength(Model.ColorToleranceExcludes, 0);
      Model.MinimumFloor := 3;
      Model.MemberOnly := True;
      Model.Hound := False;
      SetLength(Model.Runes, 2);
      Model.ChipRune := TYPE_RUNE_NONE;
      end;
      // undead soul NOT YET TESTED
      begin
      Model.ModelType := TARGET_UNDEAD_SOUL;
      Model.Descriptor := 'Undead soul';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin
      Model.ColorToleranceDefinition[0].Color := $343034; // 2564902;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5; // auto for now
    
      Model.ColorToleranceDefinition[1].Color := $9888A0; //8476273;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5; // auto for now
    
      Model.ColorToleranceDefinition[2].Color := $907090; // 10513548;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5; // auto for now
      end;
      Model.XP := 200;
      Model.MinimumLevel := 95;
      Model.Runes[0] := TYPE_RUNE_BLOOD;
      Model.Runes[1] := TYPE_RUNE_DEATH;
      AddTPAModel(Model);
      end;
    
      begin // bloody skulls
      Model.ModelType := TARGET_BLOODY_SKULLS;
      Model.Descriptor := 'Bloody skulls';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin // colors by palette analysis
      Model.ColorToleranceDefinition[0].Color := $000840;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $080818;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $303090;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      end;
      Model.XP := 160;
      Model.MinimumLevel := 83;
      Model.Runes[0] := TYPE_RUNE_BLOOD;
      Model.Runes[1] := TYPE_RUNE_DEATH;
      AddTPAModel(Model);
      end;
    
      // third floor nodes single runes
      begin
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 0);
      Model.MinimumFloor := 3;
      Model.MemberOnly := True;
      Model.Hound := False;
      SetLength(Model.Runes, 1);
      Model.ChipRune := TYPE_RUNE_NONE;
      end;
      begin // living soul
      Model.ModelType := TARGET_LIVING_SOUL;
      Model.Descriptor := 'Living soul';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin
      Model.ColorToleranceDefinition[0].Color := $B85840; //12212035;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[0].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $C86090;// 13329038;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $D878A0; //13794450;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      end;
      Model.XP := 213;
      Model.MinimumLevel := 90;
      Model.Runes[0] := TYPE_RUNE_SOUL;
      AddTPAModel(Model);
      end;
      begin // blood pool
      Model.ModelType := TARGET_BLOOD_POOL;
      Model.Descriptor := 'Blood pool';
      SetLength(Model.ColorToleranceExcludes, 6);
      begin // various excludes and reasons why false matches occured
      // essence by edge
      Model.ColorToleranceExcludes[0].Color := $686868;
      Model.ColorToleranceExcludes[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceExcludes[0].MinHitsRequired := 10;
      Model.ColorToleranceExcludes[0].ToleranceSpeed := 0;
      // purple clothing with brown and red edging, get rid of purple
      Model.ColorToleranceExcludes[1].Color := $402028;
      Model.ColorToleranceExcludes[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceExcludes[1].MinHitsRequired := 10;
      Model.ColorToleranceExcludes[1].ToleranceSpeed := 0;
      // fireball near essence and edge
      Model.ColorToleranceExcludes[2].Color := $40A0D0;
      Model.ColorToleranceExcludes[2].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceExcludes[2].MinHitsRequired := 10;
      Model.ColorToleranceExcludes[2].ToleranceSpeed := 0;
      // bloody skull
      Model.ColorToleranceExcludes[3].Color := $303090;
      Model.ColorToleranceExcludes[3].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceExcludes[3].MinHitsRequired := 5;
      Model.ColorToleranceExcludes[3].ToleranceSpeed := 0;
      // blood wraith
      Model.ColorToleranceExcludes[4].Color := $B8A0B8;
      Model.ColorToleranceExcludes[4].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceExcludes[4].MinHitsRequired := 5;
      Model.ColorToleranceExcludes[4].ToleranceSpeed := 0;
      // stones
      Model.ColorToleranceExcludes[5].Color := $787888;
      Model.ColorToleranceExcludes[5].Tolerance := WIDER_TOLERANCE;
      Model.ColorToleranceExcludes[5].ToleranceSpeed := 0;
      Model.ColorToleranceExcludes[5].MinHitsRequired := 50;
      end;
      SetLength(Model.ColorToleranceDefinition, 3);
      begin
      Model.ColorToleranceDefinition[0].Color := $18188A;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 30;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $304050;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;// WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 10;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $506880;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE; //WIDER_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 10;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      end;
      Model.XP := 146;
      Model.MinimumLevel := 77;
      Model.Runes[0] := TYPE_RUNE_BLOOD;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[2].Tolerance := 20;
      SetLength(Model.ColorToleranceExcludes, 0);
      end;
      begin // skulls
      Model.ModelType := TARGET_SKULLS;
      Model.Descriptor := 'Skulls';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin
      Model.ColorToleranceDefinition[0].Color := $98CCE2;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $405060;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $0C0D0E;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 6;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      end;
      Model.XP := 120;
      Model.MinimumLevel := 65;
      Model.Runes[0] := TYPE_RUNE_DEATH;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[2].Tolerance := 20;
      SetLength(Model.ColorToleranceExcludes, 0);
      end;
    
      // second floor hounds member only
      begin
      Model.MinimumFloor := 2;
      Model.MemberOnly := true;
      Model.Hound := True;
      SetLength(Model.Runes, 1);
      SetLength(Model.ColorToleranceDefinition, 2);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      end;
      // law
      begin
      Model.ModelType := TARGET_LAW_ESSHOUND;
      Model.Descriptor := 'Law esshound';
      Model.ColorToleranceDefinition[0].Color := 14394930; // 10776124;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 54;
      Model.MinimumLevel := 54;
      Model.Runes[0] := TYPE_RUNE_LAW;
      Model.ChipRune := TYPE_RUNE_ASTRAL;
      AddTPAModel(Model);
      end
      // law/generic
      begin
      Model.ModelType := TARGET_GENERIC_ESSHOUND;
      Model.Descriptor := ' esshound';
      Model.ColorToleranceDefinition[0].Color := 16620172;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 22;  // so that even body supercedes
      Model.MinimumLevel := 54;
      Model.Runes[0] := TYPE_RUNE_NONE;
      Model.ChipRune := TYPE_RUNE_NONE;
      AddTPAModel(Model);
      end;
      // nature
      begin
      Model.ModelType := TARGET_NATURE_ESSHOUND;
      Model.Descriptor := 'Nature esshound';
      Model.ColorToleranceDefinition[0].Color := 5145157;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 44;
      Model.MinimumLevel := 44;
      Model.Runes[0] := TYPE_RUNE_NATURE;
      Model.ChipRune := TYPE_RUNE_CHAOS;
      AddTPAModel(Model);
      end;
      // astral
      begin
      Model.ModelType := TARGET_ASTRAL_ESSHOUND;
      Model.Descriptor := 'Astral esshound';
      Model.ColorToleranceDefinition[0].Tolerance := 10;
      Model.ColorToleranceDefinition[0].Color := 14468828;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 36;
      Model.MinimumLevel := 40;
      Model.Runes[0] := TYPE_RUNE_ASTRAL;
      Model.ChipRune := TYPE_RUNE_COSMIC;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      end;
      // chaos
      begin
      Model.ModelType := TARGET_CHAOS_ESSHOUND;
      Model.Descriptor := 'Chaos esshound';
      Model.ColorToleranceDefinition[0].Color := 2729445; //2598117;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 31;
      Model.MinimumLevel := 36;
      Model.Runes[0] := TYPE_RUNE_CHAOS;
      Model.ChipRune := TYPE_RUNE_FIRE;
      AddTPAModel(Model);
      end;
      // cosmic
      begin
      Model.ModelType := TARGET_COSMIC_ESSHOUND;
      Model.Descriptor := 'Cosmic esshound';
      Model.ColorToleranceDefinition[0].Tolerance := 10;
      Model.ColorToleranceDefinition[0].Color := 7339005; //5239547;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 36;
      Model.MinimumLevel := 40;
      Model.Runes[0] := TYPE_RUNE_COSMIC;
      Model.ChipRune := TYPE_RUNE_EARTH;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 10;
      end;
    
      // second floor hounds f2p
      Model.MinimumFloor := 2;
      Model.MemberOnly := False;
      Model.Hound := True;
      SetLength(Model.Runes, 1);
      SetLength(Model.ColorToleranceDefinition, 2);
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      // body NOT YET TESTED
      Model.ModelType := TARGET_BODY_ESSHOUND;
      Model.Descriptor := 'Body esshound';
      Model.ColorToleranceDefinition[0].Color := 11414827;
      Model.ColorToleranceDefinition[1].Color := GENERIC_ESSHOUND;
      Model.XP := 23;
      Model.MinimumLevel := 20;
      Model.Runes[0] := TYPE_RUNE_BODY;
      Model.ChipRune := TYPE_RUNE_MIND;
      AddTPAModel(Model);
    
      // second floor nodes member double runes
      Model.MinimumFloor := 2;
      Model.MemberOnly := true;
      Model.Hound := False;
      SetLength(Model.Runes, 2);
      Model.ChipRune := TYPE_RUNE_NONE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      // nebula NOT YET TESTED
      Model.ModelType := TARGET_NEBULA;
      Model.Descriptor := 'Nebula';
      Model.ColorToleranceDefinition[0].Color := 8001883;
      Model.ColorToleranceDefinition[1].Color := 9443741;
      Model.XP := 75;
      Model.MinimumLevel := 40;
      Model.Runes[0] := TYPE_RUNE_ASTRAL;
      Model.Runes[1] := TYPE_RUNE_COSMIC;
      AddTPAModel(Model);
    
      // second floor nodes member single runes
      Model.MinimumFloor := 2;
      Model.MemberOnly := true;
      Model.Hound := False;
      SetLength(Model.Runes, 1);
      Model.ChipRune := TYPE_RUNE_NONE;
      SetLength(Model.ColorToleranceDefinition, 2);
      begin // jumper
      Model.ModelType := TARGET_JUMPER;
      Model.Descriptor := 'Jumper';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 2);
      begin
      Model.ColorToleranceDefinition[0].Color := $F8B050;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 10;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[1].Color := $E8A048;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      end;
      Model.XP := 108;
      Model.MinimumLevel := 54;
      Model.Runes[0] := TYPE_RUNE_LAW;
      AddTPAModel(Model);
      end;
      begin // shifter
      Model.ModelType := TARGET_SHIFTER;
      Model.Descriptor := 'Shifter';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 2);
      begin
      Model.ColorToleranceDefinition[0].Color := $30F828;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 20;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $20D020;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      end;
      Model.XP := 87;
      Model.MinimumLevel := 44;
      Model.Runes[0] := TYPE_RUNE_NATURE;
      AddTPAModel(Model);
      end;
      // chaotic cloud
      begin
      Model.ModelType := TARGET_CHAOTIC_CLOUD;
      Model.Descriptor := 'Chaotic cloud';
      Model.ColorToleranceDefinition[0].Color := 1446423;
      Model.ColorToleranceDefinition[1].Color := 1511722;
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      Model.XP := 62;
      Model.MinimumLevel := 35;
      Model.Runes[0] := TYPE_RUNE_CHAOS;
      AddTPAModel(Model);
      end;
    
      // second floor nodes f2p
      begin
      Model.MinimumFloor := 2;
      Model.MemberOnly := False;
      Model.Hound := False;
      SetLength(Model.Runes, 1);
      Model.ChipRune := TYPE_RUNE_NONE;
      SetLength(Model.ColorToleranceDefinition, 4);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[2].Tolerance := 20;
      Model.ColorToleranceDefinition[3].Tolerance := 20;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[2].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[3].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[3].ToleranceSpeed := 2;
      end;
      begin // fleshy growth
      Model.ModelType := TARGET_FLESHY_GROWTH;
      Model.Descriptor := 'Fleshy growth';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 4);
      begin
      Model.ColorToleranceDefinition[0].Color := $5868a0;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 5;
    
      Model.ColorToleranceDefinition[1].Color := $303080;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
    
      Model.ColorToleranceDefinition[2].Color := $305098;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5;
    
      Model.ColorToleranceDefinition[3].Color := $485880;
      Model.ColorToleranceDefinition[3].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[3].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[3].MinHitsRequired := 5;
      end;
      Model.XP := 46;
      Model.MinimumLevel := 20;
      Model.Runes[0] := TYPE_RUNE_BODY;
      AddTPAModel(Model);
      end;
      begin // fire storm
      Model.ModelType := TARGET_FIRE_STORM;
      Model.Descriptor := 'Fire storm';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 3);
      begin
      Model.ColorToleranceDefinition[0].Color := $1848D0;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 3;
    
      Model.ColorToleranceDefinition[1].Color := $1020A0;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 5;
    
      Model.ColorToleranceDefinition[2].Color := $081028;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 5;
      end;
      Model.XP := 32;
      Model.MinimumLevel := 27;
      SetLength(Model.Runes, 2);
      Model.Runes[0] := TYPE_RUNE_FIRE;
      Model.Runes[1] := TYPE_RUNE_AIR;
      AddTPAModel(Model);
      end;
    
      // first floor nodes double runes
      Model.MinimumFloor := 1;
      Model.MemberOnly := False;
      Model.Hound := False;
      SetLength(Model.Runes, 2);
      Model.ChipRune := TYPE_RUNE_NONE;
      SetLength(Model.ColorToleranceDefinition, 2);
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      // vine
      Model.ModelType := TARGET_VINE;
      Model.Descriptor := 'Vine';
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[0].Tolerance := 10;
      Model.ColorToleranceDefinition[1].Tolerance := 10;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[0].Color := 2178115;
      Model.ColorToleranceDefinition[1].Color := 145447;
      Model.XP := 33;
      Model.MinimumLevel := 17;
      Model.Runes[0] := TYPE_RUNE_EARTH;
      Model.Runes[1] := TYPE_RUNE_WATER;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      // first floor nodes single runes
      Model.MinimumFloor := 1;
      Model.MemberOnly := False;
      Model.Hound := False;
      SetLength(Model.Runes, 1);
      Model.ChipRune := TYPE_RUNE_NONE;
      // fireball NOT YET TESTED on 2nd and 1st
      Model.ModelType := TARGET_FIREBALL;
      Model.Descriptor := 'Fireball';
      Model.ColorToleranceDefinition[0].Color := 925837;
      Model.ColorToleranceDefinition[1].Color := 3513307;
      Model.XP := 35;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_FIRE;
      AddTPAModel(Model);
      // rock fragment
      Model.ModelType := TARGET_ROCK_FRAGMENT;
      SetLength(Model.ColorToleranceDefinition, 3);
      Model.ColorToleranceDefinition[0].Tolerance := 8;
      Model.ColorToleranceDefinition[1].Tolerance := 8;
      Model.ColorToleranceDefinition[2].Tolerance := 8;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 2;
      Model.Descriptor := 'Rock fragment';
      Model.ColorToleranceDefinition[0].Color := 2639195
      Model.ColorToleranceDefinition[1].Color := 5289629;//5488036;
      Model.ColorToleranceDefinition[2].Color := 4826768;
      Model.XP := 35;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_EARTH;
      AddTPAModel(Model);
      SetLength(Model.ColorToleranceDefinition, 2);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      // water pool NOT YET DONE
      Model.ModelType := TARGET_WATER_POOL;
      Model.Descriptor := 'Water pool';
      Model.ColorToleranceDefinition[0].Tolerance := 10;
      Model.ColorToleranceDefinition[1].Tolerance := 10;
      Model.ColorToleranceDefinition[0].Color := 13353360;
      Model.ColorToleranceDefinition[1].Color := 10849114;// 12297056;
      Model.XP := 35;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_FIRE;
      AddTPAModel(Model);
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      // mind storm NOT YET DONE
      Model.ModelType := TARGET_MIND_STORM;
      Model.Descriptor := 'Mind storm';
      //Model.ColorToleranceDefinition[0].Color := 925837;
      //Model.ColorToleranceDefinition[1].Color := 4239833;
      Model.XP := 35;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_MIND;
      //AddTPAModel(Model);
      // cyclone NOT YET DONE
      Model.Descriptor := 'Cyclone';
      Model.ModelType := TARGET_CYCLONE;
      //Model.ColorToleranceDefinition[0].Color := 925837;
      //Model.ColorToleranceDefinition[1].Color := 4239833;
      Model.XP := 35;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_AIR;
      //AddTPAModel(Model);
    
      // first floor hounds
      begin
      Model.MinimumFloor := 1;
      Model.MemberOnly := False;
      Model.Hound := True;
      SetLength(Model.Runes, 1);
      SetLength(Model.ColorToleranceDefinition, 2);
      Model.ColorToleranceDefinition[0].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[1].MinHitsRequired := 0; // auto for now
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 2;
      end;
      // fire NOT YET DONE
      begin
      Model.ModelType := TARGET_FIRE_ESSLING;
      Model.Descriptor := 'Fire essling';
      Model.ColorToleranceDefinition[0].Color := 5731996; // 12240595;
      Model.ColorToleranceDefinition[0].Tolerance := 10; // strict
      Model.ColorToleranceDefinition[1].Color := 11715024; //3692927; // 3890823;
      Model.ColorToleranceDefinition[1].Tolerance := 10; // strict
      Model.XP := 17;
      Model.MinimumLevel := 14;
      Model.Runes[0] := TYPE_RUNE_FIRE;
      Model.ChipRune := TYPE_RUNE_WATER;
      AddTPAModel(Model);
      // back to loose
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      end;
      // earth
      begin
      Model.ModelType := TARGET_EARTH_ESSLING;
      Model.Descriptor := 'Earth essling';
      Model.ColorToleranceDefinition[0].Color := 5465237;
      Model.ColorToleranceDefinition[0].Tolerance := 10; // strict
      Model.ColorToleranceDefinition[1].Color := 12769230;
      Model.XP := 14;
      Model.MinimumLevel := 9;
      Model.Runes[0] := TYPE_RUNE_EARTH;
      Model.ChipRune := TYPE_RUNE_WATER;
      AddTPAModel(Model);
      // back to loose
      Model.ColorToleranceDefinition[0].Tolerance := 20;
      Model.ColorToleranceDefinition[1].Tolerance := 20;
      end;
      // water
      begin
      Model.ModelType := TARGET_WATER_ESSLING;
      Model.Descriptor := 'Water essling';
      Model.ColorToleranceDefinition[0].Color := 9601474; //4998761; - this one is bad in 2nd floor
      Model.ColorToleranceDefinition[1].Color := 13218923; //14075248;//13681804;
      Model.XP := 13;
      Model.MinimumLevel := 5;
      Model.Runes[0] := TYPE_RUNE_WATER;
      Model.ChipRune := TYPE_RUNE_AIR;
      AddTPAModel(Model);
      end;
      // mind
      begin
      Model.ModelType := TARGET_MIND_ESSLING;
      Model.Descriptor := 'Mind essling';
      Model.ColorToleranceDefinition[0].Color := 6788506; //5602943;
      Model.ColorToleranceDefinition[1].Color := 13885421;//8569282;
      Model.XP := 10;
      Model.MinimumLevel := 1;
      Model.Runes[0] := TYPE_RUNE_MIND;
      Model.ChipRune := TYPE_RUNE_AIR;
      AddTPAModel(Model);
      end;
      begin // air essling
      Model.ModelType := TARGET_AIR_ESSLING;
      Model.Descriptor := 'Air essling';
      SetLength(Model.ColorToleranceExcludes, 0);
      SetLength(Model.ColorToleranceDefinition, 4);
      begin // from palette analysis
      Model.ColorToleranceDefinition[0].Color := $d8b078;
      Model.ColorToleranceDefinition[0].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[0].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[0].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[1].Color := $d0d8e0;
      Model.ColorToleranceDefinition[1].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[1].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[1].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[2].Color := $a88858;
      Model.ColorToleranceDefinition[2].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[2].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[2].ToleranceSpeed := 0;
    
      Model.ColorToleranceDefinition[3].Color := $786848;
      Model.ColorToleranceDefinition[3].Tolerance := STANDARD_TOLERANCE;
      Model.ColorToleranceDefinition[3].MinHitsRequired := 2;
      Model.ColorToleranceDefinition[3].ToleranceSpeed := 0;
      end;
      Model.XP := 10;
      Model.MinimumLevel := 1;
      Model.Runes[0] := TYPE_RUNE_AIR;
      Model.ChipRune := TYPE_RUNE_AIR;
      AddTPAModel(Model);
      end;
    end;
    
    function GetSearchOrderTPAModelList(HoundsOnly : Boolean; FloorLevelFilter : Integer;
      SkillLevelFilter : Integer; IsMember : Boolean) : TPAModelArray;
    var MyArray : TPAModelArray;
    var MyIndex : Integer;
    var index : Integer;
    var HighestModelXP : Integer;
    var HighestIndex : Integer;
    var OutputIndex : Integer;
    var XPCutoffThird : Integer;
    var OrderIndex : Integer;
    var MaxOrder : Integer;
    begin
      // init
      XPCutoffThird := SkillLevelFilter - 20;
      if (XPCutoffThird > 55) then XPCutoffThird := 55;
      SetLength(MyArray, Length(TPAModelList));
      MyIndex := 0;
      // sweep
      for index := 0 to Length(TPAModelList) - 1 do
      begin
        if not IsMember then
        begin
          if TPAModelList[Index].MemberOnly then continue;
        end;
        if HoundsOnly then
        begin
          if not TPAModelList[Index].Hound then continue;
        end;
        if TPAModelList[Index].MinimumFloor > FloorLevelFilter then continue;
        if TPAModelList[Index].MinimumLevel > SkillLevelFilter then continue;
        if (SMART_THIRD_LEVEL) then
        begin
          // check if on 3rd level
          if FloorLevelFilter = 3 then
          begin
            // check by XP
            if (TPAModelList[Index].XP < XPCutoffThird) then continue;
          end;
        end;
        // got result
        MyArray[MyIndex] := TPAModelList[Index];
        Inc(MyIndex);
      end;
      // do it
      SetLength(Result, MyIndex);
      OutputIndex := 0;
      // now let's order
      MaxOrder := Length(SELECTED_TARGET_ORDER);
      for OrderIndex := 0 to MaxOrder - 1 do
      begin
        repeat
          HighestModelXP := -1;
          HighestIndex := -1;
          for index := 0 to MyIndex - 1 do
          begin
            if MyArray[index].ModelType <> SELECTED_TARGET_ORDER[OrderIndex] then continue;
            if MyArray[index].XP < 0 then continue;
            if MyArray[index].XP <= HighestModelXP then continue;
            HighestModelXP := MyArray[Index].XP;
            HighestIndex := index;
          end;
          if (HighestIndex >= 0) then
          begin
            Result[OutputIndex] := MyArray[HighestIndex];
            Inc(OutputIndex);
            MyArray[HighestIndex].XP := -1;
          end;
        until (HighestIndex < 0);
      end;
    end;
    
    function TestDenseColorRaster(Target : TColorTolerance; X1, Y1, X2, Y2 : Integer) : Boolean;
    var x,y : Integer;
    begin
      SetColorToleranceSpeed(Target.ToleranceSpeed);
      Result := FindColorTolerance(x, y, Target.Color, X1, Y1, X2, Y2, Target.Tolerance);
      SetColorToleranceSpeed(1);
    end;
    
    function TestDenseColorRasterCount(Target : TColorTolerance; X1, Y1, X2, Y2 : Integer) : Boolean;
    var minMatchCount : Integer;
    var NumberFound : Integer;
    begin
      Result := false;
      SetColorToleranceSpeed(Target.ToleranceSpeed);
      NumberFound := CountColorTolerance(Target.Color, X1, Y1, X2, Y2, Target.Tolerance);
      SetColorToleranceSpeed(1);
      // set up match count
      if (Target.MinHitsRequired > 0) then
      begin
        minMatchCount := Target.MinHitsRequired;
      end
      else
      begin
        minMatchCount := 5;
        if (Target.Tolerance < 20) then
        begin
          minMatchCount := 1 + (Target.Tolerance shr 2);
        end;
      end;
      if (NumberFound < minMatchCount) then
      begin
        // done
        exit;
      end;
      Result := true;
    end;
    
    function CalculateDenseColorInfo(Target : TColorTolerance; X1, Y1, X2, Y2 : Integer) : TDenseColorInfo;
    var MatchingTPA : TPointArray;
    var Total : Integer;
    var Index : Integer;
    var minMatchCount : Integer;
    var Found : Boolean;
    begin
      Result.Pointcount := 0;
      Result.SumX := 0;
      Result.SumY := 0;
      Result.Matching := false;
      Result.FoundPointCount := 0;
      SetColorToleranceSpeed(Target.ToleranceSpeed);
      Found := FindColorsTolerance(MatchingTPA, Target.Color, X1, Y1, X2, Y2, Target.Tolerance);
      SetColorToleranceSpeed(1);
      if not Found then
      begin
        // done
        exit;
      end;
      Total := Length(MatchingTPA);
      Result.FoundPointCount := Total;
      // set up match count
      if (Target.MinHitsRequired > 0) then
      begin
        minMatchCount := Target.MinHitsRequired;
      end
      else
      begin
        minMatchCount := 5;
        if (Target.Tolerance < 20) then
        begin
          minMatchCount := 1 + (Target.Tolerance shr 2);
        end;
      end;
      if (Total < minMatchCount) then
      begin
        // done
        exit;
      end;
      Result.Matching := true;
      // check
      if (Total > 20) then
      begin
        // calculate sparse
        Total := Total div 20;
        for index := 0 to 19 do
        begin
          Inc(Result.Pointcount);
          Result.SumX := Result.SumX + MatchingTPA[index * Total].x;
          Result.SumY := Result.SumY + MatchingTPA[index * Total].y;
        end;
      end
      else
      begin
        // calculate simple
        for index := 0 to Total - 1 do
        begin
          Inc(Result.Pointcount);
          Result.SumX := Result.SumX + MatchingTPA[index].x;
          Result.SumY := Result.SumY + MatchingTPA[index].y;
        end;
      end;
    end;
    
    const MAX_MODEL_RESULTS = 10;
    // most models are at minimum 20x20 but more likely 30x40 or more
    // so we rasterize in 16x16 increments with 32x32 model spread
    function FindModel(Model : TPAModel; Environment : TColorToleranceArray) : TPointArray;
    var X1, Y1, X2, Y2 : Integer;
    var QuickMatchAll : Boolean;
    var CurrentRasterInfo : TDenseColorInfoArray;
    var OverlayCount : Integer;
    var Index : Integer;
    var ColorRasters : TDenseColorInfoArray;
    var SumIndex : Integer;
    var ResultCount : Integer;
    var MaxCounter : Integer;
    var MaxIndex : Integer;
    var secondaryIndex : Integer;
    var SCREENPOINTSIndex : Integer;
    var SCREENPOINTSCount : Integer;
    var MAXClickBans : Integer;
    var ClickBan : Boolean;
    var ExcludeCount : Integer;
    var EnvironmentCount : Integer;
    begin
      // raster info
      OverlayCount := Length(Model.ColorToleranceDefinition);
      ExcludeCount := Length(Model.ColorToleranceExcludes);
      EnvironmentCount := Length(Environment);
      SetLength(CurrentRasterInfo, OverlayCount);
      SetLength(ColorRasters, 0);
      // check
      if (Length(SCREENPOINTS) <= 0) then CalculateScreenPoints;
      SCREENPOINTSCount := Length(SCREENPOINTS);
      MAXClickBans := -1;
      for Index := 0 to CLICKBAN_MAX_AREAS - 1 do
      begin
        if (CLICKBANS[Index].x < 0) then break;
        MAXClickBans := Index;
      end;
      // go through screen
      for SCREENPOINTSIndex := 0 to SCREENPOINTSCount - 1 do
      begin
        // check against clickban
        ClickBan := false;
        for Index := 0 to MAXClickBans do
        begin
          if CLICKBANS[Index].x <> SCREENPOINTS[SCREENPOINTSIndex].x then continue;
          if CLICKBANS[Index].y <> SCREENPOINTS[SCREENPOINTSIndex].y then continue;
          ClickBan := true;
          break;
        end;
        if (ClickBan) then continue;
        X1 := MSX1 + (SCREENPOINTS[SCREENPOINTSIndex].x shl 4) - 16;
        X2 := X1 + 32;
        if (X1 < MSX1) then X1 := MSX1;
        if (X2 > MSX2) then X2 := MSX2;
        Y1 := MSY1 + (SCREENPOINTS[SCREENPOINTSIndex].y shl 4) - 16;
        Y2 := Y1 + 32;
        if (Y1 < MSX1) then Y1 := MSY1;
        if (Y2 > MSY2) then Y2 := MSY2;
        // quick check
        QuickMatchAll := True;
        for Index := 0 to OverlayCount - 1 do
        begin
          if not TestDenseColorRasterCount(Model.ColorToleranceDefinition[Index],
            X1, Y1, X2, Y2) then
          begin
            QuickMatchAll := false;
            break;
          end;
        end;
        // check
        if not QuickMatchAll then continue;
        // check environment
        for Index := 0 to EnvironmentCount - 1 do
        begin
          if (TestDenseColorRasterCount(Environment[Index], X1, Y1, X2, Y2)) then
          begin
            // environment block
            // SMART_DrawBoxMS(false, IntToBox(X1+11, Y1 + 11, X1+21, Y1 + 21), clNavy)
            continue;
          end;
        end;
        // check excludes
        for Index := 0 to ExcludeCount - 1 do
        begin
          if (TestDenseColorRasterCount(Model.ColorToleranceExcludes[Index],
            X1, Y1, X2, Y2)) then
          begin
            // model specific block
            // SMART_DrawBoxMS(false, IntToBox(X1+11, Y1 + 11, X1+21, Y1 + 21), clPurple)
            continue;
          end;
        end;
        // now get rasters
        for Index := 0 to OverlayCount - 1 do
        begin
          CurrentRasterInfo[Index] := CalculateDenseColorInfo(Model.ColorToleranceDefinition[Index],
            X1, Y1, X2, Y2);
          // check
          if not CurrentRasterInfo[Index].Matching then
          begin
            QuickMatchAll := false;
            break;
          end;
        end;
        if not QuickMatchAll then continue;
        // calculate new raster
        SumIndex := Length(ColorRasters);
        SetLength(ColorRasters, SumIndex + 1);
        ColorRasters[SumIndex].Matching := true;
        ColorRasters[SumIndex].Pointcount := 0;
        ColorRasters[SumIndex].FoundPointCount := 0;
        ColorRasters[SumIndex].SumX := 0;
        ColorRasters[SumIndex].SumY := 0;
        for Index := 0 to OverlayCount - 1 do
        begin
          //WriteLn('MATCH: ' + IntToStr(SumIndex) + ', Color layer ' + IntToStr(Index) + ' hitcount ' + IntToStr(
          //  CurrentRasterInfo[Index].FoundPointCount));
          ColorRasters[SumIndex].FoundPointCount := ColorRasters[SumIndex].FoundPointCount + CurrentRasterInfo[Index].FoundPointCount;
          ColorRasters[SumIndex].Pointcount := ColorRasters[SumIndex].Pointcount + CurrentRasterInfo[Index].Pointcount;
          ColorRasters[SumIndex].SumX := ColorRasters[SumIndex].SumX + CurrentRasterInfo[Index].SumX;
          ColorRasters[SumIndex].SumY := ColorRasters[SumIndex].SumY + CurrentRasterInfo[Index].SumY;
        end;
      end;
      // now get the results
      ResultCount := MAX_MODEL_RESULTS;
      if (ResultCount > Length(ColorRasters)) then
      begin
        ResultCount := Length(ColorRasters);
      end;
      // build result
      SetLength(Result, ResultCount);
      for index := 0 to ResultCount - 1 do
      begin
        MaxCounter := -1;
        MaxIndex := -1;
        for secondaryIndex := 0 to Length(ColorRasters) - 1 do
        begin
          if not ColorRasters[secondaryIndex].Matching then continue;
          //if ColorRasters[secondaryIndex].Pointcount < MaxCounter then continue;
          if ColorRasters[secondaryIndex].FoundPointCount < MaxCounter then continue;
          //MaxCounter := ColorRasters[secondaryIndex].Pointcount;
          MaxCounter := ColorRasters[secondaryIndex].FoundPointCount;
          MaxIndex := secondaryIndex;
        end;
        // do it
        //WriteLn('Result ' + IntTOStr(index) + ' is from match ' + IntToStr(MaxIndex));
        Result[index].x := ColorRasters[MaxIndex].SumX / ColorRasters[MaxIndex].Pointcount;
        Result[index].y := ColorRasters[MaxIndex].SumY / ColorRasters[MaxIndex].Pointcount;
        ColorRasters[MaxIndex].Matching := False;
        //if (index = 0) then WriteLn('Result#1 ' + IntTOStr(index) + ' is from match ' + IntToStr(MaxIndex) + ' with ' + IntTOStr(
        //MaxCounter));
      end;
    end;
    
    function SkipClick(x, y : Integer) : Boolean;
    begin
      // by default we enable click
      Result := false;
      // check if in the points section
      if (Y < 200) and (X < 55) then
      begin
        Result := true;
        exit;
      end;
      // check if xp overhead
      if (Y < 66) and (X > 220) and (X < 300) then
      begin
        Result := true;
        exit;
      end;
      // do not click yourself
      if (X > 235) and (X < 285) AND (Y > 125) AND (Y < 170) then
      begin
        Result := true;
        exit;
      end;
    end;
    
    procedure CalculateScreenPoints;
    var RasterX : Integer;
    var RasterY : Integer;
    var X1, Y1, X2, Y2 : Integer;
    var MaxRasterX : Integer;
    var MaxRasterY : Integer;
    var SCREENPOINTSCount : Integer;
    begin
      // go through screen to figure out which points we care about
      MaxRasterX := ((MSX2 - MSX1) shr 4);
      MaxRasterY := ((MSY2 - MSY1) shr 4);
      SetLength(SCREENPOINTS, (MaxRasterX + 1) * (MaxRasterY * 1));
      SCREENPOINTSCount := 0;
      for RasterX := 0 to MaxRasterX do
      begin
        X1 := MSX1 + (RasterX shl 4) - 16;
        X2 := X1 + 32;
        if (X1 < MSX1) then X1 := MSX1;
        if (X2 > MSX2) then X2 := MSX2;
        for RasterY := 0 to MaxRasterY do
        begin
          Y1 := MSY1 + (RasterY shl 4) - 16;
          Y2 := Y1 + 32;
          if (Y1 < MSX1) then Y1 := MSY1;
          if (Y2 > MSY2) then Y2 := MSY2;
          // finally - we can check against map rosters
          if not CheckIfWeCare(X1, Y1, X2, Y2) then
          begin
            // we do not care
            //SMART_DrawBoxEx(false, IntToBox(X1,Y1,X2,Y2), clNavy);
            continue;
          end;
          // we care
          SCREENPOINTS[SCREENPOINTSCount].X := RasterX;
          SCREENPOINTS[SCREENPOINTSCount].Y := RasterY;
          Inc(SCREENPOINTSCount);
        end;
      end;
      // done
      SetLength(SCREENPOINTS, SCREENPOINTSCount);
    end;
    
    // yay, kick some az
    var LastRightClick : Integer;
    function FindTarget(HoundsOnly : Boolean; FloorLevelFilter : Integer;
      SkillLevelFilter : Integer; IsMember : Boolean; XPFilter : Integer;
      ModelFilter : TModelTarget; UpTextCheck : Boolean; out Location : TPoint;
      out SelectedOption : Boolean; out ModelsTestedCount : Integer) : TPAModel;
    var MatchingModels : TPAModelArray;
    var index : Integer;
    var resultIndex : Integer;
    var resultValid : Boolean;
    var ModelMatches : TPointArray;
    var Box: TBox;
    var secondaryFollowed : Boolean;
    var ModelName : String;
    var ModelUptext : String;
    begin
      ModelsTestedCount := 0;
      SelectedOption := False;
      SetColorToleranceSpeed(1); // 2
      MatchingModels := GetSearchOrderTPAModelList(HoundsOnly, FloorLevelFilter,
        SkillLevelFilter, IsMember);
      Result.XP := -1;
      Location.x := -1;
      Location.y := -1;
      secondaryFollowed := not ONSCREEN_TRACK_SECONDARY;
      resultValid := false;
      for index := 0 to Length(MatchingModels) - 1 do
      begin
        // see if done - stop on xp or model filter
        if ((MatchingModels[index].XP <= XPFilter) or (MatchingModels[index].ModelType
          = ModelFilter)) then
        begin
          if (secondaryFollowed) then
          begin
            break;
          end;
        end;
        // look
        ModelName := MatchingModels[Index].Descriptor;
        ModelUptext := MakeUptext(ModelName, MatchingModels[Index].Hound);
        // however Siphon Blood pool matches too many in Siphon Blood esswraith
        ModelMatches := FindModel(MatchingModels[index], EnvironmentalExcludes[FloorLevelFilter]);
        Inc(ModelsTestedCount);
        // check if we have matches
        //WriteLn('testing ' + MatchingModels[Index].Descriptor + ' got ' + IntToStr(Length(ModelMatches)) + ' matches');
        if (Length(ModelMatches) <= 0) then continue;
        // go through results
        for resultIndex := 0 to Length(ModelMatches) - 1 do
        begin
          if SkipClick(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y) then
          begin
            if (GetSystemTime - LastRightClick) < RandomRange(15000,20000) then continue;
            // check
            if UpTextCheck then
            begin
              MMouse(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y, 5, 5);
              sleep(10+random(60));
              if (WaitUpTextFuzzy('Walk here', MATCH_LOOSE, 80)) then
              begin
                // nope
                RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                continue;
              end;
              if (WaitUpTextFuzzy('Climb ladder', MATCH_LOOSE, 80)) then
              begin
                // nope
                RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                continue;
              end;
              if (WaitUpTextFuzzy('Use platform', MATCH_LOOSE, 80)) then
              begin
                // nope
                RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                continue;
              end;
              LastRightClick := GetSystemTime;
              ClickMouse2(mouse_right);
              if not WaitOptionFuzzy(ModelUptext, MATCH_GOOD, RandomRange(400,500)) then
              begin
                sleep(RandomRange(100,500));
                RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                continue;
              end;
              SelectedOption := true;
              // draw on screen
              if ONSCREEN_FOLLOW_MODELS then
              begin
                // clear previous
                Box.X1 := ModelMatches[resultIndex].x - 5;
                Box.Y1 := ModelMatches[resultIndex].y - 5;
                Box.X2 := ModelMatches[resultIndex].x + 5;
                Box.Y2 := ModelMatches[resultIndex].y + 5;
                if (MatchingModels[Index].Hound) then
                begin
                  //SMART_DrawBoxMS(False, Box, clLime);
                end
                else
                begin
                  //SMART_DrawBoxMS(False, Box, clYellow);
                end;
              end;
              Result := MakeTPAModelCopy(MatchingModels[index]);
              Location.x := ModelMatches[resultIndex].x;
              Location.y := ModelMatches[resultIndex].y;
              //Writeln('found ' + MatchingModels[Index].Descriptor);
              resultValid := true;
              break;
            end;
          end;
          if not resultValid and (MatchingModels[index].XP > XPFilter) then
          begin
            if UpTextCheck then
            begin
              MMouse(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y, 0, 0);
    //          writeln('uptext is [' + GetUpText + '] for [' + ModelName + ']');
              if WaitUpTextFuzzy(ModelUptext, MATCH_GOOD, 80) then
              begin
                // found uptext
              end
              else
              begin
                if (GetSystemTime - LastRightClick) < RandomRange(15000,20000) then
                begin
                  RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                  continue;
                end;
                MMouse(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y, 5, 5);
                sleep(10+random(60));
    //            writeln('uptext is [' + GetUpText + '] for [' + ModelName + ']');
                if (WaitUpTextFuzzy('Walk here', MATCH_LOOSE, 80)) then
                begin
                  // nope
                  RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                  continue;
                end;
                if (WaitUpTextFuzzy('Climb ladder', MATCH_LOOSE, 80)) then
                begin
                  // nope
                  RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                  continue;
                end;
                if (WaitUpTextFuzzy('Use platform', MATCH_LOOSE, 80)) then
                begin
                  // nope
                  RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                  continue;
                end;
                LastRightClick := GetSystemTime;
                ClickMouse2(mouse_right);
                if not WaitOptionFuzzy(ModelUptext, MATCH_GOOD, RandomRange(400,500)) then
                begin
                  sleep(RandomRange(100,500));
                  // we cannot record clickban, if for example skulls and blood
                  // pool match, recording clickban would prevent skulls from
                  // working, but we should test for the options
                  // RecordClickban(ModelMatches[resultIndex].x, ModelMatches[resultIndex].y);
                  continue;
                end;
                SelectedOption := true;
              end;
              // draw on screen
              if ONSCREEN_FOLLOW_MODELS then
              begin
                // clear previous
                Box.X1 := ModelMatches[resultIndex].x - 5;
                Box.Y1 := ModelMatches[resultIndex].y - 5;
                Box.X2 := ModelMatches[resultIndex].x + 5;
                Box.Y2 := ModelMatches[resultIndex].y + 5;
                if (MatchingModels[Index].Hound) then
                begin
                  //SMART_DrawBoxMS(False, Box, clLime);
                end
                else
                begin
                  //SMART_DrawBoxMS(False, Box, clYellow);
                end;
              end;
              Result := MakeTPAModelCopy(MatchingModels[index]);
              Location.x := ModelMatches[resultIndex].x;
              Location.y := ModelMatches[resultIndex].y;
              //Writeln('found ' + MatchingModels[Index].Descriptor);
              resultValid := true;
              break;
            end;
            // draw on screen
            if ONSCREEN_FOLLOW_MODELS then
            begin
              // clear previous
              Box.X1 := ModelMatches[resultIndex].x - 5;
              Box.Y1 := ModelMatches[resultIndex].y - 5;
              Box.X2 := ModelMatches[resultIndex].x + 5;
              Box.Y2 := ModelMatches[resultIndex].y + 5;
              if (MatchingModels[Index].Hound) then
              begin

  13. #1288
    Join Date
    Mar 2012
    Location
    Canada
    Posts
    442
    Mentioned
    4 Post(s)
    Quoted
    67 Post(s)

    Default

    cont again lol...
    Code:
     //SMART_DrawBoxMS(False, Box, clLime);
              end
              else
              begin
                //SMART_DrawBoxMS(False, Box, clYellow);
              end;
            end;
            Result := MakeTPAModelCopy(MatchingModels[index]);
            Location.x := ModelMatches[resultIndex].x;
            Location.y := ModelMatches[resultIndex].y;
            //Writeln('found ' + MatchingModels[Index].Descriptor);
            resultValid := true;
            break;
          end
          else
          begin
            if ((MatchingModels[index].XP < XPFilter) or (XPFilter <= 0)) then
            begin
              if not secondaryFollowed then
              begin
                secondaryFollowed := true;
                // clear previous
                Box.X1 := ModelMatches[resultIndex].x - 5;
                Box.Y1 := ModelMatches[resultIndex].y - 5;
                Box.X2 := ModelMatches[resultIndex].x + 5;
                Box.Y2 := ModelMatches[resultIndex].y + 5;
                if (MatchingModels[Index].Hound) then
                begin
                  //SMART_DrawBoxMS(False, Box, clBlue);
                  //writeln('Marking ' + MatchingModels[Index].Descriptor + ' at ' + IntToStr(ModelMatches[resultIndex].x) + ', ' + IntToStr(ModelMatches[resultIndex].y));
                end
                else
                begin
                  //SMART_DrawBoxMS(False, Box, clAqua);
                  //writeln('Marking ' + MatchingModels[Index].Descriptor);
                end;
              end;
              break;
            end;
          end;
        end;
        if resultValid and secondaryFollowed then break;
      end;
      SetColorToleranceSpeed(1);
    end;
    
    function CheckIfWeCare(X1, Y1, X2, Y2 : Integer) : Boolean;
    var Index : Integer;
    var MAXX : Integer;
    var indexX, indexY : Integer;
    var mapType : Integer;
    begin
      MAXX := Length(MAPPOINTS);
      // until we find overlap assume false
      Result := False;
      // check overhead
      if (X1 > MSCX - 35) and (X2 < MSCX + 35) and (Y2 < 70) then Exit;
      // check left side runespan info
      if (X2 < 75) and (Y1 < 145) then Exit;
      // check if counters
      if (X2 + 40 > MSX2) and (Y1 < 80) then Exit;
      // now we should check data
      for Index := 0 to MAXX - 1 do
      begin
        indexX := MAPPOINTS[Index].x;
        indexY := MAPPOINTS[Index].y;
        mapType := MAP[indexX][indexY];
        if (mapType = MAP_TYPE_OUTSIDE) or (mapType = MAP_TYPE_UNTESTED) then continue;
        // ok we are looking at square that is either inside or border, now do some testing
        if (X1 >= MAP3D[indexX][indexY].X2) then continue;
        if (Y1 >= MAP3D[indexX][indexY].Y2) then continue;
        if (X2 <= MAP3D[indexX][indexY].X1) then continue;
        if (Y2 <= MAP3D[indexX][indexY].Y1) then continue;
        // we may have overlap but the first hit is fine
        Result := True;
        Exit;
      end;
    end;
    
    var
      StartTime : Integer;
      PreemptCounter : Integer;
      CurrentTPAModel : TPAModel;
    
      // runes
      DTM_RUNE : array [TYPE_RUNE_MIN .. TYPE_RUNE_MAX] of Integer;
      // rune counters
      COUNTER_RUNE : array [TYPE_RUNE_MIN .. TYPE_RUNE_MAX] of Integer;
      // points
      COUNTER_POINTS_INITIAL : Integer;
      COUNTER_POINTS : Integer;
    
      DTM_FIRST_FLOOR : Integer;
      DTM_SECOND_FLOOR : Integer;
      DTM_THIRD_FLOOR : Integer;
      DTM_RUNESPHERE : Integer;
    
      DTM_YELLOW_WIZARD : Integer;
    
    // globals
    var SkillLevel : Integer;
    
    function IsRuneSphere : Boolean;
    var x, y : Integer;
    begin
      // nope not until we actually care
      Result := FindDTM(DTM_RUNESPHERE, x, y, 4, 130, 64, 190);
    end;
    
    var LastFloor : Integer;
    var LastCheck : Integer;
    // returns 1, 2, 3 or 0 if not in runespan
    function GetFloorLevel : Integer;
    var X, Y : Integer;
    begin
      if (LastFloor > 0) then
      begin
        // check reset
        if (GetSystemTime - LastCheck) < 5000 then
        begin
          // eh
          Result := LastFloor;
          exit;
        end;
      end;
      LastCheck := GetSystemTime;
      Result := 0;
      LastFloor := 0;
      if FindDTM(DTM_THIRD_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 3;
        LastFloor := 3;
        Exit;
      end;
      if FindDTM(DTM_SECOND_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 2;
        LastFloor := 2;
        Exit;
      end;
      if FindDTM(DTM_FIRST_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 1;
        LastFloor := 1;
        Exit;
      end;
    end;
    
    function FloorToString : String;
    begin
      case (GetFloorLevel) of
        3: Result := 'Highest level RuneSpan';
        2: Result := 'Middle level RuneSpan';
        1: Result := 'Lowest level RuneSpan';
        0: Result := 'We are not in RuneSpan';
      end;
    end;
    
    function IsInRuneSpan : Boolean;
    var i : Integer;
    var SkillGuideDTM : Integer;
    var x, y : Integer;
    begin
      // see what floor we are
      Result := (GetFloorLevel > 0);
      if (Result) then Exit;
      WriteLn('FLOOR NOT KNOWN');
      // check skill guide
      SkillGuideDTM := DTMFromString('mwQAAAHic42RgYHjKxMDwAogfA/EjKBsk9gCIXwPxMyAWA6oTAWJhIJYCYkkglmaAiIOwABD/2aDLcHeKOsODGdoMfZFSYL4SCwuDoawsgxATE4OhigrYDEKYkQgMBwDkPRLC');
      for i := 1 to 3 do
      begin
        if (FindDTM(SkillGuideDTM, x, y, 710, 1, 760, 50)) then
        begin
          WriteLn('FOUND DTM');
          // close
          Mouse(x, y, 3, 3, true);
          WriteLn('FOUND DTM - clicked');
          Sleep(RandomRange(1000,3000));
          WriteLn('FOUND DTM - checking');
          Result := GetFloorLevel > 0;
          if (Result) then Exit;
          WriteLn('FOUND DTM - FAILED');
        end;
        sleep (RandomRange(300,500));
      end;
      WriteLn('AFTER FOUND DTM');
      // move out
      MMouse(300, 150, 80, 50);
      // double check
      for i := 0 to 50 do
      begin
        sleep (100);
        LastFloor := -1;
        Result := (GetFloorLevel > 0);
        if Result then exit;
      end;
    end;
    
    function EnterPortal : Boolean;
    var PortalDTM : Integer;
    var X, Y : Integer;
    var aFound : Extended;
    var retry : Integer;
    begin
      WriteLn('setting up to enter portal');
      Result := False;
      MakeCompass('W');
      SetAngle(SRL_ANGLE_LOW);
      PortalDTM := DTMFromString('mAAEAAHic42FgYMgB4mYgroDiciD2h+IAKM4E4hggngnEGUCcAsRRUDlXIHYGYg8gDgbiSCAOh8o7AfH0qg0MPf2TGfomTmFIycxkaO/qZejs6WdISk9nqGtsYZgweRpDeVUtnAaZKUICZiQRIwMAZkIctw==');
      retry := 30;
      repeat
        if FindDTMRotated(PortalDTM, x, y, 0, 20, 200, 200, -Pi/4, Pi/4, Pi/16, aFound) then break;
        Writeln('Not yet found');
        Dec(retry);
        if (retry <= 0) then
        begin
          FreeDTM(PortalDTM);
          // umm sorry
          Writeln('Sorry');
          Exit;
        end;
        sleep (50);
      until retry < 0;
      Writeln('Found portal: ' + IntToStr(x) + ', ' + IntToStr(y));
      FreeDTM(PortalDTM);
      // get in on highest level you can
      sleep(1000+random(100));
      MMouse(X, Y, 3, 3);
      sleep(50+random(100));
      ClickMouse2(False);
      sleep(2000+random(1000)); // wait to walk
      if not WaitOptionMulti(['Ente', 'nter', 'nte'], RandomRange(400,500)) then
      begin
        // sorry
        Writeln('option to enter not found');
        Exit;
      end;
      Writeln('option to enter portal found');
      Wait(4000+random(700));
      if (FindNPCChatText('High', Nothing)) then
      begin
        Writeln('High level entry found');
        TypeSend('3');
      end
      else
      begin
        Writeln('High level entry not found, perhaps insufficient skill level');
        TypeSend('2');
      end;
      // well that's about it
      Wait(9000 + Random(3000));
      WriteLn('going through welcome dialog');
      clickContinue(true, true);
      Wait(1000 + Random(1000));
      WriteLn('dialog finished');
      LastFloor := -1;
    
      WriteLn('Current Location: ' + FloorToString);
      Result := IsInRuneSpan;
    end;
    
    function GetBackToRuneSpan : Boolean;
    var CanUseFairy: Boolean;
    var HasEdgevilleTeleport : Boolean;
    var HasDraynorTeleport : Boolean;
    var HasHoodEquipped : Boolean;
    var HasHoodInventory : Boolean;
    var EdgeVilleDTM : Integer;
    var DraynorDTM : Integer;
    var HoodDTM : Integer;
    var edgeX, edgeY, drayX, drayY: Integer;
    var hoodX, hoodY: Integer;
    var RuneClear : Integer;
    begin
      // umm - no runes
      for RuneClear := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        RUNE_LastKnownPosition[RuneClear] := -1;
        COUNTER_RUNE[RuneClear] := 0;
      end;
    
      // not yet back
      Result := false;
      // some set up
      CanUseFairy := CAN_USE_FAIRY_RINGS;
      HasEdgevilleTeleport := false;
      HasDraynorTeleport := false;
      // check hood
      HoodDTM := DTMFromString('mwQAAAHic42RgYHBhYmBwAmJPIHYAYhsgtmWCiHsBsSsQPwOqewTETxkQ7DdA/BGI3wHxbSA2k1NhcLVVZ9BWlWBQlJRmkBOXYJASEWXwd9VhkOITZPDQMGAQAaojhBmJwHAAAMO+D1Y=');
      if (GetCurrentTab <> tab_Equip) then GameTab(tab_Equip);
      HasHoodEquipped := FindDTM(HoodDTM, hoodX, hoodY, 600, 190, 680, 265);
      Writeln('GOT ' + IntToStr(hoodX) + ' and ' + IntToSTr(Hoody));
      HasHoodInventory := false;
      if not HasHoodEquipped then
      begin
        WriteLn('Hood not equipped, checking inventory');
        if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
        HasHoodInventory := FindDTM(HoodDTM, hoodX, hoodY, MIX1, MIY1, MIX2, MIY2);
        Writeln('GOT inventory ' + IntToStr(hoodX) + ' and ' + IntToSTr(Hoody));
      end
      else
      begin
        WriteLn('Hood found equipped, can only have one, skipping inventory check');
      end;
      FreeDTM(HoodDTM);
    
      // hood makes simple
      if ((HasHoodEquipped) or (HasHoodInventory)) then
      begin
        // teleport
        MMouse(hoodX, hoodY, 5, 5);
        sleep(50+random(100));
        ClickMouse2(False);
        if WaitOptionMulti(['Tele', 'lepo', 'port'],RandomRange(400,500)) then
        begin
          WriteLn('Teleporting with hood...');
          // wait
          Wait(8000 + Random(3000));
          // done
          WriteLn('Ok we should be in teleported, now get inside');
          MakeCompass('W');
          SetAngle(SRL_ANGLE_LOW);
          // Enter Portal
          Result := EnterPortal();
          // done - either we are there or not
          exit;
        end;
      end;
    
      // spell setup
      if (not SpellSetup) then SetupSpells();
      ClickSpellMode(SpellMode_All);
      if Cast('lumbridge home teleport', False) then
      begin
        // wait
        wait(500 + Random(300));
        // edgeville
        EdgeVilleDTM := DTMFromString('mggAAAHicY2NgYFBhZGDQAGJtINYEYjkgVgTi2UC5uVA8CYqXAfHijTsZgsWlGHpmb2fg8vZgcCzKB9MiQDlsmBEHhgAAoZoL1A==');
        HasEdgevilleTeleport := FindDTM(EdgeVilleDTM, edgeX, edgeY, 240, 90, 360, 210);
        FreeDTM(EdgeVilleDTM);
        // draynor
        DraynorDTM := DTMFromString('mggAAAHicY2NgYDBhZGDQA2IrRgjbGEpfAspdhOIzQHwCiO8BcY+KCsMZbWOGYnVVMC0iLsvAyMjEIAKUw4YZcWAIAACSnArb');
        HasDraynorTeleport := FindDTM(DraynorDTM, drayX, drayY, 280, 180, 340, 240);
        FreeDTM(DraynorDTM);
      end
      else
      begin
        Writeln('Could not cast home teleport');
        exit;
      end;
      // check if we can tele using fairy
      if not HasEdgevilleTeleport then
      begin
        // nope we walk
        CanUseFairy := False;
      end;
      // for now FORCE walker
      CanUseFairy := False;
    
      // check if we can use fairy
      if (CanUseFairy) then
      begin
        // use it
        Mouse(edgeX, edgeY, 5, 5, true);
        // wait
        Wait(17000 + Random(6000));
        // done
        WriteLn('Ok we should be in Edgeville, but the rest is not yet done');
        exit;
      end
      else
      begin
        if not HasDraynorTeleport then
        begin
          // sorry you should go to draynor yourself and activate the lodestone
          Exit;
        end;
        // use it
        Mouse(drayX, drayY, 5, 5, true);
        // wait
        Wait(17000 + Random(6000));
        // done
        WriteLn('Ok we should be in Draynor, but the rest is not yet done');
        exit;
      end;
    
      // if we have succeeded, we ought to reset certain counters as, for example,
      // we will have lost all runes, and we couldn't read point balance.
      // why? because we may have been ported out in middle of long run, we better
      // then handle it properly
    
    end;
    
    function NeedMoreEssence : Boolean;
    begin
      // if we fall below 100 we need more
      Result := (COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 100);
    end;
    
    function DesperatelyNeedsEssence : Boolean;
    begin
      // if we fall below 25, we can replenish from floating essence
      Result := (COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 25);
    end;
    
    function GetInventoryCountFromDTM(DTM_IN : Integer; RUNE_TYPE : Integer) : Integer;
    var x,y : Integer;
    var startx, starty, rowsize, colsize, colnumber, rownumber: Integer;
    var indexx, indexy, index : Integer;
    var LastKnownPosition : Integer;
    begin
      // init
      Result := 0;
    
      // look for runes
      if not FindDTM(DTM_IN, x, y, MIX1, MIY1, MIX2, MIY2) then
      begin
        // see if we have last known position
        LastKnownPosition := RUNE_LastKnownPosition[RUNE_TYPE];
        if (LastKnownPosition <= 0) then exit;
        // try to get last known position
        Result := GetAmountBox(InvBox(LastKnownPosition));
        exit;
      end;
      AreaInfo('inv', startx, starty, rowsize, colsize, colnumber, rownumber);
      indexx := (x - startx) div colsize;
      indexy := (y - starty) div rowsize;
      if (indexx >= colnumber) then Exit;
      if (indexy >= rownumber) then exit;
      // get new X/Y
      LastKnownPosition := 1 + indexx + (colnumber * indexy);
      (*
      x := startx + (rowSize * indexx);
      y := starty + (colsize * indexy);
      Result := GetAmountBox(IntToBox(x,y, x + rowSize, y + ((colSize * 3) shr 2)));
      *)
      Result := GetAmountBox(InvBox(LastKnownPosition));
      if (Result > 0) then
      begin
        for index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
        begin
          if index = RUNE_TYPE then continue;
          if (RUNE_LastKnownPosition[index] = LastKnownPosition) then
          begin
            // nope
            RUNE_LastKnownPosition[index] := -1;
          end;
        end;
        RUNE_LastKnownPosition[RUNE_TYPE] := LastKnownPosition;
      end
    end;
    
    procedure RandomWalkIsland;
    var maxX : Integer;
    var maxY : Integer;
    var excludeX : Integer;
    var excludeY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var TotalGreenCount : Integer;
    var Target : Integer;
    begin
      WriteLn('Walking a little');
      UpdateMap;
      DrawMap;
      maxX := Length(MAP);
      maxY := Length(MAP[0]);
      excludeX := 1 + (maxX shr 1);
      excludeY := 1 + (maxY shr 1);
      // count
      TotalGreenCount := 0;
      for indexX := 0 to maxX - 1 do
      begin
        for indexY := 0 to maxY - 1 do
        begin
          if (indexX = excludeX) and (indexY = excludeY) then
          begin
            continue;
          end;
          if (MAP[indexX][indexY] = MAP_TYPE_ISLAND) then
          begin
            Inc(TotalGreenCount);
          end;
        end;
      end;
      // check - cannot walk if nothing found
      if (TotalGreenCount < 1) then Exit;
      if (TotalGreenCount = 1) then
      begin
        Target := 0;
      end
      else
      begin
        Target := Random(TotalGreenCount - 1);
      end;
      WriteLn(' island square count: ' + IntToStr(TotalGreenCount) + ', selected square ' + IntToStr(Target));
      TotalGreenCount := 0;
      for indexX := 0 to maxX - 1 do
      begin
        for indexY := 0 to maxY - 1 do
        begin
          if (indexX = excludeX) and (indexY = excludeY) then
          begin
            continue;
          end;
          if (MAP[indexX][indexY] = MAP_TYPE_ISLAND) then
          begin
            // check
            if (TotalGreenCount = Target) then
            begin
              // found target
              Mouse(MMX1 + 4 + (indexX shl 3), MMY1 + 4 + (indexY shl 3), 4, 4, true);
              sleep (RandomRange(800,1200));
              while (IsMoving) do;
              UpdateMap();
              DrawMap();
              exit;
            end;
            Inc(TotalGreenCount);
          end;
        end;
      end;
    end;
    
    function DoIncremental(RUNE_TYPE : Integer; DTM_IN : Integer; Initial : Boolean; Current : Integer; StatsVar : String) : Integer;
    var Difference : Integer;
    begin
      Result := GetInventoryCountFromDTM(DTM_IN, RUNE_TYPE);
      if (Initial) then exit;
      // check difference
      Difference := Result - Current;
      if (Difference > 0) then
      begin
        stats_IncVariable(StatsVar, Difference);
      end;
    end;
    
    function GetSmartValue(NewValue, OldValue : Integer) : Integer;
    begin
      // check - shouldn't drop by more than few
      if (NewValue + 5 > OldValue) then
      begin
        Result := NewValue;
        exit;
      end;
      Result := OldValue - 5;
      if Result < 0 then Result := 0;
    end;
    
    procedure RefreshRuneCounters(Initial : Boolean; Filter : Integer);
    var Points : Integer;
    var PointString : String;
    var Index : Integer;
    begin
      // check inventory
      if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
    
      // rune counters
      if (Filter > TYPE_RUNE_MAX) or (Filter < TYPE_RUNE_MIN) then
      begin
        // all counters
        PointString := GetTextAtExWrap(50, 78, 95, 92, 0, 1, 1, 16777215, 0, StatChars);
        Points := StrToIntDef(GetNumbers(PointString), 0)
        if (Initial) then
        begin
          COUNTER_POINTS := Points;
          COUNTER_POINTS_INITIAL := Points;
        end
        else
        begin
          if (Points > COUNTER_POINTS) then
          begin
            //stats_IncVariable(STATS_POINTS, Points - CURRENT_POINTS);
            COUNTER_POINTS := Points;
          end;
        end;
        for Index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
        begin
          COUNTER_RUNE[Index] := GetSmartValue(DoIncremental(Index, DTM_RUNE[Index], Initial, COUNTER_RUNE[Index], STATS_RUNE[Index]), COUNTER_RUNE[Index]);
        end;
      end
      else
      begin
        // filter counter
        COUNTER_RUNE[Filter] := GetSmartValue(DoIncremental(Filter, DTM_RUNE[Filter], Initial, COUNTER_RUNE[Filter], STATS_RUNE[Filter]), COUNTER_RUNE[Filter]);
      end;
    end;
    
    function GetSomeEssence : Boolean;
    var tryThisDTM : Integer;
    var tryThisDTM2 : Integer;
    var tryThisDTM3 : Integer;
    var x, y : Integer;
    var E: Extended;
    var counter : Integer;
    var Found : Boolean;
    begin
      Writeln('getting essence');
      if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
      tryThisDTM := DTMFromString('mlwAAAHicY2dgYLjIyMBwBYjPAfFJID4NxHeB+CEQ7wDKbwfiPVC8FYgPA/FBIE5LTWKIi4liCA0JZDA3M2WIiYpgCPD3ZbC3s2UQAcrjwox4MBQAAFmAEhg=');
      tryThisDTM2 := DTMFromString('mggAAAHicY2NgYLgMxOeB+CwQ30DiLwbiFUC8BIiXAvFyIF4IxEmJcQwO9rYMPt4eDH6+3gxenm4MkRGhDCJAOWyYEQeGAAAm0g7L');
      tryThisDTM3 := DTMFromString('mggAAAHicY2NgYPgOxF+A+CMQv4XSH4B4ExCvA+KtQLwFiDcA8Vog9vJ0Y/Dx9mCwtrJgMNDXY3B3c2GwtbFiEAHKYcOMODAEAABVLg7L');
    
      for counter := 1 to 30 do
      begin
        Found := FindDTMRotated(tryThisDTM, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if not Found then Found := FindDTMRotated(tryThisDTM2, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if not Found then Found := FindDTMRotated(tryThisDTM3, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if Found then
        begin
          Mouse(x, y, 2, 2, true);
          if (DidRedClick) then
          begin
            while IsMoving do;
          end;
        end;
        RefreshRuneCounters(false, TYPE_RUNE_ESSENCE);
        if not DesperatelyNeedsEssence then break;
        // random walk island
        WriteLn('Walking for essence');
        RandomWalkIsland();
        // check
        if not IsInRuneSpan then break;
      end;
      FreeDTM(tryThisDTM);
      FreeDTM(tryThisDTM2);
      FreeDTM(tryThisDTM3);
      // check if we got essence - for now assume success
      Result := not DesperatelyNeedsEssence;
    end;
    
    function InitialSetUp : Boolean;
    var X, Y : Integer;
    var NagScreenDTM : Integer;
    begin
      LogSmartLine('Checking for Mad Day nag screen...');
      NagScreenDTM := DTMFromString('mlwAAAHicY2dgYPjMyMDwAYi/QjGI/Q6IfwGxCFBeEIrFoVgEir9ut2H4czKV4f1GO4YUGxGG7iBZhiBFRYa09HS4GmyYEQ+GAgDZGxE5');
      if FindDTM(NagScreenDTM, X, Y, 480, MSY1, MSX2, 30) then
      begin
        // found nag screen, we are user so we kinda wait
        Wait(500 + Random(500));
        Mouse(x, y, 2, 2, true);
      end;
      FreeDTM(NagScreenDTM);
      // ok wait for nag screen to go away
      Wait(500 + Random(500));
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Checking for randoms...');
      // now do randoms
      FindNormalRandoms;
      // ok wait just in case the SRL closed some other screen
      Wait(500 + Random(500));
      AppendSmartLine('DONE', True)
    
      LogSmartLine('Our location: ' + FloorToString);
      Result := true;
      // check if we are in runespan
      if not IsInRuneSpan then
      begin
        LogSmartLine('Getting back to RuneSpan');
        // get back to runespan
        Result := GetBackToRuneSpan();
        // see if we failed
        if not Result then
        begin
          LogSmartLine('We are not in RuneSpan and we kinda failed to get back.');
          Exit;
        end;
      end;
      LogSmartLine('Checking inventory...');
      // woo hoo
      RefreshRuneCounters(true, TYPE_RUNE_NONE);
      // just because it is easier to find essence for example
      SetAngle(SRL_ANGLE_HIGH);
      AppendSmartLine('DONE', True);
      // get more essences if needed
      if DesperatelyNeedsEssence then
      begin
        LogSmartLine('Obtaining essence');
        Result := GetSomeEssence();
      end;
    end;
    
    (*
    
    Wizard without being told - the startup yellow wizard:
    
    right click - Talk to Wizard
    continue
    This is tricky, but I am getting the hang of it
    Continue
    You need to wait for a wizard to call
    continue
    *)
    
    
    var XPTimeout : Integer;
    var GuaranteedClick : Boolean;
    var LastXPTotal : integer;
    var LastOKTime : Integer;
    
    procedure WriteStats;
    var XPTotal : Integer;
    var XPPerHour : Integer;
    var XPDiff : Integer;
    var PointsTotal : Integer;
    var PointsPerHour : Integer;
    var SecondsElapsed : Integer;
    var SmartLines2: TStringArray;
    var RunesphereEvent: Boolean;
    var XPT : String;
    var XPTf : Integer;
    begin
      // get total xp tick
      XPTotal := GetXPBarTotal - Players[CurrentPlayer].Integers[0];
      PointsTotal := COUNTER_POINTS - COUNTER_POINTS_INITIAL;
      if (XPTotal > 0) then
      begin
        XPDiff := XPTotal - LastXPTotal;
        if (XPDiff) > 0 then
        begin
          stats_IncVariable(STATS_EXP_TOTAL, XPDiff);
          stats_IncVariable(STATS_EXP_RUNECRAFTING, XPDiff);
          WriteLn('Incrementing variables by ' + IntToStr(XPDiff));
    
          if (XPTotal > 0) then
          begin
            LastXPTotal := XPTotal;
            WriteLn('XP Total Diff = ' + IntToStr(XPDiff));
          end;
        end;
      end;
      // plus one - just to be sure we don't divide by zero
      SecondsElapsed := (1 + ((Getsystemtime-StartTime)/1000));
      // mind order of execution, these are integers
      XPPerHour := (3600 * XPTotal) / SecondsElapsed;
      PointsPerHour := (3600 * PointsTotal) / SecondsElapsed;
      RunesphereEvent := IsRuneSphere;
    
      if (XPTimeout > 999) then
      begin
        XPT := IntToStr(XPTimeout div 1000) + '.';
      end
      else
      begin
        XPT := '0.';
      end;
      XPTf := XPTimeout mod 1000;
      XPT := XPT + IntToStr(XPTf div 100);
      XPTf := XPTf mod 100;
      XPT := XPT + IntToStr(XPTf div 10);
      XPT := XPT + IntToStr(XPTf mod 10);
    
      if not KEEP_DEBUG then ClearDebug;
      WriteLn('============Rough Stats etc...============');
      WriteLn('Preemptible RuneSpan v. ' + VERSION);
      WriteLn('Time Running: ' + TimeRunning);
      WriteLn('XP Earned: ' + IntToStr(XPTotal) + ', per hour: ' + IntToStr(XPPerHour));
      WriteLn('Points Earned: ' + IntToStr(PointsTotal) + ', per hour: ' + IntToStr(PointsPerHour));
      WriteLn('Preempted Counter: ' + IntToStr(PreemptCounter));
      if (CurrentTPAModel.XP > 0) then
      begin
        WriteLn('Current Target: ' + CurrentTPAModel.Descriptor + ', ' + IntToStr(CurrentTPAModel.XP) + 'XP, level ' +
          IntToStr(CurrentTPAModel.MinimumLevel) + ', XPTIMEOUT ' + XPT + 's');
      end;
      WriteLn('Current Runecrafting Level: ' + IntToStr(SkillLevel));
      WriteLn('Essences Available: ' + IntToStr(COUNTER_RUNE[TYPE_RUNE_ESSENCE]));
      WriteLn('Current Floor: ' + FloorToString);
      if (RunesphereEvent) then WriteLn('RUNESPHERE is active!!!');
      WriteLn('==========================================');
    
      if (ONSCREEN_STATS) then
      begin
        SetArrayLength(SmartLines2, 9);
        SmartLines2[0] := '= Preemptible RuneSpan version ' + VERSION + ' =';
        SmartLines2[1] := 'Time Running: ' + TimeRunning;
        SmartLines2[2] := 'XP Earned: ' + IntToStr(XPTotal) + ', Per hour: ' + IntToStr(XPPerHour);
        SmartLines2[3] := 'Points Earned: ' + IntToStr(PointsTotal) + ', Per hour: ' + IntToStr(PointsPerHour);
        SmartLines2[4] := 'Preempted Counter: ' + IntToStr(PreemptCounter);
        if (CurrentTPAModel.XP > 0) then
        begin
          SmartLines2[5] := 'Current Target: ' + CurrentTPAModel.Descriptor + ', ' + IntToStr(CurrentTPAModel.XP) + 'XP, level ' + IntToStr(CurrentTPAModel.MinimumLevel) + ', XPTIMEOUT ' + XPT + 's';
        end
        else
        begin
          SmartLines2[5] := 'no target';
        end;
        SmartLines2[6] := 'Current Runecrafting Level: ' + IntToStr(SkillLevel);
        SmartLines2[7] := 'Essences Available: ' + IntToStr(COUNTER_RUNE[TYPE_RUNE_ESSENCE]);
        SmartLines2[8] := '======';
        if (RunesphereEvent) then
        begin
          SmartLines2[8] := '== RUNESPHERE IS ACTIVE ==';
        end;
        PrintOnSmart(SmartLines2,Point(15,225),clYellow);
      end;
      // commit
      Stats_Commit;
    end;
    
    const XPTIMEOUT_NODEBASE = 9000;
    const XPTIMEOUT_HOUNDBASE = 11000; // one - they're slower, two - we can detect their demise
    procedure CalculateXPTimeout;
    var DecrementAmount : Integer;
    begin
      XPTimeout := XPTIMEOUT_NODEBASE;
      if (CurrentTPAModel.Hound) then XPTimeout := XPTIMEOUT_HOUNDBASE;
      DecrementAmount := 2000;
      if (USER_IS_ADD) then DecrementAmount := 5000;
      if (SkillLevel > CurrentTPAModel.MinimumLevel) then
      begin
        if (SkillLevel > 2 * CurrentTPAModel.MinimumLevel) then
        begin
          XPTimeout := XPTimeout - DecrementAmount;
        end
        else
        begin
          XPTimeout := XPTimeout - ((DecrementAmount * (SkillLevel - CurrentTPAModel.MinimumLevel)) / CurrentTPAModel.MinimumLevel);
        end;
      end;
    end;
    
    // only for hounds
    function DO_SIPHON : Boolean;
    var EntryXP : Integer;
    var xpTick : Integer;
    var PrevRuneEssence : Integer;
    var NewModel : TPAModel;
    var TPLocation : TPoint;
    var SelectedOption : Boolean;
    var MapRefreshed : Boolean;
    var ModelsTestCount : Integer;
    var ModelsTestTimer : Integer;
    var ModelsTimerString : String;
    var EntryTime : Integer;
    var FloorLevel : Integer;
    var ModelGuaranteed : Boolean;
    var ModelTimer : Integer;
    var ModelXP : Integer;
    var CurrentXP : Integer;
    begin
      WriteLn('in siphon');
      CurrentXP := GetXPBarTotal;
      ModelGuaranteed := GuaranteedClick;
      ModelTimer := GetSystemTime;
      ModelXP := CurrentXP;
      MapRefreshed := False;
      Result := False;
      // show stats
      CalculateXPTimeout;
      //SMART_ClearCanvas();
      WriteStats;
      // initial set up
      RefreshRuneCounters(False, TYPE_RUNE_ESSENCE);
      PrevRuneEssence := COUNTER_RUNE[TYPE_RUNE_ESSENCE];
      EntryXP := CurrentXP;
    
      while (IsMoving) do;
    
      sleep (RandomRange(1000,2000)); // just to get animation started
      LastOKTime := GetSystemTime;
      EntryTime := GetSystemTime;
      while ((LastOKTime + XPTimeout) > GetSystemTime) do
      begin
        // prevent timeout
        if (EntryTime + 120000 < GetSystemTime) then
        begin
          WriteLn('Checking stuff to prevent timeout');
          HoverSkill(SKILL_RUNECRAFTING, False);
          sleep(RandomRange(200, 1500));
          EntryTime := GetSystemTime;
          WriteLn('Prev essence count: ' + IntToStr(PrevRuneEssence));
        end;
        // get xp tick
        CurrentXP := GetXPBarTotal;
        xpTick := CurrentXP - EntryXP;
        EntryXP := CurrentXP;
        // reset ok time
        if ((xpTick) > 0) then
        begin
          ModelXP := ModelXP + xpTick;
          GuaranteedClick := true;
          // Writeln ('Incremental xp: ' + IntToStr(xpTick));
          LastOKTime := GetSystemTime;
          if not MapRefreshed then
          begin
            if (Random(5)) = 1 then CompassMovement(-50, 60, false);
            MapRefreshed := True;
            UpdateMap();
            DrawMap();
          end;
          Result := True;
        end;
        // check
        if not GuaranteedClick then
        begin
          WriteLn('not guaranteed');
          // only 1/2 of timeout
          if (LastOKTime + (XPTimeout shr 1)) < GetSystemTime then
          //if (LastOKTime + ((XPTimeout + (XPTimeout shl 1)) shr 2)) > GetSystemTime then
          begin
            WriteLn('Exiting siphon due to unguaranteed click and early timeout ' + IntToStr(GetSystemTime - LastOKTime));
            Exit;
          end;
        end;
        // check exit condition - hound broken down
        RefreshRuneCounters(False, TYPE_RUNE_ESSENCE);
        if (PrevRuneEssence > COUNTER_RUNE[TYPE_RUNE_ESSENCE]) then
        begin
          // check for reasonable drain
          if (PrevRuneEssence - COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 3) then
          begin
            // ok
            LastOKTime := GetSystemTime;
          end;
        end;
        if (PrevRuneEssence < COUNTER_RUNE[TYPE_RUNE_ESSENCE]) then
        begin
          // creature broken down
          WriteLn('Creature broken down.');
          If not CurrentTPAModel.Hound then
          begin
            WriteLn('...but... we did not think it was hound');
          end
          else
          begin
            if ModelGuaranteed then
            begin
              xpTick := GetXPBarTotal - EntryXP;
              if xpTick > 0 then
              begin
                ModelXP := ModelXP + xpTick;
              end;
              WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
            end;
          end;
          Result := True;
          WriteLn('Exiting siphon due to broken creature');
          LastRightClick := GetSystemTime - 30000; // to immediately allow right click
          Exit;
        end;
        // adjust
        PrevRuneEssence := COUNTER_RUNE[TYPE_RUNE_ESSENCE];
    
        // we can loop - first see if we can get better target
        if (NeedMoreEssence) then
        begin
          // just to be sure
          if not IsInRuneSpan then
          begin
            Result := True;
            WriteLn('Exiting siphon due to not in runespan');
            Exit;
          end;
          WriteLn('need more essence');
          if (not CurrentTPAModel.Hound) then
          begin
            if ModelGuaranteed then
            begin
              xpTick := GetXPBarTotal - EntryXP;
              if xpTick > 0 then
              begin
                ModelXP := ModelXP + xpTick;
              end;
              WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
            end;
            // we got to get out of this
            Result := true;
            WriteLn('Exiting siphon due to essence count');
            Exit;
          end;
          // just sleep
          sleep(RandomRange(1000,2000));
          continue;
        end;
        // we don't need more essence yet
        FloorLevel := GetFloorLevel;
        if (FloorLevel <= 0) then
        begin
          if not IsInRuneSpan() then Exit;
          FloorLevel := GetFloorLevel;
        end;
        ModelsTestTimer := GetSystemTime;
        NewModel := FindTarget(False, FloorLevel, SkillLevel, Players[0].Member,
          0, CurrentTPAModel.ModelType, true, TPLocation, SelectedOption,
          ModelsTestCount);
        ModelsTestTimer := GetSystemTime - ModelsTestTimer;
        // check
        if (ModelsTestCount > 0) then
        begin
          ModelsTestTimer := (ModelsTestTimer * 100) / ModelsTestCount;
          ModelsTimerString := IntToStr(ModelsTestTimer div 100) + '.';
          ModelsTestTimer := ModelsTestTimer mod 100;
          ModelsTimerString := ModelsTimerString + IntToStr(ModelsTestTimer div 10) + IntToStr(ModelsTestTimer mod 10);
          if (LOG_LONG_SEARCHES) then WriteLn('Pre-empt routine tested ' + IntToStr(ModelsTestCount) + ' model(s) at ' +
            ModelsTimerString + 'ms each');
        end;
        if (NewModel.XP < 0) then
        begin
          // wait a little
          if (ModelsTestTimer < 800) then
          begin
            Sleep(RandomRange(100, 1000 - ModelsTestTimer));
          end;
          continue;
        end;
        // check
        if SelectedOption then
        begin
          CurrentXP := GetXPBarTotal;
          if ModelGuaranteed then
          begin
            xpTick := GetXPBarTotal - EntryXP;
            if xpTick > 0 then
            begin
              ModelXP := ModelXP + xpTick;
            end;
            WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
          end;
          ModelGuaranteed := true;
          ModelXP := CurrentXP;
          ModelTimer := GetSystemTime;
          GuaranteedClick := true;
        end
        else
        begin
          GuaranteedClick := false;
          MMouse(TPLocation.x, tpLocation.y, 2, 2);
          if (WaitUpTextFuzzy('Walk here', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          if (WaitUpTextFuzzy('Climb ladder', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          if (WaitUpTextFuzzy('Use platform', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          // check if node
          if (NewModel.Hound) then
          begin
            ClickMouse2(mouse_left);
            GuaranteedClick := false;
            // GuaranteedClick := DidRedClick;
            // if not (GuaranteedClick) then continue;
          end
          else
          begin
            // nodes don't move
            if (GetSystemTime - LastRightClick) < RandomRange(15000,20000) then
            begin
              ClickMouse2(mouse_left);
              // not guaranteed
              // GuaranteedClick := DidRedClick;
              // if not (GuaranteedClick) then continue;
              GuaranteedClick := false;
            end
            else
            begin
              LastRightClick := GetSystemTime;
              ClickMouse2(mouse_Right);
              if not WaitOptionFuzzy(MakeUptext(NewModel.Descriptor, NewModel.Hound),
                MATCH_GOOD, RandomRange(400,500)) then
              begin
                sleep(RandomRange(100,500));
                continue;
              end;
              GuaranteedClick := True;
            end;
          end;
          CurrentXP := GetXPBarTotal;
          if ModelGuaranteed then
          begin
            xpTick := GetXPBarTotal - EntryXP;
            if xpTick > 0 then
            begin
              ModelXP := ModelXP + xpTick;
            end;
            WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
          end;
          ModelGuaranteed := GuaranteedClick;
          ModelXP := CurrentXP;
          ModelTimer := GetSystemTime;
          GuaranteedClick := true;
        end;
        // just wait
        while (IsMoving) do;
        // we need to treat accounting correctly so don't assume start until now
        ModelTimer := GetSystemTime;
        // will refresh map and move camera
        MapRefreshed := false;
        Inc(PreemptCounter);
        CurrentTPAModel := NewModel;
        CalculateXPTimeout;
        if not GuaranteedClick then
        begin
          if (ModelsTestTimer < 800) then
          begin
            Sleep(RandomRange(100, 1000 - ModelsTestTimer));
          end;
        end;
        LastOKTime := GetSystemTime;
        //SMART_ClearCanvas();
        WriteStats;
        UpdateMap();
        DrawMap();
      end;
      WriteLn('Exiting siphon through normal loop ' + IntToStr(LastOKTime + XPTimeout) + ' vs ' + IntToStr(GetSystemTime));
    end;
    
    Procedure antiban;
    var
      I: Integer;
      CurrentCompassDegree: Extended;
    begin
      I := Random(1000);
      case (I) of
        0..99 :
          begin
            GameTab(tab_Stats);
            HoverSkill('runecrafting', False);
            GameTab(28);
          end;
        100..200 :
          begin
            SleepAndMoveMouse(7000 + Random(500));
            MakeCompass(CurrentCompassDegree-18+Random(32));
          end;
        200..210 :
          begin
            GameTab(Tab_Stats);
            MakeCompass(CurrentCompassDegree-2+Random(34));
            Wait(3000 + Random(500));
            GameTab(28);
          end;
        211..220 :
          begin
            BoredHuman;
            MakeCompass('N');
          end;
        221..300 :
          begin
          GameTab(Tab_Stats);
          Wait(100 + Random(1000));
          end;
      end;
    end;
    
    // hmm ok let's unify what result means - false - we couldn't do our work, true
    // means we did
    function MainLoop : boolean;
    var TPLocation : TPoint;
    var Looper : Integer;
    var DID_FIND : Boolean;
    var SelectedOption : Boolean;
    var ModelsTestCount : Integer;
    var ModelsTestTimer : Integer;
    var ModelsTimerString : String;
    var LastWasHound : Boolean;
    var StartTime : Integer;
    var FloorLevel : Integer;
    var NewSkillLevel : Integer;
    begin
      Result := True;
      if FindNormalRandoms then
      begin
        LastOKTime := GetSystemTime;
        SetAngle(SRL_ANGLE_HIGH);
      end;
      // check if we are in runespan
      if not IsInRuneSpan then
      begin
        FindNormalRandoms;
        if not LoggedIn() then
        begin
          Result := false;
          Exit;
        end;
        // get back to runespan
        Result := GetBackToRuneSpan();
        // see if we failed
        if not Result then
        begin
          WriteLn('We are not in RuneSpan and we kinda failed to get back.');
          Exit;
        end;
      end;
      // ok let's do it
      if (not Result) then exit;
      RefreshRuneCounters(false, TYPE_RUNE_NONE);
    
      // check
      if (SkillLevel < 1) then
      begin
        NewSkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
        if (NewSkillLevel > 0) then
        begin
          // save
          SkillLevel := NewSkillLevel;
        end
        else
        begin
          // ok just pretend this didn't happen for now but if longer than 3 min we
          // will have to end
          Result := true;
          // check if we are without xp for more than 3 minutes
          if (LastOKTime - GetSystemTime) > 180000 then
          begin
            Result := false;
          end
          else
          begin
            // waiting
            WriteLn('Waiting for skill info');
            Sleep(RandomRange(800, 2700));
          end;
          exit;
        end;
      end;
    
      // hopping
      //DoDTMHop2Main;
      DID_FIND := false;
      LastWasHound := CurrentTPAModel.Hound;
      StartTime := GetSystemTime;
      for Looper := 1 to 50 do
      begin
        ModelsTestTimer := GetSystemTime;
        FloorLevel := GetFloorLevel;
        if FloorLevel <= 0 then
        begin
          if not IsInRunespan() then break;
          FloorLevel := GetFloorLevel;
        end;
        CurrentTPAModel := FindTarget(NeedMoreEssence, FloorLevel, SkillLevel,
          Players[0].Member, 0, TARGET_NONE, true, TPLocation, SelectedOption,
          ModelsTestCount);
        ModelsTestTimer := GetSystemTime - ModelsTestTimer;
        if (ModelsTestCount > 0) then
        begin
          ModelsTestTimer := (ModelsTestTimer * 100) / ModelsTestCount;
          ModelsTimerString := IntToStr(ModelsTestTimer div 100) + '.';
          ModelsTestTimer := ModelsTestTimer mod 100;
          ModelsTimerString := ModelsTimerString + IntToStr(ModelsTestTimer div 10) + IntToStr(ModelsTestTimer mod 10);
          if LOG_LONG_SEARCHES then WriteLn('Main loop tested ' + IntToStr(ModelsTestCount) + ' model(s) at ' +
            ModelsTimerString + 'ms each');
        end;
        if (CurrentTPAModel.XP > 0) then
        begin
          if SelectedOption then
          begin
            DID_FIND := True;
            GuaranteedClick := True;
            writeln('Guaranteed click');
            Break;
          end;
          GuaranteedClick := False;
          MMouse(TPLocation.x, TPLocation.y, 2, 2);
          ClickMouse2(mouse_left);
          // GuaranteedClick := DidRedClick;
          DID_FIND := True;
          if (DID_FIND) then Break;
        end;
        // still looping?
        if LastWasHound then
        begin
          // assume 7 seconds recovery time
          if GetSystemTime - StartTime < 7000 then continue;
        end;
        if (Looper mod 3) = 0 then
        begin
          // random walk island
          WriteLn('Walking for target');
          RandomWalkIsland();
        end;
      end;
    
      while (IsMoving) do;
      UpdateMap;
      DrawMap;
      if (DID_FIND) then DID_FIND := DO_SIPHON;
    
      if (DID_FIND) then
      begin
        WriteLn('main loop done without finding target');
      end
      else
      begin
        WriteLn('main loop failed to find anything');
        if (Random(25) = 1) then
        begin
          CompassMovement(-50, -20, false);
          if (Random(4) = 1) then CompassMovement(2, 12, false);
        end
        else
        begin
          if (Random(10) = 1) then CompassMovement(5, 22, false);
        end;
        if (Random(18) = 1) then
        begin
          // walk a little
          RandomWalkIsland;
        end;
        if (Random(9) = 1) then
        begin
          SetAngle(SRL_ANGLE_LOW);
          CompassMovement(-40, 100, false);
          SetAngle(SRL_ANGLE_HIGH);
        end;
      end;
      // ensure that we don't have nag window
      IsInRuneSpan();
      // check if we are without xp for more than 3 minutes
      if (LastOKTime - GetSystemTime) > 180000 then
      begin
        Result := false;
      end;
    end;
    
    procedure TestModels;
    var DESC : TStringArray;
    var MatchingModels : TPAModelArray;
    var ModelMatches : TPointArray;
    var index : Integer;
    var resultIndex : Integer;
    var MyStart : Integer;
    var MyEnd : Integer;
    begin
      SetColorToleranceSpeed(1); // 2
      MatchingModels := GetSearchOrderTPAModelList(False, 3, 99, True)
      SetLength(DESC, 1);
      for index := 0 to Length(MatchingModels) - 1 do
      begin
        // look
        if // (MatchingModels[index].Descriptor <> 'Skulls')
        //and
        (MatchingModels[index].Descriptor <> 'Fleshy growth')
        and (MatchingModels[index].Descriptor <> 'Fire storm')
        //and (MatchingModels[index].Descriptor <> 'Bloody skulls')
        and (MatchingModels[index].Descriptor <> 'Undead soul')
    
        and (MatchingModels[index].Descriptor <> 'Soul esswraith')
        //(MatchingModels[index].Descriptor <> 'Blood esswraith')
         then continue;
        MyStart := GetSystemTime;
        ModelMatches := FindModel(MatchingModels[index], EnvironmentalExcludes[GetFloorLevel]);
        MyEnd := GetSystemTime;
        // check if we have matches
        Writeln('LOOKING FOR ' + MatchingModels[index].Descriptor + ' TOOK ' +
          IntToStr(MyEnd - MyStart) + 'ms and got ' + IntToStr(Length(ModelMatches)));
        if (Length(ModelMatches) <= 0) then continue;
        DESC[0] := MatchingModels[Index].Descriptor;
        // go through results
        for resultIndex := 0 to Length(ModelMatches) - 1 do
        begin
    
          if ModelMatches[resultIndex].x < 6 then continue;
          if ModelMatches[resultIndex].y < 6 then continue;
    
          break;
        end;
      end;
    end;
    
    procedure FreeMyDTMs;
    var Index : Integer;
    begin
      // runes
      for Index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        FreeDTM(DTM_RUNE[Index]);
      end;
    
      FreeDTM(DTM_FIRST_FLOOR);
      FreeDTM(DTM_SECOND_FLOOR);
      FreeDTM(DTM_THIRD_FLOOR);
      FreeDTM(DTM_RUNESPHERE);
    
      FreeDTM(DTM_YELLOW_WIZARD);
    end;
    
    procedure ClearClickban;
    var i : integer;
    begin
      for i := 0 to CLICKBAN_MAX_AREAS - 1 do
      begin
        CLICKBANS[i].X := -1;
        CLICKBANS[i].Y := -1;
      end;
    end;
    
    procedure RecordClickbanEX(RasterX, RasterY : Integer);
    var Index : Integer;
    var CBCount : Integer;
    begin
      // check
      for Index := 0 to CLICKBAN_MAX_AREAS - 1 do
      begin
        if CLICKBANS[Index].x < 0 then
        begin
          CBCount := Index;
    
          break;
        end;
        if CLICKBANS[Index].x <> RasterX then continue;
        if CLICKBANS[Index].y <> RasterY then continue;
        // already exists
        exit;
      end;
      // move out
      if CBCount = CLICKBAN_MAX_AREAS - 1 then
      begin
        // watch it buster
        Dec(CBCount);
      end;
      for Index := CBCount - 1 downto 0 do
      begin
        CLICKBANS[Index + 1].x := CLICKBANS[Index].x;
        CLICKBANS[Index + 1].y := CLICKBANS[Index].y;
      end;
      CLICKBANS[0].X := RasterX;
      CLICKBANS[0].Y := RasterY;
    end;
    
    procedure RecordClickban(X, Y : Integer);
    var RecordXPlus : Boolean;
    var RecordYPlus : Boolean;
    var RasterX : Integer;
    var RasterY : Integer;
    begin
      RasterX := (X - MSX1) shr 3;
      RasterY := (Y - MSY1) shr 3;
      RecordXPlus := false;
      RecordYPlus := false;
      if (RasterX < ((MSX2 - MSX1) shr 3)) then RecordXPlus := True;
      if (RasterY < ((MSY2 - MSY1) shr 3)) then RecordYPlus := True;
      RecordClickbanEX(RasterX, RasterY);
      if RecordYPlus then RecordClickBanEx(RasterX, RasterY + 1);
      if RecordXPlus then RecordClickBanEx(RasterX + 1, RasterY);
      if (RecordXPlus and RecordYPlus) then RecordClickBanEx(RasterX + 1, RasterY + 1);
      if (RasterX > 0) then
      begin
        RecordClickBanEX(RasterX - 1, RasterY);
        if RecordYPlus then RecordClickBanEx(RasterX - 1, RasterY + 1);
        if (RasterY > 0) then
        begin
          RecordClickBanEX(RasterX, RasterY - 1);
          RecordClickBanEX(RasterX - 1, RasterY - 1);
        end;
      end
      else
      begin
        if (RasterY > 0) then
        begin
          if RecordXPlus then RecordClickBanEx(RasterX + 1, RasterY - 1);
          RecordClickbanEX(RasterX, RasterY - 1);
        end;
      end;
    end;
    
    procedure WriteAccountingForCurrentModel(XPDiff : Integer; TimeDiff : Integer);
    begin
      writeln('Account for current model [' + CurrentTPAModel.Descriptor + '], adding ' +
        IntToStr(XPDiff) + ' XP in ' + IntToStr(TimeDiff) + ' ticks.');
    end;
    
    var i : integer;
    var runecraftingLevelsGained : Integer;
    var EverythingOk : Boolean;
    var TargetTime : Integer;
    var NewSkillLevel : Integer;
    begin
      DeclarePlayers;
    ;
      SmartSetRefresh(100);
      SetupSRL;
      SetupSRLStats(1037, SRLStats_User, SRLStats_Password);
      LoginPlayer;
      while not RSReady do Sleep(2000);
      SmartSetRefresh(60);
    
      ClearDebug();
      //SMART_ClearCanvas();
      WriteLn     ('**************************');
      LogSmartLine('** Preemptible RuneSpan **');
      WriteLn     ('**************************');
    
      LogSmartLine('Initializing density models and 3D engine...');
      // clear
      InitializeStatsArray();
      CurrentTPAModel.XP := -1;
      LastFloor := -1;
    
      // register TPA models
      RegisterTPAModels();
      Initialize3D();
      SetLength(CLICKBANS, CLICKBAN_MAX_AREAS);
      ClearClickban;
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Initializing inventory DTMs...');
      for i := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        RUNE_LastKnownPosition[i] := -1;
        COUNTER_RUNE[i] := 0;
      end;
    
      // runes
      DTM_RUNE[TYPE_RUNE_ESSENCE] := DTMFromString('maQEAAHicrcxNCsJADIbh6A2E9hJSf9pOsRXEotJOOxsRj+9CRUUE3QiufQmzEFzqwMPkS0J6IrLoimyxQYwUKzQoUMFi5q3hUGKJGifu3LHDHk+fj7jghYefXXHAGbePXmYSqeepsqXR37VWRsOBTIucbHQnn2SawzCQJB5rHUV9nQXc+VXnD77eGw4EIEI=');
    
      DTM_RUNE[TYPE_RUNE_AIR] := DTMFromString('mFQEAAHic42VgYJjFxMAwHYinAfFsIJ4LxHOAeAYQTwHiiUDcB8XzgHghED8G6nsExM+gGMS/D8UPkcTeAvE7qForC1OGjx8/Mly9epXhy5cvYPzu3TsGO1trBkd7O4bZs+sZ/H29GSLCQhg83FwYLMzNGESA+kjFjGRgFAAALvspiQ==');
      DTM_RUNE[TYPE_RUNE_MIND] := DTMFromString('mlwAAAHicY2dgYDjIxMBwHIp3Q/ERIN4PxI8ZIPgBFIPYL4H4ORC7ODsyvFihwWBlac7g7eXB8GqTNpgGYRGgPC7MiAdDAQDaHxOy');
      DTM_RUNE[TYPE_RUNE_WATER] := DTMFromString('mrAAAAHic42BgYDjAhMC7gPgYEJ8F4r1QNgcjAwMLEPMwQtggzAbEokAsAMT6pesZdMs3MFQt2cfg6eHG4OLsxKCtrcUQGR7KkBAXzSACtAMfZiSAYQAAHOAP7Q==');
      DTM_RUNE[TYPE_RUNE_EARTH] := DTMFromString('m6wAAAHic42ZgYMhiguBiIM4F4hQouxxKJwNxElTNU6D6RwwQ+jkQvwLi+0D8GIjfQzFI/h4QOznaM8yp8maojpZmWNURyxAaEgRm29laM3h5ujPExUYxRISFMHi4uTCIANUTixlJwEgAAPNjGpI=');
      DTM_RUNE[TYPE_RUNE_FIRE] := DTMFromString('m6wAAAHic42ZgYDjABMEHgXgfEB8F4r1AfASIt0PxSSA+BcQvgOofA/F9KH4AxE+B+BmU/RaI30PlvoXrMPy6Zs3wa78dg4O9KcP9q8EMXyptGX4GqTIEB/kzxERFMDg7OjAYGxkyiADVE4sZScBIAADNxCHK');
      DTM_RUNE[TYPE_RUNE_BODY] := DTMFromString('mggAAAHicY2NgYDjIxMBwBIiPAvEBIN4DxLuAmI0RgrmBmBdKg/jOTg4Mf//+ZTA0nc/Q1LqXwch0AUNp+U4GEaBZ2DAjDgwBAKWvDn0=');
      DTM_RUNE[TYPE_RUNE_COSMIC] := DTMFromString('mrAAAAHic42BgYHBgYmDwAGI/IHYFYjcgdgJiKyC2BGJ2RgQWBmJuIGYCYgEgZgXivz/PMPz5UsBgY23FEBToz/Dnaw+Dm6szQ0JcNENocCCDCNAOfJiRAIYBAPlzD0s=');
      DTM_RUNE[TYPE_RUNE_CHAOS] := DTMFromString('m6wAAAHic42ZgYMhnYmDIAuJcIM6D0hlAXAzESVBcAsRlQMzGCMHMQMwFxLxAzATE7EDMCsRCQCwMZVtZWjLYWFsx/P37F4yfLFZleLJEA0irMISGBDHEREUwuLo4MViYmzGIAN1BLGYkASMBACzvFec=');
      DTM_RUNE[TYPE_RUNE_ASTRAL] := DTMFromString('mggAAAHicY2NgYJjOxMAwB4pnAvFEKNZmZGDQBGJdIDaA0lpAfOfYHYaJ3RsZPD3cGG5ee8wQ4O/DsH/jGQYRoFnYMCMODAEA9csQBA==');
      DTM_RUNE[TYPE_RUNE_NATURE] := DTMFromString('mrAAAAHic42BgYJjKxMAwE4inAPF0IO6B4vlAvBCIORkZGDiAmA+IWYCYDYiFgFgYyp5zNpJh3uV4hrBtKQx2ttYMwUH+DFERYQwO9rYMZqbGDCJAO/BhRgIYBgB1aQ7c');
      DTM_RUNE[TYPE_RUNE_LAW] := DTMFromString('mggAAAHicY2NgYDjEBMH7oXg3EB8DYk1GBgYDKNYGYh0odnJ0YNCce45Ba945Bnc3FyD7LBiLAM3ChhlxYAgAAJOqDgo=');
      DTM_RUNE[TYPE_RUNE_DEATH] := DTMFromString('m1gAAAHic42JgYMhgYmAoBOJcIC4C4jQgToRikFwZEJcDsSEjBKsCsSYQa0BpEyC2BGJzINYC4t+/f8Oxg70tQ3CQP0Ogvy9DTFQEQ0RYCIOzowODtZUFgwjQbmIwI5EYAQBkixXw');
      DTM_RUNE[TYPE_RUNE_BLOOD] := DTMFromString('mlwAAAHicY2dgYMhlYmDIA+I0IC6B4iSoWAgjA4MfEPsDsS8QR0BxFBDvk8th2CVaxODj5cFgb2fDEB0VzhAXE8UQGhzIIAI0FxdmxIOhAADo6Q0q');
      DTM_RUNE[TYPE_RUNE_SOUL] := DTMFromString('m6wAAAHic42ZgYIgH4mQgTgXiRCCOAeJIIA4D4mggjoLKZwBxMyMDQxMQ1wFxDRDXAnE9I0QchDuAuB2qxs/Xm2H5igcMc5feY3Cwt2VwdXYE84MD/cF0eFgwQ4C/L4O7mwuDCNBsYjEjCRgJAAB26Blr');
    
      PreemptCounter := 0;
    
      DTM_FIRST_FLOOR := DTMFromString('mbQAAAHicY2VgYFADYgkgFgdiLSBWAuIGIC4C4hogLgXiViD+/ZOJYVKdLkPvBDGGuFB2hjU72BlEgOLomBELBgMAfv8KAw==');
      DTM_SECOND_FLOOR := DTMFromString('mbQAAAHicY2VgYFAGYk0oLQXE0kBcBcQFQJwPxOVA3A7Ee2sfM2iq2zHcXvaFYcq8hQwyUsoMIkBxdMyIBYMBAKUACrY=');
      DTM_THIRD_FLOOR := DTMFromString('mbQAAAHicY2VgYFAGYkUglgdiaSCWAeJaIK4E4mIgbgbidpA6ZWWG6x+/M+x/8ophxtoNDIxAKAIUR8eMWDAYAAC72wso');
      DTM_RUNESPHERE := DTMFromString('mbQAAAHicY2VgYGADYh4g5mSAABYo5oaKsUPFu/o2Mjg5ZDCkff3FoPXqB4Pd1z8MIkBxdMyIBYMBACN6CfA=');
    
      DTM_YELLOW_WIZARD := DTMFromString('mWAAAAHicY2FgYNgLxHuAeDcQ7wfiAiBOBuJoIK4F4qgQP4a/1wsYXp3IYLixI4EBHTCiYRAAAJNrC/E=');
    
      AddOnTerminate('FreeMyDTMs');
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Checking skill level for target selection...');
      StartTime := GetSystemTime;
      LastRightClick := GetSystemTime;
      SmartSetDebug(True);
      SkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
      AppendSmartLine('DONE (' + IntToStr(SkillLevel) + ')', True);
    
      LogSmartLine('Initiating startup sequence...');
      if not (InitialSetUp()) then
      begin
        LogSmartLine('Failed to set up, could be we could not get to runespan or obtain essences');
        Exit;
      end;
    
      LogSmartLine('Obtaining current XP reading...');
      if not IsXPBarOpen then ToggleXPBar(true);
      Players[CurrentPlayer].Integers[0] := GetXPBarTotal;
      LastXPTotal := 0;
      SetAngle(SRL_ANGLE_HIGH);
      AppendSmartLine('DONE (' + IntToStr(Players[CurrentPlayer].Integers[0]) + ')', True);
    
      LogSmartLine('Assessing map...');
      UpdateMap();
      AppendSmartLine('DONE', True);
      LogSmartLine('Generating map 3D projection cache...');
      Initialize3DModelFromMap();
      //SMART_ClearCanvas();
      AppendSmartLine('DONE', True);
    
      DrawMap();
    
    
      if TEST_MODELS then
      begin
        repeat
          TestModels();
          //Sleep(2000);
          //SMART_ClearCanvas();
        until not LoggedIn;
      end;
    
      // set up levels
      repeat
        // break target time
        TargetTime := GetSystemTime + (60000 * BREAK_TIME) - (60000 * BREAK_TIME_SPREAD) + RANDOM(120000 * BREAK_TIME_SPREAD);
        // do tiers set up
        LastOKTime := GetSystemTime;
        repeat
          EverythingOk := MainLoop;
          if (EverythingOk) then
          begin
            if (DesperatelyNeedsEssence) then
            begin
              writeln('desperately need essence');
              EverythingOk := GetSomeEssence();
            end;
          end;
          I := Random(360);
          case (I) of
          0..99 :
            begin
              antiban;
            end;
          end;
          CurrentTPAModel.XP := -1;
          //SMART_ClearCanvas();
          WriteStats;
          UpdateMap;
          DrawMap;
          wait(500+Random(300));
          // check sparsely
          if (Random(10) = 1) then
          begin
            NewSkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
            if (NewSkillLevel > 0) then
            begin
              if (SkillLevel > 0) then
              begin
                runecraftingLevelsGained := NewSkillLevel - SkillLevel;
                if (runecraftingLevelsGained > 0) then
                begin
                  WriteLn('Gratz on level up');
                  stats_IncVariable(STATS_LEVEL_RUNECRAFTING, runecraftingLevelsGained);
                  // we should rethink
                  SkillLevel := NewSkillLevel;
                end;
              end
              else
              begin
    
                SkillLevel := NewSkillLevel;
              end;
            end;
          end;
        until (not Loggedin) or (not EverythingOk) or (TargetTime < GetSystemTime);
        if not TAKES_BREAKS then exit;
    
        ExitToLobby;
    
        sleep ((45000 * BREAK_DURATION) + RANDOM(30000 * BREAK_DURATION));
        if not LoggedIn then
        begin
          LoginPlayer;
        end;
        MMouse(100,100,100,100);
        if SWITCH_WORLDS_BREAK then ChangeWorld(RandomWorld(Players[0].Member, False));
        Wait(4000 + Random(3000))
        FindNormalRandoms;
      until not TAKES_BREAKS;
    end.

  14. #1289
    Join Date
    Mar 2007
    Posts
    5,125
    Mentioned
    275 Post(s)
    Quoted
    901 Post(s)

    Default

    Quote Originally Posted by kyleinkman View Post
    cont again lol...
    Code:
     //SMART_DrawBoxMS(False, Box, clLime);
              end
              else
              begin
                //SMART_DrawBoxMS(False, Box, clYellow);
              end;
            end;
            Result := MakeTPAModelCopy(MatchingModels[index]);
            Location.x := ModelMatches[resultIndex].x;
            Location.y := ModelMatches[resultIndex].y;
            //Writeln('found ' + MatchingModels[Index].Descriptor);
            resultValid := true;
            break;
          end
          else
          begin
            if ((MatchingModels[index].XP < XPFilter) or (XPFilter <= 0)) then
            begin
              if not secondaryFollowed then
              begin
                secondaryFollowed := true;
                // clear previous
                Box.X1 := ModelMatches[resultIndex].x - 5;
                Box.Y1 := ModelMatches[resultIndex].y - 5;
                Box.X2 := ModelMatches[resultIndex].x + 5;
                Box.Y2 := ModelMatches[resultIndex].y + 5;
                if (MatchingModels[Index].Hound) then
                begin
                  //SMART_DrawBoxMS(False, Box, clBlue);
                  //writeln('Marking ' + MatchingModels[Index].Descriptor + ' at ' + IntToStr(ModelMatches[resultIndex].x) + ', ' + IntToStr(ModelMatches[resultIndex].y));
                end
                else
                begin
                  //SMART_DrawBoxMS(False, Box, clAqua);
                  //writeln('Marking ' + MatchingModels[Index].Descriptor);
                end;
              end;
              break;
            end;
          end;
        end;
        if resultValid and secondaryFollowed then break;
      end;
      SetColorToleranceSpeed(1);
    end;
    
    function CheckIfWeCare(X1, Y1, X2, Y2 : Integer) : Boolean;
    var Index : Integer;
    var MAXX : Integer;
    var indexX, indexY : Integer;
    var mapType : Integer;
    begin
      MAXX := Length(MAPPOINTS);
      // until we find overlap assume false
      Result := False;
      // check overhead
      if (X1 > MSCX - 35) and (X2 < MSCX + 35) and (Y2 < 70) then Exit;
      // check left side runespan info
      if (X2 < 75) and (Y1 < 145) then Exit;
      // check if counters
      if (X2 + 40 > MSX2) and (Y1 < 80) then Exit;
      // now we should check data
      for Index := 0 to MAXX - 1 do
      begin
        indexX := MAPPOINTS[Index].x;
        indexY := MAPPOINTS[Index].y;
        mapType := MAP[indexX][indexY];
        if (mapType = MAP_TYPE_OUTSIDE) or (mapType = MAP_TYPE_UNTESTED) then continue;
        // ok we are looking at square that is either inside or border, now do some testing
        if (X1 >= MAP3D[indexX][indexY].X2) then continue;
        if (Y1 >= MAP3D[indexX][indexY].Y2) then continue;
        if (X2 <= MAP3D[indexX][indexY].X1) then continue;
        if (Y2 <= MAP3D[indexX][indexY].Y1) then continue;
        // we may have overlap but the first hit is fine
        Result := True;
        Exit;
      end;
    end;
    
    var
      StartTime : Integer;
      PreemptCounter : Integer;
      CurrentTPAModel : TPAModel;
    
      // runes
      DTM_RUNE : array [TYPE_RUNE_MIN .. TYPE_RUNE_MAX] of Integer;
      // rune counters
      COUNTER_RUNE : array [TYPE_RUNE_MIN .. TYPE_RUNE_MAX] of Integer;
      // points
      COUNTER_POINTS_INITIAL : Integer;
      COUNTER_POINTS : Integer;
    
      DTM_FIRST_FLOOR : Integer;
      DTM_SECOND_FLOOR : Integer;
      DTM_THIRD_FLOOR : Integer;
      DTM_RUNESPHERE : Integer;
    
      DTM_YELLOW_WIZARD : Integer;
    
    // globals
    var SkillLevel : Integer;
    
    function IsRuneSphere : Boolean;
    var x, y : Integer;
    begin
      // nope not until we actually care
      Result := FindDTM(DTM_RUNESPHERE, x, y, 4, 130, 64, 190);
    end;
    
    var LastFloor : Integer;
    var LastCheck : Integer;
    // returns 1, 2, 3 or 0 if not in runespan
    function GetFloorLevel : Integer;
    var X, Y : Integer;
    begin
      if (LastFloor > 0) then
      begin
        // check reset
        if (GetSystemTime - LastCheck) < 5000 then
        begin
          // eh
          Result := LastFloor;
          exit;
        end;
      end;
      LastCheck := GetSystemTime;
      Result := 0;
      LastFloor := 0;
      if FindDTM(DTM_THIRD_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 3;
        LastFloor := 3;
        Exit;
      end;
      if FindDTM(DTM_SECOND_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 2;
        LastFloor := 2;
        Exit;
      end;
      if FindDTM(DTM_FIRST_FLOOR, x, y, 4, 100, 64, 150) then
      begin
        Result := 1;
        LastFloor := 1;
        Exit;
      end;
    end;
    
    function FloorToString : String;
    begin
      case (GetFloorLevel) of
        3: Result := 'Highest level RuneSpan';
        2: Result := 'Middle level RuneSpan';
        1: Result := 'Lowest level RuneSpan';
        0: Result := 'We are not in RuneSpan';
      end;
    end;
    
    function IsInRuneSpan : Boolean;
    var i : Integer;
    var SkillGuideDTM : Integer;
    var x, y : Integer;
    begin
      // see what floor we are
      Result := (GetFloorLevel > 0);
      if (Result) then Exit;
      WriteLn('FLOOR NOT KNOWN');
      // check skill guide
      SkillGuideDTM := DTMFromString('mwQAAAHic42RgYHjKxMDwAogfA/EjKBsk9gCIXwPxMyAWA6oTAWJhIJYCYkkglmaAiIOwABD/2aDLcHeKOsODGdoMfZFSYL4SCwuDoawsgxATE4OhigrYDEKYkQgMBwDkPRLC');
      for i := 1 to 3 do
      begin
        if (FindDTM(SkillGuideDTM, x, y, 710, 1, 760, 50)) then
        begin
          WriteLn('FOUND DTM');
          // close
          Mouse(x, y, 3, 3, true);
          WriteLn('FOUND DTM - clicked');
          Sleep(RandomRange(1000,3000));
          WriteLn('FOUND DTM - checking');
          Result := GetFloorLevel > 0;
          if (Result) then Exit;
          WriteLn('FOUND DTM - FAILED');
        end;
        sleep (RandomRange(300,500));
      end;
      WriteLn('AFTER FOUND DTM');
      // move out
      MMouse(300, 150, 80, 50);
      // double check
      for i := 0 to 50 do
      begin
        sleep (100);
        LastFloor := -1;
        Result := (GetFloorLevel > 0);
        if Result then exit;
      end;
    end;
    
    function EnterPortal : Boolean;
    var PortalDTM : Integer;
    var X, Y : Integer;
    var aFound : Extended;
    var retry : Integer;
    begin
      WriteLn('setting up to enter portal');
      Result := False;
      MakeCompass('W');
      SetAngle(SRL_ANGLE_LOW);
      PortalDTM := DTMFromString('mAAEAAHic42FgYMgB4mYgroDiciD2h+IAKM4E4hggngnEGUCcAsRRUDlXIHYGYg8gDgbiSCAOh8o7AfH0qg0MPf2TGfomTmFIycxkaO/qZejs6WdISk9nqGtsYZgweRpDeVUtnAaZKUICZiQRIwMAZkIctw==');
      retry := 30;
      repeat
        if FindDTMRotated(PortalDTM, x, y, 0, 20, 200, 200, -Pi/4, Pi/4, Pi/16, aFound) then break;
        Writeln('Not yet found');
        Dec(retry);
        if (retry <= 0) then
        begin
          FreeDTM(PortalDTM);
          // umm sorry
          Writeln('Sorry');
          Exit;
        end;
        sleep (50);
      until retry < 0;
      Writeln('Found portal: ' + IntToStr(x) + ', ' + IntToStr(y));
      FreeDTM(PortalDTM);
      // get in on highest level you can
      sleep(1000+random(100));
      MMouse(X, Y, 3, 3);
      sleep(50+random(100));
      ClickMouse2(False);
      sleep(2000+random(1000)); // wait to walk
      if not WaitOptionMulti(['Ente', 'nter', 'nte'], RandomRange(400,500)) then
      begin
        // sorry
        Writeln('option to enter not found');
        Exit;
      end;
      Writeln('option to enter portal found');
      Wait(4000+random(700));
      if (FindNPCChatText('High', Nothing)) then
      begin
        Writeln('High level entry found');
        TypeSend('3');
      end
      else
      begin
        Writeln('High level entry not found, perhaps insufficient skill level');
        TypeSend('2');
      end;
      // well that's about it
      Wait(9000 + Random(3000));
      WriteLn('going through welcome dialog');
      clickContinue(true, true);
      Wait(1000 + Random(1000));
      WriteLn('dialog finished');
      LastFloor := -1;
    
      WriteLn('Current Location: ' + FloorToString);
      Result := IsInRuneSpan;
    end;
    
    function GetBackToRuneSpan : Boolean;
    var CanUseFairy: Boolean;
    var HasEdgevilleTeleport : Boolean;
    var HasDraynorTeleport : Boolean;
    var HasHoodEquipped : Boolean;
    var HasHoodInventory : Boolean;
    var EdgeVilleDTM : Integer;
    var DraynorDTM : Integer;
    var HoodDTM : Integer;
    var edgeX, edgeY, drayX, drayY: Integer;
    var hoodX, hoodY: Integer;
    var RuneClear : Integer;
    begin
      // umm - no runes
      for RuneClear := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        RUNE_LastKnownPosition[RuneClear] := -1;
        COUNTER_RUNE[RuneClear] := 0;
      end;
    
      // not yet back
      Result := false;
      // some set up
      CanUseFairy := CAN_USE_FAIRY_RINGS;
      HasEdgevilleTeleport := false;
      HasDraynorTeleport := false;
      // check hood
      HoodDTM := DTMFromString('mwQAAAHic42RgYHBhYmBwAmJPIHYAYhsgtmWCiHsBsSsQPwOqewTETxkQ7DdA/BGI3wHxbSA2k1NhcLVVZ9BWlWBQlJRmkBOXYJASEWXwd9VhkOITZPDQMGAQAaojhBmJwHAAAMO+D1Y=');
      if (GetCurrentTab <> tab_Equip) then GameTab(tab_Equip);
      HasHoodEquipped := FindDTM(HoodDTM, hoodX, hoodY, 600, 190, 680, 265);
      Writeln('GOT ' + IntToStr(hoodX) + ' and ' + IntToSTr(Hoody));
      HasHoodInventory := false;
      if not HasHoodEquipped then
      begin
        WriteLn('Hood not equipped, checking inventory');
        if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
        HasHoodInventory := FindDTM(HoodDTM, hoodX, hoodY, MIX1, MIY1, MIX2, MIY2);
        Writeln('GOT inventory ' + IntToStr(hoodX) + ' and ' + IntToSTr(Hoody));
      end
      else
      begin
        WriteLn('Hood found equipped, can only have one, skipping inventory check');
      end;
      FreeDTM(HoodDTM);
    
      // hood makes simple
      if ((HasHoodEquipped) or (HasHoodInventory)) then
      begin
        // teleport
        MMouse(hoodX, hoodY, 5, 5);
        sleep(50+random(100));
        ClickMouse2(False);
        if WaitOptionMulti(['Tele', 'lepo', 'port'],RandomRange(400,500)) then
        begin
          WriteLn('Teleporting with hood...');
          // wait
          Wait(8000 + Random(3000));
          // done
          WriteLn('Ok we should be in teleported, now get inside');
          MakeCompass('W');
          SetAngle(SRL_ANGLE_LOW);
          // Enter Portal
          Result := EnterPortal();
          // done - either we are there or not
          exit;
        end;
      end;
    
      // spell setup
      if (not SpellSetup) then SetupSpells();
      ClickSpellMode(SpellMode_All);
      if Cast('lumbridge home teleport', False) then
      begin
        // wait
        wait(500 + Random(300));
        // edgeville
        EdgeVilleDTM := DTMFromString('mggAAAHicY2NgYFBhZGDQAGJtINYEYjkgVgTi2UC5uVA8CYqXAfHijTsZgsWlGHpmb2fg8vZgcCzKB9MiQDlsmBEHhgAAoZoL1A==');
        HasEdgevilleTeleport := FindDTM(EdgeVilleDTM, edgeX, edgeY, 240, 90, 360, 210);
        FreeDTM(EdgeVilleDTM);
        // draynor
        DraynorDTM := DTMFromString('mggAAAHicY2NgYDBhZGDQA2IrRgjbGEpfAspdhOIzQHwCiO8BcY+KCsMZbWOGYnVVMC0iLsvAyMjEIAKUw4YZcWAIAACSnArb');
        HasDraynorTeleport := FindDTM(DraynorDTM, drayX, drayY, 280, 180, 340, 240);
        FreeDTM(DraynorDTM);
      end
      else
      begin
        Writeln('Could not cast home teleport');
        exit;
      end;
      // check if we can tele using fairy
      if not HasEdgevilleTeleport then
      begin
        // nope we walk
        CanUseFairy := False;
      end;
      // for now FORCE walker
      CanUseFairy := False;
    
      // check if we can use fairy
      if (CanUseFairy) then
      begin
        // use it
        Mouse(edgeX, edgeY, 5, 5, true);
        // wait
        Wait(17000 + Random(6000));
        // done
        WriteLn('Ok we should be in Edgeville, but the rest is not yet done');
        exit;
      end
      else
      begin
        if not HasDraynorTeleport then
        begin
          // sorry you should go to draynor yourself and activate the lodestone
          Exit;
        end;
        // use it
        Mouse(drayX, drayY, 5, 5, true);
        // wait
        Wait(17000 + Random(6000));
        // done
        WriteLn('Ok we should be in Draynor, but the rest is not yet done');
        exit;
      end;
    
      // if we have succeeded, we ought to reset certain counters as, for example,
      // we will have lost all runes, and we couldn't read point balance.
      // why? because we may have been ported out in middle of long run, we better
      // then handle it properly
    
    end;
    
    function NeedMoreEssence : Boolean;
    begin
      // if we fall below 100 we need more
      Result := (COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 100);
    end;
    
    function DesperatelyNeedsEssence : Boolean;
    begin
      // if we fall below 25, we can replenish from floating essence
      Result := (COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 25);
    end;
    
    function GetInventoryCountFromDTM(DTM_IN : Integer; RUNE_TYPE : Integer) : Integer;
    var x,y : Integer;
    var startx, starty, rowsize, colsize, colnumber, rownumber: Integer;
    var indexx, indexy, index : Integer;
    var LastKnownPosition : Integer;
    begin
      // init
      Result := 0;
    
      // look for runes
      if not FindDTM(DTM_IN, x, y, MIX1, MIY1, MIX2, MIY2) then
      begin
        // see if we have last known position
        LastKnownPosition := RUNE_LastKnownPosition[RUNE_TYPE];
        if (LastKnownPosition <= 0) then exit;
        // try to get last known position
        Result := GetAmountBox(InvBox(LastKnownPosition));
        exit;
      end;
      AreaInfo('inv', startx, starty, rowsize, colsize, colnumber, rownumber);
      indexx := (x - startx) div colsize;
      indexy := (y - starty) div rowsize;
      if (indexx >= colnumber) then Exit;
      if (indexy >= rownumber) then exit;
      // get new X/Y
      LastKnownPosition := 1 + indexx + (colnumber * indexy);
      (*
      x := startx + (rowSize * indexx);
      y := starty + (colsize * indexy);
      Result := GetAmountBox(IntToBox(x,y, x + rowSize, y + ((colSize * 3) shr 2)));
      *)
      Result := GetAmountBox(InvBox(LastKnownPosition));
      if (Result > 0) then
      begin
        for index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
        begin
          if index = RUNE_TYPE then continue;
          if (RUNE_LastKnownPosition[index] = LastKnownPosition) then
          begin
            // nope
            RUNE_LastKnownPosition[index] := -1;
          end;
        end;
        RUNE_LastKnownPosition[RUNE_TYPE] := LastKnownPosition;
      end
    end;
    
    procedure RandomWalkIsland;
    var maxX : Integer;
    var maxY : Integer;
    var excludeX : Integer;
    var excludeY : Integer;
    var indexX : Integer;
    var indexY : Integer;
    var TotalGreenCount : Integer;
    var Target : Integer;
    begin
      WriteLn('Walking a little');
      UpdateMap;
      DrawMap;
      maxX := Length(MAP);
      maxY := Length(MAP[0]);
      excludeX := 1 + (maxX shr 1);
      excludeY := 1 + (maxY shr 1);
      // count
      TotalGreenCount := 0;
      for indexX := 0 to maxX - 1 do
      begin
        for indexY := 0 to maxY - 1 do
        begin
          if (indexX = excludeX) and (indexY = excludeY) then
          begin
            continue;
          end;
          if (MAP[indexX][indexY] = MAP_TYPE_ISLAND) then
          begin
            Inc(TotalGreenCount);
          end;
        end;
      end;
      // check - cannot walk if nothing found
      if (TotalGreenCount < 1) then Exit;
      if (TotalGreenCount = 1) then
      begin
        Target := 0;
      end
      else
      begin
        Target := Random(TotalGreenCount - 1);
      end;
      WriteLn(' island square count: ' + IntToStr(TotalGreenCount) + ', selected square ' + IntToStr(Target));
      TotalGreenCount := 0;
      for indexX := 0 to maxX - 1 do
      begin
        for indexY := 0 to maxY - 1 do
        begin
          if (indexX = excludeX) and (indexY = excludeY) then
          begin
            continue;
          end;
          if (MAP[indexX][indexY] = MAP_TYPE_ISLAND) then
          begin
            // check
            if (TotalGreenCount = Target) then
            begin
              // found target
              Mouse(MMX1 + 4 + (indexX shl 3), MMY1 + 4 + (indexY shl 3), 4, 4, true);
              sleep (RandomRange(800,1200));
              while (IsMoving) do;
              UpdateMap();
              DrawMap();
              exit;
            end;
            Inc(TotalGreenCount);
          end;
        end;
      end;
    end;
    
    function DoIncremental(RUNE_TYPE : Integer; DTM_IN : Integer; Initial : Boolean; Current : Integer; StatsVar : String) : Integer;
    var Difference : Integer;
    begin
      Result := GetInventoryCountFromDTM(DTM_IN, RUNE_TYPE);
      if (Initial) then exit;
      // check difference
      Difference := Result - Current;
      if (Difference > 0) then
      begin
        stats_IncVariable(StatsVar, Difference);
      end;
    end;
    
    function GetSmartValue(NewValue, OldValue : Integer) : Integer;
    begin
      // check - shouldn't drop by more than few
      if (NewValue + 5 > OldValue) then
      begin
        Result := NewValue;
        exit;
      end;
      Result := OldValue - 5;
      if Result < 0 then Result := 0;
    end;
    
    procedure RefreshRuneCounters(Initial : Boolean; Filter : Integer);
    var Points : Integer;
    var PointString : String;
    var Index : Integer;
    begin
      // check inventory
      if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
    
      // rune counters
      if (Filter > TYPE_RUNE_MAX) or (Filter < TYPE_RUNE_MIN) then
      begin
        // all counters
        PointString := GetTextAtExWrap(50, 78, 95, 92, 0, 1, 1, 16777215, 0, StatChars);
        Points := StrToIntDef(GetNumbers(PointString), 0)
        if (Initial) then
        begin
          COUNTER_POINTS := Points;
          COUNTER_POINTS_INITIAL := Points;
        end
        else
        begin
          if (Points > COUNTER_POINTS) then
          begin
            //stats_IncVariable(STATS_POINTS, Points - CURRENT_POINTS);
            COUNTER_POINTS := Points;
          end;
        end;
        for Index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
        begin
          COUNTER_RUNE[Index] := GetSmartValue(DoIncremental(Index, DTM_RUNE[Index], Initial, COUNTER_RUNE[Index], STATS_RUNE[Index]), COUNTER_RUNE[Index]);
        end;
      end
      else
      begin
        // filter counter
        COUNTER_RUNE[Filter] := GetSmartValue(DoIncremental(Filter, DTM_RUNE[Filter], Initial, COUNTER_RUNE[Filter], STATS_RUNE[Filter]), COUNTER_RUNE[Filter]);
      end;
    end;
    
    function GetSomeEssence : Boolean;
    var tryThisDTM : Integer;
    var tryThisDTM2 : Integer;
    var tryThisDTM3 : Integer;
    var x, y : Integer;
    var E: Extended;
    var counter : Integer;
    var Found : Boolean;
    begin
      Writeln('getting essence');
      if (GetCurrentTab <> Tab_Inv) then GameTab(Tab_Inv);
      tryThisDTM := DTMFromString('mlwAAAHicY2dgYLjIyMBwBYjPAfFJID4NxHeB+CEQ7wDKbwfiPVC8FYgPA/FBIE5LTWKIi4liCA0JZDA3M2WIiYpgCPD3ZbC3s2UQAcrjwox4MBQAAFmAEhg=');
      tryThisDTM2 := DTMFromString('mggAAAHicY2NgYLgMxOeB+CwQ30DiLwbiFUC8BIiXAvFyIF4IxEmJcQwO9rYMPt4eDH6+3gxenm4MkRGhDCJAOWyYEQeGAAAm0g7L');
      tryThisDTM3 := DTMFromString('mggAAAHicY2NgYPgOxF+A+CMQv4XSH4B4ExCvA+KtQLwFiDcA8Vog9vJ0Y/Dx9mCwtrJgMNDXY3B3c2GwtbFiEAHKYcOMODAEAABVLg7L');
    
      for counter := 1 to 30 do
      begin
        Found := FindDTMRotated(tryThisDTM, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if not Found then Found := FindDTMRotated(tryThisDTM2, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if not Found then Found := FindDTMRotated(tryThisDTM3, X, Y, MSX1, MSY1, MSX2, MSY2, - Pi/4, Pi/4, Pi/16, E);
        if Found then
        begin
          Mouse(x, y, 2, 2, true);
          if (DidRedClick) then
          begin
            while IsMoving do;
          end;
        end;
        RefreshRuneCounters(false, TYPE_RUNE_ESSENCE);
        if not DesperatelyNeedsEssence then break;
        // random walk island
        WriteLn('Walking for essence');
        RandomWalkIsland();
        // check
        if not IsInRuneSpan then break;
      end;
      FreeDTM(tryThisDTM);
      FreeDTM(tryThisDTM2);
      FreeDTM(tryThisDTM3);
      // check if we got essence - for now assume success
      Result := not DesperatelyNeedsEssence;
    end;
    
    function InitialSetUp : Boolean;
    var X, Y : Integer;
    var NagScreenDTM : Integer;
    begin
      LogSmartLine('Checking for Mad Day nag screen...');
      NagScreenDTM := DTMFromString('mlwAAAHicY2dgYPjMyMDwAYi/QjGI/Q6IfwGxCFBeEIrFoVgEir9ut2H4czKV4f1GO4YUGxGG7iBZhiBFRYa09HS4GmyYEQ+GAgDZGxE5');
      if FindDTM(NagScreenDTM, X, Y, 480, MSY1, MSX2, 30) then
      begin
        // found nag screen, we are user so we kinda wait
        Wait(500 + Random(500));
        Mouse(x, y, 2, 2, true);
      end;
      FreeDTM(NagScreenDTM);
      // ok wait for nag screen to go away
      Wait(500 + Random(500));
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Checking for randoms...');
      // now do randoms
      FindNormalRandoms;
      // ok wait just in case the SRL closed some other screen
      Wait(500 + Random(500));
      AppendSmartLine('DONE', True)
    
      LogSmartLine('Our location: ' + FloorToString);
      Result := true;
      // check if we are in runespan
      if not IsInRuneSpan then
      begin
        LogSmartLine('Getting back to RuneSpan');
        // get back to runespan
        Result := GetBackToRuneSpan();
        // see if we failed
        if not Result then
        begin
          LogSmartLine('We are not in RuneSpan and we kinda failed to get back.');
          Exit;
        end;
      end;
      LogSmartLine('Checking inventory...');
      // woo hoo
      RefreshRuneCounters(true, TYPE_RUNE_NONE);
      // just because it is easier to find essence for example
      SetAngle(SRL_ANGLE_HIGH);
      AppendSmartLine('DONE', True);
      // get more essences if needed
      if DesperatelyNeedsEssence then
      begin
        LogSmartLine('Obtaining essence');
        Result := GetSomeEssence();
      end;
    end;
    
    (*
    
    Wizard without being told - the startup yellow wizard:
    
    right click - Talk to Wizard
    continue
    This is tricky, but I am getting the hang of it
    Continue
    You need to wait for a wizard to call
    continue
    *)
    
    
    var XPTimeout : Integer;
    var GuaranteedClick : Boolean;
    var LastXPTotal : integer;
    var LastOKTime : Integer;
    
    procedure WriteStats;
    var XPTotal : Integer;
    var XPPerHour : Integer;
    var XPDiff : Integer;
    var PointsTotal : Integer;
    var PointsPerHour : Integer;
    var SecondsElapsed : Integer;
    var SmartLines2: TStringArray;
    var RunesphereEvent: Boolean;
    var XPT : String;
    var XPTf : Integer;
    begin
      // get total xp tick
      XPTotal := GetXPBarTotal - Players[CurrentPlayer].Integers[0];
      PointsTotal := COUNTER_POINTS - COUNTER_POINTS_INITIAL;
      if (XPTotal > 0) then
      begin
        XPDiff := XPTotal - LastXPTotal;
        if (XPDiff) > 0 then
        begin
          stats_IncVariable(STATS_EXP_TOTAL, XPDiff);
          stats_IncVariable(STATS_EXP_RUNECRAFTING, XPDiff);
          WriteLn('Incrementing variables by ' + IntToStr(XPDiff));
    
          if (XPTotal > 0) then
          begin
            LastXPTotal := XPTotal;
            WriteLn('XP Total Diff = ' + IntToStr(XPDiff));
          end;
        end;
      end;
      // plus one - just to be sure we don't divide by zero
      SecondsElapsed := (1 + ((Getsystemtime-StartTime)/1000));
      // mind order of execution, these are integers
      XPPerHour := (3600 * XPTotal) / SecondsElapsed;
      PointsPerHour := (3600 * PointsTotal) / SecondsElapsed;
      RunesphereEvent := IsRuneSphere;
    
      if (XPTimeout > 999) then
      begin
        XPT := IntToStr(XPTimeout div 1000) + '.';
      end
      else
      begin
        XPT := '0.';
      end;
      XPTf := XPTimeout mod 1000;
      XPT := XPT + IntToStr(XPTf div 100);
      XPTf := XPTf mod 100;
      XPT := XPT + IntToStr(XPTf div 10);
      XPT := XPT + IntToStr(XPTf mod 10);
    
      if not KEEP_DEBUG then ClearDebug;
      WriteLn('============Rough Stats etc...============');
      WriteLn('Preemptible RuneSpan v. ' + VERSION);
      WriteLn('Time Running: ' + TimeRunning);
      WriteLn('XP Earned: ' + IntToStr(XPTotal) + ', per hour: ' + IntToStr(XPPerHour));
      WriteLn('Points Earned: ' + IntToStr(PointsTotal) + ', per hour: ' + IntToStr(PointsPerHour));
      WriteLn('Preempted Counter: ' + IntToStr(PreemptCounter));
      if (CurrentTPAModel.XP > 0) then
      begin
        WriteLn('Current Target: ' + CurrentTPAModel.Descriptor + ', ' + IntToStr(CurrentTPAModel.XP) + 'XP, level ' +
          IntToStr(CurrentTPAModel.MinimumLevel) + ', XPTIMEOUT ' + XPT + 's');
      end;
      WriteLn('Current Runecrafting Level: ' + IntToStr(SkillLevel));
      WriteLn('Essences Available: ' + IntToStr(COUNTER_RUNE[TYPE_RUNE_ESSENCE]));
      WriteLn('Current Floor: ' + FloorToString);
      if (RunesphereEvent) then WriteLn('RUNESPHERE is active!!!');
      WriteLn('==========================================');
    
      if (ONSCREEN_STATS) then
      begin
        SetArrayLength(SmartLines2, 9);
        SmartLines2[0] := '= Preemptible RuneSpan version ' + VERSION + ' =';
        SmartLines2[1] := 'Time Running: ' + TimeRunning;
        SmartLines2[2] := 'XP Earned: ' + IntToStr(XPTotal) + ', Per hour: ' + IntToStr(XPPerHour);
        SmartLines2[3] := 'Points Earned: ' + IntToStr(PointsTotal) + ', Per hour: ' + IntToStr(PointsPerHour);
        SmartLines2[4] := 'Preempted Counter: ' + IntToStr(PreemptCounter);
        if (CurrentTPAModel.XP > 0) then
        begin
          SmartLines2[5] := 'Current Target: ' + CurrentTPAModel.Descriptor + ', ' + IntToStr(CurrentTPAModel.XP) + 'XP, level ' + IntToStr(CurrentTPAModel.MinimumLevel) + ', XPTIMEOUT ' + XPT + 's';
        end
        else
        begin
          SmartLines2[5] := 'no target';
        end;
        SmartLines2[6] := 'Current Runecrafting Level: ' + IntToStr(SkillLevel);
        SmartLines2[7] := 'Essences Available: ' + IntToStr(COUNTER_RUNE[TYPE_RUNE_ESSENCE]);
        SmartLines2[8] := '======';
        if (RunesphereEvent) then
        begin
          SmartLines2[8] := '== RUNESPHERE IS ACTIVE ==';
        end;
        PrintOnSmart(SmartLines2,Point(15,225),clYellow);
      end;
      // commit
      Stats_Commit;
    end;
    
    const XPTIMEOUT_NODEBASE = 9000;
    const XPTIMEOUT_HOUNDBASE = 11000; // one - they're slower, two - we can detect their demise
    procedure CalculateXPTimeout;
    var DecrementAmount : Integer;
    begin
      XPTimeout := XPTIMEOUT_NODEBASE;
      if (CurrentTPAModel.Hound) then XPTimeout := XPTIMEOUT_HOUNDBASE;
      DecrementAmount := 2000;
      if (USER_IS_ADD) then DecrementAmount := 5000;
      if (SkillLevel > CurrentTPAModel.MinimumLevel) then
      begin
        if (SkillLevel > 2 * CurrentTPAModel.MinimumLevel) then
        begin
          XPTimeout := XPTimeout - DecrementAmount;
        end
        else
        begin
          XPTimeout := XPTimeout - ((DecrementAmount * (SkillLevel - CurrentTPAModel.MinimumLevel)) / CurrentTPAModel.MinimumLevel);
        end;
      end;
    end;
    
    // only for hounds
    function DO_SIPHON : Boolean;
    var EntryXP : Integer;
    var xpTick : Integer;
    var PrevRuneEssence : Integer;
    var NewModel : TPAModel;
    var TPLocation : TPoint;
    var SelectedOption : Boolean;
    var MapRefreshed : Boolean;
    var ModelsTestCount : Integer;
    var ModelsTestTimer : Integer;
    var ModelsTimerString : String;
    var EntryTime : Integer;
    var FloorLevel : Integer;
    var ModelGuaranteed : Boolean;
    var ModelTimer : Integer;
    var ModelXP : Integer;
    var CurrentXP : Integer;
    begin
      WriteLn('in siphon');
      CurrentXP := GetXPBarTotal;
      ModelGuaranteed := GuaranteedClick;
      ModelTimer := GetSystemTime;
      ModelXP := CurrentXP;
      MapRefreshed := False;
      Result := False;
      // show stats
      CalculateXPTimeout;
      //SMART_ClearCanvas();
      WriteStats;
      // initial set up
      RefreshRuneCounters(False, TYPE_RUNE_ESSENCE);
      PrevRuneEssence := COUNTER_RUNE[TYPE_RUNE_ESSENCE];
      EntryXP := CurrentXP;
    
      while (IsMoving) do;
    
      sleep (RandomRange(1000,2000)); // just to get animation started
      LastOKTime := GetSystemTime;
      EntryTime := GetSystemTime;
      while ((LastOKTime + XPTimeout) > GetSystemTime) do
      begin
        // prevent timeout
        if (EntryTime + 120000 < GetSystemTime) then
        begin
          WriteLn('Checking stuff to prevent timeout');
          HoverSkill(SKILL_RUNECRAFTING, False);
          sleep(RandomRange(200, 1500));
          EntryTime := GetSystemTime;
          WriteLn('Prev essence count: ' + IntToStr(PrevRuneEssence));
        end;
        // get xp tick
        CurrentXP := GetXPBarTotal;
        xpTick := CurrentXP - EntryXP;
        EntryXP := CurrentXP;
        // reset ok time
        if ((xpTick) > 0) then
        begin
          ModelXP := ModelXP + xpTick;
          GuaranteedClick := true;
          // Writeln ('Incremental xp: ' + IntToStr(xpTick));
          LastOKTime := GetSystemTime;
          if not MapRefreshed then
          begin
            if (Random(5)) = 1 then CompassMovement(-50, 60, false);
            MapRefreshed := True;
            UpdateMap();
            DrawMap();
          end;
          Result := True;
        end;
        // check
        if not GuaranteedClick then
        begin
          WriteLn('not guaranteed');
          // only 1/2 of timeout
          if (LastOKTime + (XPTimeout shr 1)) < GetSystemTime then
          //if (LastOKTime + ((XPTimeout + (XPTimeout shl 1)) shr 2)) > GetSystemTime then
          begin
            WriteLn('Exiting siphon due to unguaranteed click and early timeout ' + IntToStr(GetSystemTime - LastOKTime));
            Exit;
          end;
        end;
        // check exit condition - hound broken down
        RefreshRuneCounters(False, TYPE_RUNE_ESSENCE);
        if (PrevRuneEssence > COUNTER_RUNE[TYPE_RUNE_ESSENCE]) then
        begin
          // check for reasonable drain
          if (PrevRuneEssence - COUNTER_RUNE[TYPE_RUNE_ESSENCE] < 3) then
          begin
            // ok
            LastOKTime := GetSystemTime;
          end;
        end;
        if (PrevRuneEssence < COUNTER_RUNE[TYPE_RUNE_ESSENCE]) then
        begin
          // creature broken down
          WriteLn('Creature broken down.');
          If not CurrentTPAModel.Hound then
          begin
            WriteLn('...but... we did not think it was hound');
          end
          else
          begin
            if ModelGuaranteed then
            begin
              xpTick := GetXPBarTotal - EntryXP;
              if xpTick > 0 then
              begin
                ModelXP := ModelXP + xpTick;
              end;
              WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
            end;
          end;
          Result := True;
          WriteLn('Exiting siphon due to broken creature');
          LastRightClick := GetSystemTime - 30000; // to immediately allow right click
          Exit;
        end;
        // adjust
        PrevRuneEssence := COUNTER_RUNE[TYPE_RUNE_ESSENCE];
    
        // we can loop - first see if we can get better target
        if (NeedMoreEssence) then
        begin
          // just to be sure
          if not IsInRuneSpan then
          begin
            Result := True;
            WriteLn('Exiting siphon due to not in runespan');
            Exit;
          end;
          WriteLn('need more essence');
          if (not CurrentTPAModel.Hound) then
          begin
            if ModelGuaranteed then
            begin
              xpTick := GetXPBarTotal - EntryXP;
              if xpTick > 0 then
              begin
                ModelXP := ModelXP + xpTick;
              end;
              WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
            end;
            // we got to get out of this
            Result := true;
            WriteLn('Exiting siphon due to essence count');
            Exit;
          end;
          // just sleep
          sleep(RandomRange(1000,2000));
          continue;
        end;
        // we don't need more essence yet
        FloorLevel := GetFloorLevel;
        if (FloorLevel <= 0) then
        begin
          if not IsInRuneSpan() then Exit;
          FloorLevel := GetFloorLevel;
        end;
        ModelsTestTimer := GetSystemTime;
        NewModel := FindTarget(False, FloorLevel, SkillLevel, Players[0].Member,
          0, CurrentTPAModel.ModelType, true, TPLocation, SelectedOption,
          ModelsTestCount);
        ModelsTestTimer := GetSystemTime - ModelsTestTimer;
        // check
        if (ModelsTestCount > 0) then
        begin
          ModelsTestTimer := (ModelsTestTimer * 100) / ModelsTestCount;
          ModelsTimerString := IntToStr(ModelsTestTimer div 100) + '.';
          ModelsTestTimer := ModelsTestTimer mod 100;
          ModelsTimerString := ModelsTimerString + IntToStr(ModelsTestTimer div 10) + IntToStr(ModelsTestTimer mod 10);
          if (LOG_LONG_SEARCHES) then WriteLn('Pre-empt routine tested ' + IntToStr(ModelsTestCount) + ' model(s) at ' +
            ModelsTimerString + 'ms each');
        end;
        if (NewModel.XP < 0) then
        begin
          // wait a little
          if (ModelsTestTimer < 800) then
          begin
            Sleep(RandomRange(100, 1000 - ModelsTestTimer));
          end;
          continue;
        end;
        // check
        if SelectedOption then
        begin
          CurrentXP := GetXPBarTotal;
          if ModelGuaranteed then
          begin
            xpTick := GetXPBarTotal - EntryXP;
            if xpTick > 0 then
            begin
              ModelXP := ModelXP + xpTick;
            end;
            WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
          end;
          ModelGuaranteed := true;
          ModelXP := CurrentXP;
          ModelTimer := GetSystemTime;
          GuaranteedClick := true;
        end
        else
        begin
          GuaranteedClick := false;
          MMouse(TPLocation.x, tpLocation.y, 2, 2);
          if (WaitUpTextFuzzy('Walk here', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          if (WaitUpTextFuzzy('Climb ladder', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          if (WaitUpTextFuzzy('Use platform', MATCH_LOOSE, 80)) then
          begin
            // nope
            RecordClickban(TPLocation.x, TPLocation.y);
            continue;
          end;
          // check if node
          if (NewModel.Hound) then
          begin
            ClickMouse2(mouse_left);
            GuaranteedClick := false;
            // GuaranteedClick := DidRedClick;
            // if not (GuaranteedClick) then continue;
          end
          else
          begin
            // nodes don't move
            if (GetSystemTime - LastRightClick) < RandomRange(15000,20000) then
            begin
              ClickMouse2(mouse_left);
              // not guaranteed
              // GuaranteedClick := DidRedClick;
              // if not (GuaranteedClick) then continue;
              GuaranteedClick := false;
            end
            else
            begin
              LastRightClick := GetSystemTime;
              ClickMouse2(mouse_Right);
              if not WaitOptionFuzzy(MakeUptext(NewModel.Descriptor, NewModel.Hound),
                MATCH_GOOD, RandomRange(400,500)) then
              begin
                sleep(RandomRange(100,500));
                continue;
              end;
              GuaranteedClick := True;
            end;
          end;
          CurrentXP := GetXPBarTotal;
          if ModelGuaranteed then
          begin
            xpTick := GetXPBarTotal - EntryXP;
            if xpTick > 0 then
            begin
              ModelXP := ModelXP + xpTick;
            end;
            WriteAccountingForCurrentModel(ModelXP, GetSystemTime - ModelTimer);
          end;
          ModelGuaranteed := GuaranteedClick;
          ModelXP := CurrentXP;
          ModelTimer := GetSystemTime;
          GuaranteedClick := true;
        end;
        // just wait
        while (IsMoving) do;
        // we need to treat accounting correctly so don't assume start until now
        ModelTimer := GetSystemTime;
        // will refresh map and move camera
        MapRefreshed := false;
        Inc(PreemptCounter);
        CurrentTPAModel := NewModel;
        CalculateXPTimeout;
        if not GuaranteedClick then
        begin
          if (ModelsTestTimer < 800) then
          begin
            Sleep(RandomRange(100, 1000 - ModelsTestTimer));
          end;
        end;
        LastOKTime := GetSystemTime;
        //SMART_ClearCanvas();
        WriteStats;
        UpdateMap();
        DrawMap();
      end;
      WriteLn('Exiting siphon through normal loop ' + IntToStr(LastOKTime + XPTimeout) + ' vs ' + IntToStr(GetSystemTime));
    end;
    
    Procedure antiban;
    var
      I: Integer;
      CurrentCompassDegree: Extended;
    begin
      I := Random(1000);
      case (I) of
        0..99 :
          begin
            GameTab(tab_Stats);
            HoverSkill('runecrafting', False);
            GameTab(28);
          end;
        100..200 :
          begin
            SleepAndMoveMouse(7000 + Random(500));
            MakeCompass(CurrentCompassDegree-18+Random(32));
          end;
        200..210 :
          begin
            GameTab(Tab_Stats);
            MakeCompass(CurrentCompassDegree-2+Random(34));
            Wait(3000 + Random(500));
            GameTab(28);
          end;
        211..220 :
          begin
            BoredHuman;
            MakeCompass('N');
          end;
        221..300 :
          begin
          GameTab(Tab_Stats);
          Wait(100 + Random(1000));
          end;
      end;
    end;
    
    // hmm ok let's unify what result means - false - we couldn't do our work, true
    // means we did
    function MainLoop : boolean;
    var TPLocation : TPoint;
    var Looper : Integer;
    var DID_FIND : Boolean;
    var SelectedOption : Boolean;
    var ModelsTestCount : Integer;
    var ModelsTestTimer : Integer;
    var ModelsTimerString : String;
    var LastWasHound : Boolean;
    var StartTime : Integer;
    var FloorLevel : Integer;
    var NewSkillLevel : Integer;
    begin
      Result := True;
      if FindNormalRandoms then
      begin
        LastOKTime := GetSystemTime;
        SetAngle(SRL_ANGLE_HIGH);
      end;
      // check if we are in runespan
      if not IsInRuneSpan then
      begin
        FindNormalRandoms;
        if not LoggedIn() then
        begin
          Result := false;
          Exit;
        end;
        // get back to runespan
        Result := GetBackToRuneSpan();
        // see if we failed
        if not Result then
        begin
          WriteLn('We are not in RuneSpan and we kinda failed to get back.');
          Exit;
        end;
      end;
      // ok let's do it
      if (not Result) then exit;
      RefreshRuneCounters(false, TYPE_RUNE_NONE);
    
      // check
      if (SkillLevel < 1) then
      begin
        NewSkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
        if (NewSkillLevel > 0) then
        begin
          // save
          SkillLevel := NewSkillLevel;
        end
        else
        begin
          // ok just pretend this didn't happen for now but if longer than 3 min we
          // will have to end
          Result := true;
          // check if we are without xp for more than 3 minutes
          if (LastOKTime - GetSystemTime) > 180000 then
          begin
            Result := false;
          end
          else
          begin
            // waiting
            WriteLn('Waiting for skill info');
            Sleep(RandomRange(800, 2700));
          end;
          exit;
        end;
      end;
    
      // hopping
      //DoDTMHop2Main;
      DID_FIND := false;
      LastWasHound := CurrentTPAModel.Hound;
      StartTime := GetSystemTime;
      for Looper := 1 to 50 do
      begin
        ModelsTestTimer := GetSystemTime;
        FloorLevel := GetFloorLevel;
        if FloorLevel <= 0 then
        begin
          if not IsInRunespan() then break;
          FloorLevel := GetFloorLevel;
        end;
        CurrentTPAModel := FindTarget(NeedMoreEssence, FloorLevel, SkillLevel,
          Players[0].Member, 0, TARGET_NONE, true, TPLocation, SelectedOption,
          ModelsTestCount);
        ModelsTestTimer := GetSystemTime - ModelsTestTimer;
        if (ModelsTestCount > 0) then
        begin
          ModelsTestTimer := (ModelsTestTimer * 100) / ModelsTestCount;
          ModelsTimerString := IntToStr(ModelsTestTimer div 100) + '.';
          ModelsTestTimer := ModelsTestTimer mod 100;
          ModelsTimerString := ModelsTimerString + IntToStr(ModelsTestTimer div 10) + IntToStr(ModelsTestTimer mod 10);
          if LOG_LONG_SEARCHES then WriteLn('Main loop tested ' + IntToStr(ModelsTestCount) + ' model(s) at ' +
            ModelsTimerString + 'ms each');
        end;
        if (CurrentTPAModel.XP > 0) then
        begin
          if SelectedOption then
          begin
            DID_FIND := True;
            GuaranteedClick := True;
            writeln('Guaranteed click');
            Break;
          end;
          GuaranteedClick := False;
          MMouse(TPLocation.x, TPLocation.y, 2, 2);
          ClickMouse2(mouse_left);
          // GuaranteedClick := DidRedClick;
          DID_FIND := True;
          if (DID_FIND) then Break;
        end;
        // still looping?
        if LastWasHound then
        begin
          // assume 7 seconds recovery time
          if GetSystemTime - StartTime < 7000 then continue;
        end;
        if (Looper mod 3) = 0 then
        begin
          // random walk island
          WriteLn('Walking for target');
          RandomWalkIsland();
        end;
      end;
    
      while (IsMoving) do;
      UpdateMap;
      DrawMap;
      if (DID_FIND) then DID_FIND := DO_SIPHON;
    
      if (DID_FIND) then
      begin
        WriteLn('main loop done without finding target');
      end
      else
      begin
        WriteLn('main loop failed to find anything');
        if (Random(25) = 1) then
        begin
          CompassMovement(-50, -20, false);
          if (Random(4) = 1) then CompassMovement(2, 12, false);
        end
        else
        begin
          if (Random(10) = 1) then CompassMovement(5, 22, false);
        end;
        if (Random(18) = 1) then
        begin
          // walk a little
          RandomWalkIsland;
        end;
        if (Random(9) = 1) then
        begin
          SetAngle(SRL_ANGLE_LOW);
          CompassMovement(-40, 100, false);
          SetAngle(SRL_ANGLE_HIGH);
        end;
      end;
      // ensure that we don't have nag window
      IsInRuneSpan();
      // check if we are without xp for more than 3 minutes
      if (LastOKTime - GetSystemTime) > 180000 then
      begin
        Result := false;
      end;
    end;
    
    procedure TestModels;
    var DESC : TStringArray;
    var MatchingModels : TPAModelArray;
    var ModelMatches : TPointArray;
    var index : Integer;
    var resultIndex : Integer;
    var MyStart : Integer;
    var MyEnd : Integer;
    begin
      SetColorToleranceSpeed(1); // 2
      MatchingModels := GetSearchOrderTPAModelList(False, 3, 99, True)
      SetLength(DESC, 1);
      for index := 0 to Length(MatchingModels) - 1 do
      begin
        // look
        if // (MatchingModels[index].Descriptor <> 'Skulls')
        //and
        (MatchingModels[index].Descriptor <> 'Fleshy growth')
        and (MatchingModels[index].Descriptor <> 'Fire storm')
        //and (MatchingModels[index].Descriptor <> 'Bloody skulls')
        and (MatchingModels[index].Descriptor <> 'Undead soul')
    
        and (MatchingModels[index].Descriptor <> 'Soul esswraith')
        //(MatchingModels[index].Descriptor <> 'Blood esswraith')
         then continue;
        MyStart := GetSystemTime;
        ModelMatches := FindModel(MatchingModels[index], EnvironmentalExcludes[GetFloorLevel]);
        MyEnd := GetSystemTime;
        // check if we have matches
        Writeln('LOOKING FOR ' + MatchingModels[index].Descriptor + ' TOOK ' +
          IntToStr(MyEnd - MyStart) + 'ms and got ' + IntToStr(Length(ModelMatches)));
        if (Length(ModelMatches) <= 0) then continue;
        DESC[0] := MatchingModels[Index].Descriptor;
        // go through results
        for resultIndex := 0 to Length(ModelMatches) - 1 do
        begin
    
          if ModelMatches[resultIndex].x < 6 then continue;
          if ModelMatches[resultIndex].y < 6 then continue;
    
          break;
        end;
      end;
    end;
    
    procedure FreeMyDTMs;
    var Index : Integer;
    begin
      // runes
      for Index := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        FreeDTM(DTM_RUNE[Index]);
      end;
    
      FreeDTM(DTM_FIRST_FLOOR);
      FreeDTM(DTM_SECOND_FLOOR);
      FreeDTM(DTM_THIRD_FLOOR);
      FreeDTM(DTM_RUNESPHERE);
    
      FreeDTM(DTM_YELLOW_WIZARD);
    end;
    
    procedure ClearClickban;
    var i : integer;
    begin
      for i := 0 to CLICKBAN_MAX_AREAS - 1 do
      begin
        CLICKBANS[i].X := -1;
        CLICKBANS[i].Y := -1;
      end;
    end;
    
    procedure RecordClickbanEX(RasterX, RasterY : Integer);
    var Index : Integer;
    var CBCount : Integer;
    begin
      // check
      for Index := 0 to CLICKBAN_MAX_AREAS - 1 do
      begin
        if CLICKBANS[Index].x < 0 then
        begin
          CBCount := Index;
    
          break;
        end;
        if CLICKBANS[Index].x <> RasterX then continue;
        if CLICKBANS[Index].y <> RasterY then continue;
        // already exists
        exit;
      end;
      // move out
      if CBCount = CLICKBAN_MAX_AREAS - 1 then
      begin
        // watch it buster
        Dec(CBCount);
      end;
      for Index := CBCount - 1 downto 0 do
      begin
        CLICKBANS[Index + 1].x := CLICKBANS[Index].x;
        CLICKBANS[Index + 1].y := CLICKBANS[Index].y;
      end;
      CLICKBANS[0].X := RasterX;
      CLICKBANS[0].Y := RasterY;
    end;
    
    procedure RecordClickban(X, Y : Integer);
    var RecordXPlus : Boolean;
    var RecordYPlus : Boolean;
    var RasterX : Integer;
    var RasterY : Integer;
    begin
      RasterX := (X - MSX1) shr 3;
      RasterY := (Y - MSY1) shr 3;
      RecordXPlus := false;
      RecordYPlus := false;
      if (RasterX < ((MSX2 - MSX1) shr 3)) then RecordXPlus := True;
      if (RasterY < ((MSY2 - MSY1) shr 3)) then RecordYPlus := True;
      RecordClickbanEX(RasterX, RasterY);
      if RecordYPlus then RecordClickBanEx(RasterX, RasterY + 1);
      if RecordXPlus then RecordClickBanEx(RasterX + 1, RasterY);
      if (RecordXPlus and RecordYPlus) then RecordClickBanEx(RasterX + 1, RasterY + 1);
      if (RasterX > 0) then
      begin
        RecordClickBanEX(RasterX - 1, RasterY);
        if RecordYPlus then RecordClickBanEx(RasterX - 1, RasterY + 1);
        if (RasterY > 0) then
        begin
          RecordClickBanEX(RasterX, RasterY - 1);
          RecordClickBanEX(RasterX - 1, RasterY - 1);
        end;
      end
      else
      begin
        if (RasterY > 0) then
        begin
          if RecordXPlus then RecordClickBanEx(RasterX + 1, RasterY - 1);
          RecordClickbanEX(RasterX, RasterY - 1);
        end;
      end;
    end;
    
    procedure WriteAccountingForCurrentModel(XPDiff : Integer; TimeDiff : Integer);
    begin
      writeln('Account for current model [' + CurrentTPAModel.Descriptor + '], adding ' +
        IntToStr(XPDiff) + ' XP in ' + IntToStr(TimeDiff) + ' ticks.');
    end;
    
    var i : integer;
    var runecraftingLevelsGained : Integer;
    var EverythingOk : Boolean;
    var TargetTime : Integer;
    var NewSkillLevel : Integer;
    begin
      DeclarePlayers;
    ;
      SmartSetRefresh(100);
      SetupSRL;
      SetupSRLStats(1037, SRLStats_User, SRLStats_Password);
      LoginPlayer;
      while not RSReady do Sleep(2000);
      SmartSetRefresh(60);
    
      ClearDebug();
      //SMART_ClearCanvas();
      WriteLn     ('**************************');
      LogSmartLine('** Preemptible RuneSpan **');
      WriteLn     ('**************************');
    
      LogSmartLine('Initializing density models and 3D engine...');
      // clear
      InitializeStatsArray();
      CurrentTPAModel.XP := -1;
      LastFloor := -1;
    
      // register TPA models
      RegisterTPAModels();
      Initialize3D();
      SetLength(CLICKBANS, CLICKBAN_MAX_AREAS);
      ClearClickban;
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Initializing inventory DTMs...');
      for i := TYPE_RUNE_MIN to TYPE_RUNE_MAX do
      begin
        RUNE_LastKnownPosition[i] := -1;
        COUNTER_RUNE[i] := 0;
      end;
    
      // runes
      DTM_RUNE[TYPE_RUNE_ESSENCE] := DTMFromString('maQEAAHicrcxNCsJADIbh6A2E9hJSf9pOsRXEotJOOxsRj+9CRUUE3QiufQmzEFzqwMPkS0J6IrLoimyxQYwUKzQoUMFi5q3hUGKJGifu3LHDHk+fj7jghYefXXHAGbePXmYSqeepsqXR37VWRsOBTIucbHQnn2SawzCQJB5rHUV9nQXc+VXnD77eGw4EIEI=');
    
      DTM_RUNE[TYPE_RUNE_AIR] := DTMFromString('mFQEAAHic42VgYJjFxMAwHYinAfFsIJ4LxHOAeAYQTwHiiUDcB8XzgHghED8G6nsExM+gGMS/D8UPkcTeAvE7qForC1OGjx8/Mly9epXhy5cvYPzu3TsGO1trBkd7O4bZs+sZ/H29GSLCQhg83FwYLMzNGESA+kjFjGRgFAAALvspiQ==');
      DTM_RUNE[TYPE_RUNE_MIND] := DTMFromString('mlwAAAHicY2dgYDjIxMBwHIp3Q/ERIN4PxI8ZIPgBFIPYL4H4ORC7ODsyvFihwWBlac7g7eXB8GqTNpgGYRGgPC7MiAdDAQDaHxOy');
      DTM_RUNE[TYPE_RUNE_WATER] := DTMFromString('mrAAAAHic42BgYDjAhMC7gPgYEJ8F4r1QNgcjAwMLEPMwQtggzAbEokAsAMT6pesZdMs3MFQt2cfg6eHG4OLsxKCtrcUQGR7KkBAXzSACtAMfZiSAYQAAHOAP7Q==');
      DTM_RUNE[TYPE_RUNE_EARTH] := DTMFromString('m6wAAAHic42ZgYMhiguBiIM4F4hQouxxKJwNxElTNU6D6RwwQ+jkQvwLi+0D8GIjfQzFI/h4QOznaM8yp8maojpZmWNURyxAaEgRm29laM3h5ujPExUYxRISFMHi4uTCIANUTixlJwEgAAPNjGpI=');
      DTM_RUNE[TYPE_RUNE_FIRE] := DTMFromString('m6wAAAHic42ZgYDjABMEHgXgfEB8F4r1AfASIt0PxSSA+BcQvgOofA/F9KH4AxE+B+BmU/RaI30PlvoXrMPy6Zs3wa78dg4O9KcP9q8EMXyptGX4GqTIEB/kzxERFMDg7OjAYGxkyiADVE4sZScBIAADNxCHK');
      DTM_RUNE[TYPE_RUNE_BODY] := DTMFromString('mggAAAHicY2NgYDjIxMBwBIiPAvEBIN4DxLuAmI0RgrmBmBdKg/jOTg4Mf//+ZTA0nc/Q1LqXwch0AUNp+U4GEaBZ2DAjDgwBAKWvDn0=');
      DTM_RUNE[TYPE_RUNE_COSMIC] := DTMFromString('mrAAAAHic42BgYHBgYmDwAGI/IHYFYjcgdgJiKyC2BGJ2RgQWBmJuIGYCYgEgZgXivz/PMPz5UsBgY23FEBToz/Dnaw+Dm6szQ0JcNENocCCDCNAOfJiRAIYBAPlzD0s=');
      DTM_RUNE[TYPE_RUNE_CHAOS] := DTMFromString('m6wAAAHic42ZgYMhnYmDIAuJcIM6D0hlAXAzESVBcAsRlQMzGCMHMQMwFxLxAzATE7EDMCsRCQCwMZVtZWjLYWFsx/P37F4yfLFZleLJEA0irMISGBDHEREUwuLo4MViYmzGIAN1BLGYkASMBACzvFec=');
      DTM_RUNE[TYPE_RUNE_ASTRAL] := DTMFromString('mggAAAHicY2NgYJjOxMAwB4pnAvFEKNZmZGDQBGJdIDaA0lpAfOfYHYaJ3RsZPD3cGG5ee8wQ4O/DsH/jGQYRoFnYMCMODAEA9csQBA==');
      DTM_RUNE[TYPE_RUNE_NATURE] := DTMFromString('mrAAAAHic42BgYJjKxMAwE4inAPF0IO6B4vlAvBCIORkZGDiAmA+IWYCYDYiFgFgYyp5zNpJh3uV4hrBtKQx2ttYMwUH+DFERYQwO9rYMZqbGDCJAO/BhRgIYBgB1aQ7c');
      DTM_RUNE[TYPE_RUNE_LAW] := DTMFromString('mggAAAHicY2NgYDjEBMH7oXg3EB8DYk1GBgYDKNYGYh0odnJ0YNCce45Ba945Bnc3FyD7LBiLAM3ChhlxYAgAAJOqDgo=');
      DTM_RUNE[TYPE_RUNE_DEATH] := DTMFromString('m1gAAAHic42JgYMhgYmAoBOJcIC4C4jQgToRikFwZEJcDsSEjBKsCsSYQa0BpEyC2BGJzINYC4t+/f8Oxg70tQ3CQP0Ogvy9DTFQEQ0RYCIOzowODtZUFgwjQbmIwI5EYAQBkixXw');
      DTM_RUNE[TYPE_RUNE_BLOOD] := DTMFromString('mlwAAAHicY2dgYMhlYmDIA+I0IC6B4iSoWAgjA4MfEPsDsS8QR0BxFBDvk8th2CVaxODj5cFgb2fDEB0VzhAXE8UQGhzIIAI0FxdmxIOhAADo6Q0q');
      DTM_RUNE[TYPE_RUNE_SOUL] := DTMFromString('m6wAAAHic42ZgYIgH4mQgTgXiRCCOAeJIIA4D4mggjoLKZwBxMyMDQxMQ1wFxDRDXAnE9I0QchDuAuB2qxs/Xm2H5igcMc5feY3Cwt2VwdXYE84MD/cF0eFgwQ4C/L4O7mwuDCNBsYjEjCRgJAAB26Blr');
    
      PreemptCounter := 0;
    
      DTM_FIRST_FLOOR := DTMFromString('mbQAAAHicY2VgYFADYgkgFgdiLSBWAuIGIC4C4hogLgXiViD+/ZOJYVKdLkPvBDGGuFB2hjU72BlEgOLomBELBgMAfv8KAw==');
      DTM_SECOND_FLOOR := DTMFromString('mbQAAAHicY2VgYFAGYk0oLQXE0kBcBcQFQJwPxOVA3A7Ee2sfM2iq2zHcXvaFYcq8hQwyUsoMIkBxdMyIBYMBAKUACrY=');
      DTM_THIRD_FLOOR := DTMFromString('mbQAAAHicY2VgYFAGYkUglgdiaSCWAeJaIK4E4mIgbgbidpA6ZWWG6x+/M+x/8ophxtoNDIxAKAIUR8eMWDAYAAC72wso');
      DTM_RUNESPHERE := DTMFromString('mbQAAAHicY2VgYGADYh4g5mSAABYo5oaKsUPFu/o2Mjg5ZDCkff3FoPXqB4Pd1z8MIkBxdMyIBYMBACN6CfA=');
    
      DTM_YELLOW_WIZARD := DTMFromString('mWAAAAHicY2FgYNgLxHuAeDcQ7wfiAiBOBuJoIK4F4qgQP4a/1wsYXp3IYLixI4EBHTCiYRAAAJNrC/E=');
    
      AddOnTerminate('FreeMyDTMs');
      AppendSmartLine('DONE', True);
    
      LogSmartLine('Checking skill level for target selection...');
      StartTime := GetSystemTime;
      LastRightClick := GetSystemTime;
      SmartSetDebug(True);
      SkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
      AppendSmartLine('DONE (' + IntToStr(SkillLevel) + ')', True);
    
      LogSmartLine('Initiating startup sequence...');
      if not (InitialSetUp()) then
      begin
        LogSmartLine('Failed to set up, could be we could not get to runespan or obtain essences');
        Exit;
      end;
    
      LogSmartLine('Obtaining current XP reading...');
      if not IsXPBarOpen then ToggleXPBar(true);
      Players[CurrentPlayer].Integers[0] := GetXPBarTotal;
      LastXPTotal := 0;
      SetAngle(SRL_ANGLE_HIGH);
      AppendSmartLine('DONE (' + IntToStr(Players[CurrentPlayer].Integers[0]) + ')', True);
    
      LogSmartLine('Assessing map...');
      UpdateMap();
      AppendSmartLine('DONE', True);
      LogSmartLine('Generating map 3D projection cache...');
      Initialize3DModelFromMap();
      //SMART_ClearCanvas();
      AppendSmartLine('DONE', True);
    
      DrawMap();
    
    
      if TEST_MODELS then
      begin
        repeat
          TestModels();
          //Sleep(2000);
          //SMART_ClearCanvas();
        until not LoggedIn;
      end;
    
      // set up levels
      repeat
        // break target time
        TargetTime := GetSystemTime + (60000 * BREAK_TIME) - (60000 * BREAK_TIME_SPREAD) + RANDOM(120000 * BREAK_TIME_SPREAD);
        // do tiers set up
        LastOKTime := GetSystemTime;
        repeat
          EverythingOk := MainLoop;
          if (EverythingOk) then
          begin
            if (DesperatelyNeedsEssence) then
            begin
              writeln('desperately need essence');
              EverythingOk := GetSomeEssence();
            end;
          end;
          I := Random(360);
          case (I) of
          0..99 :
            begin
              antiban;
            end;
          end;
          CurrentTPAModel.XP := -1;
          //SMART_ClearCanvas();
          WriteStats;
          UpdateMap;
          DrawMap;
          wait(500+Random(300));
          // check sparsely
          if (Random(10) = 1) then
          begin
            NewSkillLevel := GetSkillLevel(SKILL_RUNECRAFTING);
            if (NewSkillLevel > 0) then
            begin
              if (SkillLevel > 0) then
              begin
                runecraftingLevelsGained := NewSkillLevel - SkillLevel;
                if (runecraftingLevelsGained > 0) then
                begin
                  WriteLn('Gratz on level up');
                  stats_IncVariable(STATS_LEVEL_RUNECRAFTING, runecraftingLevelsGained);
                  // we should rethink
                  SkillLevel := NewSkillLevel;
                end;
              end
              else
              begin
    
                SkillLevel := NewSkillLevel;
              end;
            end;
          end;
        until (not Loggedin) or (not EverythingOk) or (TargetTime < GetSystemTime);
        if not TAKES_BREAKS then exit;
    
        ExitToLobby;
    
        sleep ((45000 * BREAK_DURATION) + RANDOM(30000 * BREAK_DURATION));
        if not LoggedIn then
        begin
          LoginPlayer;
        end;
        MMouse(100,100,100,100);
        if SWITCH_WORLDS_BREAK then ChangeWorld(RandomWorld(Players[0].Member, False));
        Wait(4000 + Random(3000))
        FindNormalRandoms;
      until not TAKES_BREAKS;
    end.
    1 word: Pastebin

    Forum account issues? Please send me a PM

  15. #1290
    Join Date
    May 2012
    Posts
    1
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    it says cannot spawn client. it compiles though.

  16. #1291
    Join Date
    Jul 2006
    Posts
    13
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by [S]paz View Post
    This attitude is what drags, not only this community but, society down.
    No, THIS:

    Quote Originally Posted by footballjds View Post
    maybe i could, but then again maybe i don't have the time to help a leech.
    attitude is.

    Jackasses who think they're better than others because they have more knowledge in a certain topic (scripting in this case).

    Thanks to kyle for the updated script. I haven't tried it, but the effort is appreciated in any case

  17. #1292
    Join Date
    May 2012
    Location
    Somewhere in, PA
    Posts
    1,810
    Mentioned
    9 Post(s)
    Quoted
    226 Post(s)

    Default

    Quote Originally Posted by Voyaging View Post
    No, THIS:



    attitude is.

    Jackasses who think they're better than others because they have more knowledge in a certain topic (scripting in this case).

    Thanks to kyle for the updated script. I haven't tried it, but the effort is appreciated in any case
    Well, this site is a learning site and if you wanna be here, you have to learn some stuff. If you don't want to learn anything, well then there's Powerbot and such. You can't just ask for everything and expect to be given it.
    My First Build!, Selling Downloadable Games
    -------------------------------------

  18. #1293
    Join Date
    May 2012
    Posts
    2
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    I was trying to use your script and was hit with a:
    [Error] C:\Simba\Includes\SRL/SRL/misc/paintsmart.simba(42:33): Unknown identifier 'SmartGetDebugDC' at line 41
    Compiling failed.

    --any help?

  19. #1294
    Join Date
    Mar 2012
    Location
    Canada
    Posts
    442
    Mentioned
    4 Post(s)
    Quoted
    67 Post(s)

    Default

    Quote Originally Posted by Flamingluigi View Post
    I was trying to use your script and was hit with a:
    [Error] C:\Simba\Includes\SRL/SRL/misc/paintsmart.simba(42:33): Unknown identifier 'SmartGetDebugDC' at line 41
    Compiling failed.

    --any help?
    yeah use my updated script like 3 posts up

  20. #1295
    Join Date
    Mar 2012
    Location
    Canada
    Posts
    442
    Mentioned
    4 Post(s)
    Quoted
    67 Post(s)

    Default

    Quote Originally Posted by Zuriel View Post
    it says cannot spawn client. it compiles though.
    run as admin

  21. #1296
    Join Date
    Dec 2012
    Posts
    191
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    It doesn't do the air or mind ones, It only does the water ones for me

  22. #1297
    Join Date
    Nov 2011
    Posts
    45
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Well no paint (cause it kept interfering) but 99! Ran for 10+ hours, to bad I don't have the debug, I forgot about it.


  23. #1298
    Join Date
    May 2012
    Location
    Somewhere in, PA
    Posts
    1,810
    Mentioned
    9 Post(s)
    Quoted
    226 Post(s)

    Default

    Quote Originally Posted by Deadlast View Post
    Well no paint (cause it kept interfering) but 99! Ran for 10+ hours, to bad I don't have the debug, I forgot about it.

    Nice, what xp/hour were you getting? And which island?
    My First Build!, Selling Downloadable Games
    -------------------------------------

  24. #1299
    Join Date
    Nov 2011
    Posts
    45
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    Quote Originally Posted by Austin View Post
    Nice, what xp/hour were you getting? And which island?
    I was getting 45k XP/h on a F2P which was awesome. I was on the island right above the vine ladder (you can see the big island on the minimap)

  25. #1300
    Join Date
    May 2012
    Posts
    8
    Mentioned
    0 Post(s)
    Quoted
    0 Post(s)

    Default

    im getting this error, wat did i do wrong ? o.O

    [Error] C:\Simba\Includes\SRL/SRL/misc/paintsmart.simba(42:33): Unknown identifier 'SmartGetDebugDC' at line 41
    Compiling failed.

Page 52 of 57 FirstFirst ... 2425051525354 ... LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •