SRL Archive - Research & Development Lounge

Warrior's Guild - A development journal


Er1k

2012-05-21 02:03:46

I am sure most are familiar with the minigames - http://runescape.wikia.com/wiki/Warriors'_Guild. I was bored and I decided to see if it was at all possible to do something about the minigames.

- Animation room
- Catapult room
- Dummy room
- Keg room
- Shotput room

I have only started working on the first minigame, but I plan to work on others if I have incentives and time. This will be a development journal showing how it was done and some of the considerations I had when making. I hope this will open some of your eyes as to how a scripter makes his decisions, and hopefully learn something from it.

5/21/2012 The catapult minigame - Is it at all possible?

It was an idea I had sometime ago, and I decided to give it a go at one of the more challenging minigames among the 5, the catapult one.

You should know how it works. You get a shield from that guy, and you stand on the stand to be shot by the catapult and change your shield to different styles in order to block and earn points.

1. Setting up our player

The difficulty in doing the minigame mainly has to do with the fact that we have very little time to determine what the object is and switch to the correct style. So in order to make our lives easier, the first thing we do is to position ourselves at a better angle.


1. Getting object colors

Now it's up to me to decide how this is going to work. Apparently we need to get the colors of the projectiles so that we can detect them. And this is where the 2nd problem comes in - colors false positive with the background, which is not good news for us.

But before we look at our problem, we first define a simple data structure that will house our objects.

type
projectile = record
color, tol, minc: integer;
hmod, smod: extended;
name: string;
end;

var projectiles: array of projectile;

Now with our objects, after a bit of ACA-ing, I happen to only get some of the values…

  with projectiles[0] do
begin
color:= 1545178;
tol:= 7;
minc:= 30;
hmod:= 0.11;
smod:= 0.92;
name:= 'magic';
end;

with projectiles[1] do
begin
color:= 6841443;
tol:= 7;
minc:= 30;
hmod:= 0.48;
smod:= 0.07;
name:= 'stab';
end;

with projectiles[2] do
begin
color:= 8748670;
tol:= 8;
minc:= 25;
hmod:= 0.00;
smod:= 0.11;
name:= 'slash';
end;

This is not good news at first sight, because I have only managed to get 3 of the required objects out of 4 in ACA. But if you think about it more deeply, if a detection does not return the either 3 as the result, it's very likely the object is the last one that we didn't have. It's not perfect, but at least it will work and we are gonna use that.

3. Designing our script logic

The minigame itself is simple enough. You see Magic projectile, you click magic shield, and you do that for other projectiles. So our next step would be scripting the part where you click the correct style.


Apparently we can do the easy way where we simply get a point in each "box" and call Mouse(x, y, 15, 15, mouse_left) to deal with it. But that wasn't very satisfying and I wanted to use a Mousebox. To use a mousebox, however, I will have to do some trial and error to get the box areas with paintsmart.

procedure Draw;
var i: integer;
begin
for i:=0 to 3 do
SMART_DRAWBox(IntToBox(564, 232+56*3, 564 + 156, 232 + 49 +56*3));
// for i:=0 to 3 do
// SMART_DRAWBox(IntToBox(575, 235 + 57*i, 575 + 135, 235 + 40 +57*i));
end;

So I went do do some trial and error, and in a few minutes I get these values. They draw our boxes very well, and now we can proceed to code our functions.

In order to select a style, we need to decide which style is active at the moment, and whether we need to actually click another box to switch style. Effectively this means we need two functions - one that tells us what we're choosing now, another one that tells us to click a style.

function ActiveStyle(style: string): boolean;
var i: integer;
begin
case style of
'stab': i:=0;
'blunt': i:=1;
'slash': i:=2;
'magic': i:=3;
end;
SetColorToleranceSpeed(1);
result:= CountColorTolerance(3908062, 564, 232+56*i, 564 + 156, 232 + 49 +56*i, 27) > 100;
end;

This decides what is active. Active means the lighter yellowish-orange borders in the box. To play it safe, CTS1 with 25 tolerance has never really failed me in circumstances with menus, which colors are mostly static ;) Now, onto selecting the style, it keeps getting easier, isn't it?

function SelectStyle(style: string): boolean;
var i: integer;
v: TVariantArray;
begin
result:= true;
if ActiveStyle(style) then Exit;
case style of
'stab': i:=0;
'blunt': i:=1;
'slash': i:=2;
'magic': i:=3;
end;
MouseBox(575, 235 + 57*i, 575 + 135, 235 + 40 +57*i, mouse_left);
v:= [style];
if not WaitFuncEx('ActiveStyle', v, 100, 2000) then
result:=false;
end;

