Foundry
2014-03-01 23:33:37
Anyone who has seen my application knows that I have been working on an include for Wurm Online. Since I first applied I have rewritten a good bit of it and am looking to improve it even further. I'm gonna post stuff here periodically when I think it is good to get second opinions. I understand hardly anyone plays Wurm, so here is a picture of the regular interface (you can move all of the windows around, this is just the way I like it). [ATTACH=CONFIG]23034[/ATTACH]
If you want to take a look at the whole deal, the Github is located here.
Today I bring you…
All of the functions that handle the chatbox.
… and the functions for the toolbelt.
If you want to take a look at the whole deal, the Github is located here.
Today I bring you…
All of the functions that handle the chatbox.
{$include_once srlutilities\srlutilities.simba}
// The functions in window getter get the bounds of interfaces based
// on a text file Wurm saves the window positions to on logout.
{$include_once windowgetter.simba}
{==============================================================================]
TYPE
----
A type that will have the bounds of the chatbox, tabs, etc.
[==============================================================================}
type
TChatboxTab = record
Bounds: TBox;
Text: String;
Active, Red: Boolean;
end;
TChatbox = record
Bounds, SpecialBox: TBox;
Tabs: array of TChatboxTab;
end;
// By Foundry
{==============================================================================]
VAR
---
These two variables are for the right and left chat tabs and such.
[==============================================================================}
var
LocalChat, EventChat: TChatbox;
// By Foundry
{==============================================================================]
CONSTANTS
---------
Just the colors of the text in the different tabs. Also some to represent
different tabs.
[==============================================================================}
const
CHAT_ACTIVE = 16777215;
CHAT_RED = 51;
CHAT_INACTIVE = 0;
TAB_LOCAL = 'Local';
TAB_CA_HELP = 'HELP';
TAB_GL_FREEDOM = 'GL-Freedom';
TAB_FREEDOM = 'Freedom';
TAB_EVENT = 'Event';
TAB_FRIENDS = 'Friends';
TAB_SUPPORT = 'Support';
TAB_SKILLS = 'Skills';
// By Foundry
{==============================================================================]
Gets the bounds of the chatbox by reading the txt file in the player folder.
[==============================================================================}
function TChatbox.getBounds(EventChat: Boolean = false): TBox;
var
SearchFor: String = 'chat';
begin
if not((Bounds.x2 = 0) and (Bounds.y2 = 0)) then
exit(Bounds);
if EventChat then
SearchFor := 'event';
if not getStoredBounds(SearchFor, Bounds) then
begin
writeLn('Couldn''t get the bounds of the chatbox! Did you enter the wrong' +
' char name?');
exit([-1, -1, -1, -1]);
end;
if EventChat and (Bounds.x1 = Bounds.x2) then
Bounds.x2 := ResX;
exit(Bounds);
end;
// By Foundry
{==============================================================================]
Gets the tabs of the chatbox and also sets the current tab. Will take a look
at it later and add comments.
[==============================================================================}
function TChatbox.getTabs(): Boolean;
var
textcolors, atpa: T2DPointArray;
i, j, k: Integer;
copy: TBox;
const
colors = [CHAT_INACTIVE, CHAT_RED, CHAT_ACTIVE];
begin
copy := getBounds();
copy.y2 := copy.y1 + 15;
setLength(textcolors, 3);
for i := 0 to high(colors) do
begin
if findColors(textcolors[i], colors[i], copy) then
begin
atpa := textcolors[i].cluster(6, 2);
atpa.filterBetween(0, 15);
for j := 0 to high(atpa) do
begin
if (inRange(atpa[j].getBounds().getHeight(), 8, 15)) and
(atpa[j].getBounds().getWidth() > 10) then
begin
setLength(Tabs, k + 1);
Tabs[k].Active := (i = 2);
Tabs[k].Red := (i = 1);
Tabs[k].Bounds := atpa[j].getBounds();
try
Tabs[k].Text := atpa[j].getBounds().getText(colors[i]);
except begin
writeLn('Failed to get a tab... must have been a false positive.');
continue;
end;
end;
result := true;
inc(k);
end;
end;
end;
end;
end;
// By Foundry
{==============================================================================]
Clicks the tab. Relies on the bounds stored from getTabs().
[==============================================================================}
procedure TChatboxTab.click();
begin
mouseBox(Bounds, MOUSE_LEFT);
end;
// By Foundry
{==============================================================================]
Clicks the tab with the given text.
[==============================================================================}
function TChatbox.setTab(TabName: String): Boolean;
var
i: Integer;
textmatch: Boolean;
begin
getTabs();
for i := 0 to high(Tabs) do
begin
if (TabName = TAB_GL_FREEDOM) or
(TabName = TAB_FREEDOM) then
textmatch := (TabName = Tabs[i].Text)
else
textmatch := (pos(TabName, Tabs[i].Text) > 0);
if not textmatch then
continue;
if not Tabs[i].Active then
Tabs[i].click();
exit(true);
end;
end;
// By Foundry
{==============================================================================]
Gets the bounds of the local name box. Uses the arrows that are located on the
left, right, and bottom.
[==============================================================================}
function TChatbox.isTabPresent(TabName: String): Boolean;
var
i: Integer;
begin
getTabs();
for i := 0 to high(Tabs) do
begin
if (pos(TabName, Tabs[i].Text) > 0) then
exit(true);
end;
end;
// By Foundry
{==============================================================================]
Gets the bounds of the local name box. Uses the arrows that are located on the
left, right, and bottom.
[==============================================================================}
function TChatbox.getLocalNameBox(): Boolean;
var
LeftArrow, RightArrow, UpArrow: Integer;
begin
LeftArrow := BitmapFromString(8, 9, 'meJxz8gr1CIp1DoqydQ+0dguwcvfn' +
'FxB29Yt2CQh39gmx9wx2D4kXFBblFxDKL6sBMoLikr1Co0XEJMUkp' +
'Bvb+upaOpra+4RExF39I4GC9a09K9ZsXLJi/eoNW+YtXi4sJsnHL5' +
'iZV1xW07Rr/+Enz18eOXm2d9J0MQkZIysXASFhY3Pb9OyiC1eu7z1' +
'4tHfqDGFRCV0zBx0zex5+QQEhUVsnN1fvwOqGdgEhESsXH0uXQGvP' +
'QDMnb34hYX5Boay8EqC4d0isX1hcYGSCR2Ckd0i0T2i0kIgYkAsA4' +
'KhFmw==');
RightArrow := BitmapFromString(8, 9, 'meJxz8AwTEZVw8o+ycvG18Qh28I2w' +
'94nwDUkUlZAys3IQFZfiFxK29w5z8A538o3wi0wRlZD2D46YMHVOe' +
'88kQWFRASFRK3dvB99gYVGJwIi4leu3rN+yc9O2nVNmLRYRk3D2CR' +
'cUFnN28920Y++Z85ev3ry9btP2grJqIRExfiERGwf3GXMXHThyYu+' +
'BozWN7UDTrD18gaSJpZ2Vg4unX5CYpIyYhLS+paOJvRvQPZZ2rqLi' +
'kiLikk4+oe5BkXZeAb6R8UGxiQLCop4B4e7+YXbuPj7BEd5B4W4+o' +
'QCtPkM8');
UpArrow := BitmapFromString(9, 8, 'meJxz9Apz8Amz9Qyy8woVFBa19Q71' +
'DI73Dkn0D4m38Qi0dPI2sXcXEBLJK6oSEhHzi0jxi0kLiEzwi4h38' +
'AoGqm9s6+6eOCM0JkFMUsY3Os43LNo9NCEmp1xSRn7vgcM3bt+fu2' +
'iFlIyiR2CUm2+YhbOPsKjEpOlz123edvHK9YNHT7j5BAqLigdGJ5r' +
'YugPNb+uZNHHa3C07967btD0lKw8o5Rkczi8orGNoamZt5xcUmZpV' +
'EBwV5xMUbmZlD3SVvW+EiJiEiLikhLSspIycrIKSpLSsmISUR0AYA' +
'Bp+RHg=');
if findBitmapToleranceIn(LeftArrow, SpecialBox.x1, SpecialBox.y2, Bounds, 10) and
findBitmapToleranceIn(UpArrow, SpecialBox.x1, SpecialBox.y1, intToBox(SpecialBox.x1 - 50, 0, SpecialBox.x1 + 15, SpecialBox.y2 - 10), 10) and
findBitmapToleranceIn(RightArrow, SpecialBox.x2, SpecialBox.y2, intToBox(SpecialBox.x1 + 20, SpecialBox.y2 - 30, SpecialBox.x1 + 200, SpecialBox.y2 + 30), 10) then
begin
SpecialBox.x2 := SpecialBox.x2 + 20;
result := true;
end else begin
result := false;
writeLn('Failed to find the local name box.');
end;
freeBitmaps([LeftArrow, RightArrow, UpArrow]);
end;
// By Foundry
{==============================================================================]
This gets the bounds of the timer bar (the one that fills up as you are doing
an action) and returns whether it found it or not.
[==============================================================================}
function TChatbox.getTimerBox(): Boolean;
var
tpa: TPointArray;
atpa: T2DPointArray;
begin
getBounds(true);
SpecialBox.edit(Bounds.x1, Bounds.y2 - 30, Bounds.x2, Bounds.y2 - 16);
result := (countColorTolerance(5069157, SpecialBox, 6, colorSetting(2, 0.31, 0.55)) > (SpecialBox.getWidth() * 2))
if not result then
begin
writeLn('Failed to find the timer bar!');
terminateScript();
end;
end;
// By Foundry
{==============================================================================]
Snatches the names from the local name box.
[==============================================================================}
function TChatbox.ignoreLocalNames(): Boolean;
var
i, j: Integer;
tpa, tpa2: TPointArray;
atpa: T2DPointArray;
tmpstr: String;
begin
if findColors(tpa, BLUE_TEXT_COLOR, SpecialBox) or
findColors(tpa, 8454016, SpecialBox) then
begin
findColors(tpa2, 16777215, SpecialBox);
tpa.combine(tpa2);
atpa := tpa.cluster(SpecialBox.getWidth(), 3);
atpa.filterBetween(0, 10);
for i := 0 to high(atpa) do
begin
tmpstr := extractTextFromTPA(atpa[i], 1, 4, 3, 'WurmF');
// Ignore event actions from anyone but the player.
if not (lowercase(tmpstr) = lowercase(CharName)) then
begin
setLength(IgnoreNames, j + 1);
IgnoreNames[j] := tmpstr;
inc(j);
end;
end;
exit(true);
end else
begin
writeLn('Failed to grab local names.');
terminateScript();
end;
end;
// By Foundry
{==============================================================================]
Initializes the chatbox. If EventChat is true it will initialize as the
right side event chat.
[==============================================================================}
procedure TChatbox.init(EventChat: Boolean = false);
begin
getBounds(EventChat);
if EventChat then
begin
if (not isTabPresent(TAB_EVENT)) then
mouse(Bounds.getMiddle().rand(15), MOUSE_LEFT);
if (not getTimerBox()) then
begin
writeLn('Couldn''t set up timer box');
terminateScript();
end;
exit;
end;
if not setTab(TAB_LOCAL) then
writeLn('Couldn''t set the tab to local');
if (not getLocalNameBox()) or (not ignoreLocalNames()) then
begin
writeLn('Couldn''t set up the chatbox. Is your resolution right?');
terminateScript();
end;
end;
// By Foundry
{==============================================================================]
Checks to see if a GM is in local by searching for GM_COLOR in the local name
box.
[==============================================================================}
procedure TChatbox.GMCheck();
begin
if SpecialBox.colorExists(GM_COLOR) then
begin
writeLn('A GM IN LOCAL!!! ABANDONING SHIP!!!');
// play sound here if sounds enabled.
terminateScript();
end;
end;
// By Foundry
{==============================================================================]
This returns whether or not the given 'Activity' is being performed.
[==============================================================================}
function TChatbox.doingActivity(Activity: String = 'None'): Boolean;
begin
if SpecialBox.colorExists(7955803, 14) then
begin
if Activity = 'None' then
exit(true);
if SpecialBox.colorExists(16777215) then
begin
if pos(Activity, SpecialBox.getText()) > 0 then
exit(true);
end else
if pos(Activity, SpecialBox.getText(45568)) > 0 then
exit(true);
end;
end;
// By Foundry
{==============================================================================]
Waits for the current activity to finish. Will work on queues.
[==============================================================================}
function TChatbox.waitOnActivity(Activity: String = 'None'): Boolean;
var
t, randomtimeout: Integer;
begin
randomtimeout := randomrange(2000, 4000);
t := getSystemTime();
result := true;
repeat
wait(200 + random(300));
if getSystemTime() - t > randomtimeout then
begin
writeLn('waitOnActivity couldn''t detect activity after ' + toStr(round((getSystemTime() - t) / 1000)) + ' seconds.');
exit(false);
end;
until(doingActivity(Activity));
repeat
wait(randomrange(900, 1200));
if getSystemTime() - t > 200000 then
begin
writeLn('waitOnActivity timed out after ' + toStr(round((getSystemTime() - t) / 1000)) + ' seconds.');
exit(false);
end;
until(not doingActivity(Activity));
end; … and the functions for the toolbelt.
{$include_once srlutilities/srlutilities.simba}
{$include_once hotkeys.simba}
type
// By Foundry
{==============================================================================]
TYPE
----
The TToolbeltSlot type holds the data for a given slot in the toolbelt.
[==============================================================================}
TToolbeltSlot = record
Box: TBox;
ID: Integer;
end;
// By Foundry
{==============================================================================]
TYPE
----
The TToolbelt type holds the data for th whole toolbelt. Such as the bounds
and currently active slot.
[==============================================================================}
TToolbelt = record
Bounds: TBox;
Slots: array of TToolbeltSlot;
ActiveSlot: Integer;
end;
// By Foundry
{==============================================================================]
GLOBAL VAR
----------
Toolbelt record that is automatically initialized at script startup.
[==============================================================================}
var
Toolbelt: TToolbelt;
// By Foundry
{==============================================================================]
Returns whether or not the toolbelt was found. If it is, then it will apply
the bounds.
[==============================================================================}
function TToolbelt.getBounds(): TBox;
var
BoxesToCheck: TBoxArray;
i, j: Integer;
begin
// If the bounds have already been found then no need to find them again.
if Bounds.x2 > 0 and Bounds.y2 > 0 then
exit(Bounds);
if getStoredBounds('toolbelt', Bounds) then
begin
Bounds.x1 := Bounds.x1 + 39;
Bounds.x2 := Bounds.x1 + 200; // This is the maximum length the toolbelt can
// be. It can go up to 9 slots.
Bounds.y2 := Bounds.y1 + 20;
end else begin
writeLn('Couldn''t get the bounds of the toolbelt.');
terminateScript();
end;
// I just tinkered with this until it worked.
BoxesToCheck := grid(7, 1, 20, 15, 28, 0, point(Bounds.x1 + 13, Bounds.y1 + 17));
// The following checks each box made by the grid to see if it is a part of the
// toolbelt.
for i := 0 to high(BoxesToCheck) do
begin
if countColorTolerance(2307923, BoxesToCheck[i], 5, colorSetting(2, 0.26, 1.07)) > 80 then
begin
setLength(Slots, j + 1);
Slots[j].Box := BoxesToCheck[i];
inc(j);
end;
end;
end;
// By Foundry
{==============================================================================]
Applies the improvement type (actually the tool type, same thing) to each slot
object by searching in the slot box.
[==============================================================================}
procedure TToolbelt.getImpTypes();
var
i: Integer;
begin
for i := 0 to high(Slots) do
begin
Slots[i].ID := getImpType(Slots[i].Box);
end;
end;
// By Foundry
{==============================================================================]
Initializes the toolbelt. If it fails it will display a nice error message.
[==============================================================================}
procedure TToolbelt.init();
begin
getBounds();
getImpTypes();
end;
// By Foundry
{==============================================================================]
The wonderful part of this file. This function returns whether or not you have
the tool for a given job in your toolbelt.
[==============================================================================}
function TToolbelt.isToolPresent(ID: Integer): Boolean;
var
i: Integer;
begin
for i := 0 to high(Slots) do
begin
if Slots[i].ID = ID then
exit(true)
else if (ID = CHISEL) and // Chisel and knife are the same bitmap
(Slots[i].ID = KNIFE) then
exit(true);
end;
end;
// By Foundry
{==============================================================================]
Like the above function but this returns the slot in which that tool is found.
[==============================================================================}
function TToolbelt.getToolSlot(ID: Integer): Integer;
var
i: Integer;
begin
for i := 0 to high(Slots) do
begin
if Slots[i].ID = ID then
exit(i + 1)
else
if (ID = CHISEL) and
(Slots[i].ID = KNIFE) then
exit(i + 1);
end;
end;
// By Foundry
{==============================================================================]
Moves the mouse to the desired slot of the toolbelt.
[==============================================================================}
procedure TToolbelt.mouseSlot(Slot: Integer; clicktype: Integer = MOUSE_MOVE);
begin
Slots[Slot - 1].Box.mouse(clicktype);
end;
// By Foundry
{==============================================================================]
Activates the desired tool in the toolbelt by either clicking on it or
pressing the corresponding key.
[==============================================================================}
procedure TToolbelt.activateSlot(Slot: Integer; click: Boolean);
begin
if click then
mouseSlot(Slot, MOUSE_LEFT)
else
WurmPressKey(toStr(Slot));
end;