We use a simple WaitFuncEx here to make sure we the clicked style activates before returning the true/false values. In reality, however, we might not really need to check the success/failure, but the function is written to return a boolean just for the sake of easy debugging later.

4. Deciding which projectile to defend against

Here's the most important part of the script. Once we get onto the stand and gets barraged, we need to react to the projectiles and select the correct style. However, we know TPA finding is not perfect, and it might find the wrong object, so what we need to look for here is a more robust approach that gives high success rate. Also we must take care of the potential issue of switching between the styles too often.

Here is an example. Assume the object finder returns the following series of decisions:

Stab, magic, stab, stab, stab, blunt, slash, magic

So what could this object be? Common sense tells us it could more likely be stab than others, because for this series of decisions "stab" style has the most number of occurrence. We are going to use this in our decision making function.

function FindMode(a: TIntegerArray): integer;
var i, m: integer;
b: TIntegerArray;
begin
setlength(b, 4);
for i:=0 to high(a) do
Inc(b[a[i]]);
m:= max(b[0], max(b[1], max(b[2], b[3])));
for i:=0 to high(b) do
if b[i]= m then
result:= i;
writeln('mode: ' + IntToStr(m) + ' style: ' + IntToStr(result));
end;

The above concept is realized into the above block of code, returning the mode of a sample. In our application, we will call our object finders a number of times, and use the found mode as the guess of what the object might be. This is far from being perfect, but robust enough to give high success rates, given our object finders are accurate.

function DecideStyle: string;
var i: integer;
a: TIntegerArray;
begin
setLength(a, 7);
for i:=1 to length(a) do
FindProjectile(a[i-1]);
case FindMode(a) of
0: result:= 'stab';
1: result:= 'blunt';
2: result:= 'slash';
3: result:= 'magic';
end;
writeln('decide style: ' + result);
end;

Here is the meat we're looking for. We apply our knowledge of statistics and use the mode to guess the current projectile. We have also decided we do a sampling of 7 times and use the data to make our decision. Of course, this number can be changed, but remember TPA finding takes time and we need to balance between making a decision too quickly and missing the time to make a decision and get hit by the catapult.

5. Filling in the blank - setting up our object finder

All our pretty talk does not work if our object finder returns trash. This is also the most difficult part of setting up the script correctly - determining the correct style given the TPA finds.

I am not going to give the exact code here. It worked for me one day, it messed up on the other, so it's pretty much a trial and error thing, but with the code below you will start to see the logic and how the pieces fit together.

function FindProjectile(var v: integer): boolean;
var i, x, y: integer;
s: TIntegerArray;
tpa: TPointArray;
pass: boolean;
begin
SetLength(s, 3);
SetColorToleranceSpeed(2);
v:=-1;
for i:=0 to high(projectiles) do
begin
SetColorSpeed2Modifiers(projectiles[i].hmod, projectiles[i].smod);
FindColorsTolerance(tpa, projectiles[i].color, MSX1, MSY1, MSX2, MSY2, projectiles[i].tol);
s[i]:= length(tpa);
end;
writeln(format('magic: %d stab %d slash %d', [s[0], s[1], s[2]]));

//Process the colors
//Code your own logic here
if (s[0] >= projectiles[0].minc) then
v:=3 //magic
else if (s[1] >= 25) and (s[2] <= 25) then
v:= 0 //stab
else if {(s[1] <= 50) and} (s[2] >= 25) then
v:= 2 //slash
else if (v=-1) then //blunt
v:= 1;

case v of
0: writeln('projectile guess: stab');
1: writeln('projectile guess: blunt');
2: writeln('projectile guess: slash');
3: writeln('projectile guess: magic');
end;
end;

Basically what we do here is to iterate through our objects and get the color counts. Our decision will be based on how many colors of each style we find, and we decide what values will fit to which style. Because colors change all the time, something that worked yesterday might not work today, and I'm leaving it here for you to do the trial and error and relate the found color counts to a particular style.

To facilitate the process I also have a small snippet to do the sampling without select a style. This might come in handy for observing.

procedure Test;
var s: integer;
begin
while true do
begin
FindProjectile(s);
wait(100+random(50));
end;
end;

6. Putting it all together - main loop

This is quite self-explanatory. I never really expect it to be unattended, so I don't go over the crap of setting up the player angles with the script. Also FindNormalRandoms seems to misdetect the area as Cap'n Arnav random, so I am forced to drop it here. All in all, it will be a babysit script, you will eventually die depending how well your colors fit and how well the object finder makes the correct decisions, but for an attempt to simply cut you some slack in playing the minigame, it's a good try nevertheless.

procedure MainLoop;
var style: string;
begin
reincarnate:= true;
while LoggedIn do
begin
// FindNormalRandoms;
FindDead;
SelectStyle(DecideStyle);
wait(100+random(50));
end;
end;

End result


The script is made and tested in action. Needless to say I died a few times, but with some tweaking and babysitting for an hour or 2, I have already accumulated enough tokens for hours of defender hunting if used in the Multi-token mode.

Script is attached for reference. ACA colors might need redo, and you're expected to make FindProjectile work for your own circumstances. It's one of the more difficult challenges for which I don't think it's worth the efforts to make it fully automatic, so I will just leave it here for those who're willing to DIY to get their tokens ;)

J J

2012-05-21 09:11:52

Nice progress so far :D
I was thinking about making a Warriors Guild script aswell, but mine would only kill the animated sets and the cyclopses. This is even better :)
What about adding Cyclops Killing aswell to make it truly AIO?

Er1k

2012-05-21 10:32:57

From: J J
Nice progress so far :D
I was thinking about making a Warriors Guild script aswell, but mine would only kill the animated sets and the cyclopses. This is even better :)
What about adding Cyclops Killing aswell to make it truly AIO?


AIO is not worth the efforts in my opinion ;) They're very different minigames and some of them I think should only be babysit, say catapult.

As for cyclope killing, it's another thing as well. One simple way is to put narcle and deal with it. Not on my list anyway :P

The animation game has been on my todo list for my while ;) I am not sure if it is next on my list though. But I am guessing a little competition can't really hurt.

cause

2012-05-21 14:55:15

Nice work dude! A question for you, what is your current success rate with defending against the correct incoming object? A method that just popped into my head while reading your blog would be to just do getcolor() on a point where the projectile crosses every time (I don't know if all 4 projectile types take the exact same route every time) and when it returns a different value than whatever the background color would be, make some type of guess at which projectile it was using a tolerance at first then checking if exp/token were gained do determine if that was a correct prediction. Anyways I'm rambling, nice work!

Er1k

2012-05-21 21:05:16

From: cause
Nice work dude! A question for you, what is your current success rate with defending against the correct incoming object? A method that just popped into my head while reading your blog would be to just do getcolor() on a point where the projectile crosses every time (I don't know if all 4 projectile types take the exact same route every time) and when it returns a different value than whatever the background color would be, make some type of guess at which projectile it was using a tolerance at first then checking if exp/token were gained do determine if that was a correct prediction. Anyways I'm rambling, nice work!


It highly depends on the colors imo. If I set up the FindProjectile correctly and the colors don't change a lot, the success rate can be more than 80%. In fact there was one day where I got quite lucky and it basically played the minigame with only 5 fails for 20-30 minutes.

Usually if it doesn't go well, it will fail a particular object more often, so it would be 60-75% in average.

awesomem8

2012-06-13 12:12:08

no offence but one of the scripters made me a token script within hours of requesting it and i used it to get 65k tokens within a day. link: http://villavu.com/forum/showthread.php?t=69404

J J

2012-06-13 14:33:41

From: awesomem8
no offence but one of the scripters made me a token script within hours of requesting it and i used it to get 65k tokens within a day. link: http://villavu.com/forum/showthread.php?t=69404

What is the point of posting this? Er1k is obviously working on a much more advanced way of getting tokens, which is a harder activity aswell. The script you used barely used any randomization.

Anyways, are you still working on this Er1k?

Olly

2012-06-13 14:37:49

From: J J
What is the point of posting this? Er1k is obviously working on a much more advanced way of getting tokens, which is a harder activity aswell. The script you used barely used any randomization.

Anyways, are you still working on this Er1k?


I believe he isn't due to the fact he wanted to make a aio warriors guild but didn't want to undertake killing the monsters that drop the defenders, I may be wrong though.

Er1k

2012-06-13 21:26:08

Well I am actually on a break, but last time I was still scripting, I had the dummy game half completed. ;) Going on a vacation next week, so I aint sure ;)