Jump to content
  • Sky
  • Blueberry
  • Slate
  • Blackcurrant
  • Watermelon
  • Strawberry
  • Orange
  • Banana
  • Apple
  • Emerald
  • Chocolate
  • Charcoal
slacky

Help porting (RS07) MapWalking function from Python.

Recommended Posts

Hello there!

I made this (map) walking-function some days ago. I initialy inteded to port my SPS(similar)-function but was hard to get it to work on runescape 2007 (oldschool), but very possible - but it requres me to create a plugin for scar (speed is of the essence).

I think there's small hunger for a simple mapwalk-function on this forums (for rs 2007)... So here I am to share my code, and I hope that We can together; quckly port it to pascal (SCAR), and take it further.

 

Short: I need help porting it to pascal from python, this is due to me having little knowledge of Pascal, and the unfamiliar syntax makes me nauseous :P

- To be honest, I'm also very lazy...

 

If your willing to help, or atleast take a look, the code is hosted over _HERE_

All questions will be answered, and I will also be tryin' to port it, but I'm a tolerant guy, so it might take a little time if I'm to port it alone.

See "edit"... It's ported.

 

 

(Path making tool) for testing and development:

I just wrapped together a (browser) javascript function to make paths. As I made it a little to quick it will contain some "undocumented features".

URL: http://slacky.site90.com/

 

Known bugs for the path making tool:

- 1.a: It does not work in firefox (FF handles mouseevents another way: had a hard time remembering how..).
- 1.b: [i]Chrome[/i], [i] Opera[/i] should work, and IE might work.

- 2.a: In any broweser: [u]Enable active catche[/u] -> This way you dont have to reload images each time.
- 2.b: Image not showing? When loading a new map/image -> [u]wait a few secounds[/u] (while image loads *not visible*) -> click "change" again. (requires active catching)

- 3.: Not a bug -> But [i]jpg, png and gif[/i] does not work: Use [i]bmp[/i]. Max width/height/size for upload is 500*500, 1MB
- 4.: The once I HAVE UPLOADED might be VERY inaccurate (made in 5 sec).
- 5.: Not optimized for long walks. Short walks works just fine. (well as soon as the algo is finished i mean...)

 

 

 

= - - - - - - - - - - - E D I T - - - - - - - - - - - =

So, we have eventually managed to port it. Not to bad, not to bad!! (thanks for all the help) :)

A couple of questions have rised, bacuse it wont actually work as well in rs07 (or any later..), the minimap seems to be dynamicly resizing (90-110%), and a method to ensure that we dont miss our target needs to be created, I was tinking object matching - create a set of objects (sizes, colors, tolerance.. etc), use am to triangulate the offset from the initial point we were to walk and correct it: That way our "walker" dont get lost and eventually fails... But it requires (me) to write a set of "hardcore" functions, since i could not find a function in scar to find multiple DTMs also tolerating affine transformation of the objects.

Edited by slacky

Share this post


Link to post
Share on other sites
[SIZE=4][B]Updated post![/B][/SIZE]
So far i've got the following, which works:

[Code]
{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
| It's a form for radialWalk, but works a little different, I believe.
| I find it simpler, yet accurate enough for small straws...
| - It MIGHT contain bugs'n'flaws.
|
| - Slacky
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
const
//Minimap START and END
MMX1 = 578; MMY1 = 10;
MMX2 = 710; MMY2 = 142;
//Minimap CENTER
MMCX = 644; MMCY = 77;
//Minimap radius
MMRAD = 66;


{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
| Creator: Janilabo
| Destructor: Slacky... I removed some security stuff :p
| Explanation: Returns the compass angle as extended. [0.0 - 360.0]
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
function GetCompassAngle: Extended;
var
mc, mn: TPoint;
nTPA: TPointArray;
sTPA: T2DPointArray;
begin
{Security check routunes should be called here.}
Result := -1;
if FindColorEx(nTPA, 1911089, 546, 1, 587, 39) then
begin
sTPA := SplitTPA(nTPA, 2);
SetLength(nTPA, 0);
SortATPABySize(sTPA);
nTPA := CopyTPA(sTPA[High(sTPA)]);
SetLength(sTPA, 0);
mc := Point(561, 20);
mn := TPAMean(nTPA);
SetLength(nTPA, 0);
Result := (Degrees(ArcTan2(-(mn.Y - mc.Y), mn.X - mc.X)) - 90);
if (Result < 0) then
Result := (Result + 360);
end;
end;


{CLASS FindMyFlag;}
// Find the flag using Bitmap (tip from janilabo)
function FindFlagBitmap: Boolean;
var
FlagBMP: TSCARBitmap;
x,y: Integer;
begin
FlagBMP := TSCARBitmap.Create('deNpjZ+BnwAT/sYn8xybyH5vIf2wi/7GJ4EKY4FgYyyAUBACnrST7');
FlagBMP.TranspColor := 0;
Result:= FindBitmapTol(x,y, FlagBMP, 570, 6, 714, 151,4);
FlagBMP.Free;
end;

// Same as above, but using TPA.
function FindFlagTPA: Boolean;
var
TPA: TPointArray;
ATPA: T2DPointArray;
i: Integer;
begin
Result := False;
if FindColorEx(TPA, 255, 570, 6, 714, 151) then
begin
ATPA := SplitTPA(TPA, 1);
for i := 0 to High(ATPA) do
if (Length(ATPA[i]) >= 26) AND (Length(ATPA[i]) <= 50) then
Result := True;
end;
end;

// Now we can try to find the flag
function FFlag: Boolean;
begin
{Security check routunes should be called here.}
Result := False;
if FindFlagBitmap then
Result := True
else if FindFlagTPA then
Result := True;
end;
{EndCLASS;}


// Get the degrees between two coordiants
function CalcPointAngle(c, p: TPoint): Extended;
begin
Result := Degrees(ArcTan2(-(p.Y - c.Y), p.X - c.X)) - 90;
if (Result < 0) then
Result := (Result + 360);
end;


// Is coordiant within A pieslice of a circle?
function InSectorArea(p: TPoint; o: TPoint; sector: Extended): Boolean;
var
center: TPoint;
angle, newPointAngle: Extended;
differ: Integer;
begin
Result := False;
center := Point(MMCX, MMCY);
newPointAngle := CalcPointAngle(center, p);
angle := CalcPointAngle(center, o);

differ := round(angle - newPointAngle);
differ := round(Abs((differ + 180) mod 360 - 180));

if(differ < sector) then
Result := True;
end;


// Is the coordiants within the minimap circle bounds?
function OnMinimap(x,y: Integer): Boolean;
var
center: TPoint;
radius: Integer;
dist: Extended;
begin
Result := false;
center := Point(MMCX, MMCY);
radius := MMRAD;
dist := Sqrt(Pow((center.x - x),2) + Pow((center.y - y),2));
Result := dist <= radius;
end;


//Rotate a point around center.
function RotatePoint(p, o: TPoint; dgrs:Extended): TPoint;
var
dx,dy: Integer;
newx,newy: Integer;
begin
dx := p.X - o.X;
dy := p.Y - o.Y;

newx := Round((dy*Sin(Radians(dgrs))) + (dx*Cos(Radians(dgrs))) + o.X);
newy := Round((dy*Cos(Radians(dgrs))) - (dx*Sin(Radians(dgrs))) + o.Y);

Result := Point(newx,newy+7);
end;


// More paramters TBA. Thies are not very optimal.
procedure RandomAct;
var
x,y,randCase: Integer;
begin
{Security check routunes should be called here.}
Wait(RandomRange(150,1200));
GetMousePos(x, y);
randCase := RandomRange(1,24);
case randCase of
1..15: begin
MoveMouseEx(x+RandomRange(-12,11), y+RandomRange(-12,12), RandomRange(9,15));
Wait(RandomRange(10,600));
end;
15..18: MoveMouseEx(x+RandomRange(-50,55), y+RandomRange(-47,57), RandomRange(13,23));
19..24: begin
MoveMouseEx(x+RandomRange(-26,38), y+RandomRange(-40,32), RandomRange(15,22));
Wait(RandomRange(700,1600));
end;
end;
end;


{=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
| Contributor: Slacky
| Description: Walks one the minimap of RS07
| Params:
| - path: Points to walk. IE: [(9,-45),(10,15)]...
| - colors: Eather specify a color for each point, or just one golobal color.
| - tolerance: One tolerance for each point, OR one singel global tolerance.
| - Sector: Angle size to search within: 30 will be 60* sight .:. 10 will be 20*
| - Rotation: Allow for rotated compass? I wouln't recomend it.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=}
function MapWalk(path: TPointArray; color, tolerance: TIntArray; sector: Integer; Rotation: Boolean): Boolean;
var
colorArray, tolArray: Boolean;
i, j, clr, tol, x,y: Integer;
Timer: Integer;
W,OP: TPoint;
TPA: TPointArray;
begin
Result := False;

colorArray := True;
tolArray := True;
if(Length(color) = 1) then colorArray := False;
if(Length(tolerance) = 1) then tolArray := False;

for j:=0 to High(path) do
begin
{Security check routunes should be called here.}
OP := Point(path[j].x+MMCX, path[j].y+MMCY);

//rotate around self or center..? Hmm. (not desided yet)
if Rotation then
W := RotatePoint(OP, Point(MMCX,MMCY), GetCompassAngle())
else
W := Point(OP.X, OP.Y+7);

//One color per point?
if colorArray then clr := color[j]
else clr := color[0];

//Seperate tolerance per point?
if tolArray then tol := tolerance[j]
else tol := tolerance[0];

if FindColorTolEx(TPA, clr, MMX1, MMY1, MMX2, MMY2, tol) then
begin
if Length(TPA)<5 then Exit;

SortTPASpiralEx(TPA, W);
for i:=0 to High(TPA) do
begin
{Security check routunes should be called here.}

x := TPA[i].X + RandomRange(-2,2);
y := TPA[i].Y + RandomRange(-2,2);

if not OnMinimap(x, y) then
begin
if (i = High(TPA)) then Exit
else Continue;
end;

if not InSectorArea(Point(x, y), W, sector) then
begin
if (i = High(TPA)) then Exit
else Continue;
end;

MoveMouseEx(x, y, 24);
Wait(40 + Random(20));
MouseBtnDown(x, y, mbLeft);
Wait(30 + Random(20));
MouseBtnUp(x, y, mbLeft);

if Random(5)=0 then RandomAct;

//Wait for flag to appear (high ping, lagg etc):
for Timer:=0 to 16 do
begin
if not FFlag then Wait(50 + Random(100)) else Break;
if Timer=16 then exit;
end;

//Wait for flag to disappear:
Wait(1200 + Random(800))
for Timer:=0 to 151 do
begin
if FFlag then Wait(200 + Random(300)) else Break;
if Timer=151 then exit; //40-50 sec wasted.
end;

Wait(100 + Random(300));
Break;
end;
end
else Exit;
end;

Result := True;
end;

//Test me... when we get that far: Start at Al'kharid gate.
var
color, tol: TIntArray;
path: TPointArray;
begin
path := [Point(-52, -38), Point(-52, -38)];
color := [16777215, 16777215];

tol := [202,250];
ColorToleranceSpeed(0);
MapWalk(path, color, tol, 2, false);
end.
[/Code]
If anyone got any improvements, or se any bugs (or things that might not work yet), please speak now! :)
- This is just "prototyping" there might be some bugs.

[I]14. March - 19.13: [/I]
Well, it seems to have come to a point where it gets a little rough... And I don't believe I can solve the rest any simple way. I would prob. need to implement some sort of object recognition to triangulate the offset and correct it. The thing is, with enough time I might be able to figure out how to do this, but am I really that motivated to do ALL THAT?
- "FindDTM" does not cut it, since it only finds the first, and I could not find a function like "FindDTM[B]s[/B](myDTM)" (which sould find all DMs)... [I]Does this exist[/I]?


Updatelog: Last add: 13. March - [I]14:47 GMT+1[/I]
[CODE]
╠ 11. March - 13:20: Started porting the source from Python.
╠ 11. March - 17:37: Code did compile, and it seems to work. CTS(0) gave my walk path best result;
╠ 12. March - 06:57: Fixed bug in function inSectorArea(*..*);
╠ 12. March - 07:35: Use CTS(0), CTS(1)... Others might work, but I don't count on it.
╠ 12. March - 13:06: Fixed some bugs/mistakes. Now it's pretty similar to python-version.
╠ 12. March - 15:29: CompassAngle independent! Walks at any angle ;)
╠ 12. March - 17.35: Added some randomness + Fixes
╠ 12. March - 23.37: Better flag-finding + some fixes, and descovered a bug with InSectorArea().
╠ 13. March - 14.44: Fixed bug mentioned above, I rewrote InSectorArea(). And some other bugfixes.
[/CODE]


Image... Just to SHOW you what the "sector"-parameter does..
[attachment=446:name]
- Max sector would then be [B][I][FONT=Georgia]180[/FONT][/I][/B]. (Untested)
- Minimum sector to counter minimap rotation would be [B][I][FONT=Georgia]6 or 7[/FONT][/I][/B].
The angle of the vision is calculated based on minimap center, and the coordinat you originally wanted to walk. Edited by slacky
FindDTMs, / ObjDTMs / DDTMs?

Share this post


Link to post
Share on other sites

Wow, nice work slacky! I will go through the SCAR code, sadly I can't help you out with converting from Python to Pascal though, simply because I have basically close to 0 knowledge from Python. :(

 

Anyways, I am glad to see you have been working on this! This looks good. Yummy. :P

 

I'll let you know if I find anything to improve.

 

Keep up the good work mate!

-Jani

Share this post


Link to post
Share on other sites

Thank you! :) Porting is usully straight forward, python is similar to normal pseudo-code... But no worrys, I think I got it under control.

 

I just hope it works with SCARs color modifiers (CTS). In python I use a different algorithom due to the fact that I was not happy with eather the resolt from HSL- and RGB-colorspace when doing just regular euclidean distance. Had to use LAB and double the lightness-tolerance (can prob be done in HSL where L-L1<=tol*2 <--what I did in LAB).

 

Uhhm... Anyways:

What mouse move, and click function should I use?

 

 

- Would be so tragic if it ends up not working :P

Edited by slacky

Share this post


Link to post
Share on other sites

Thank you!

-=-=-=-=-=-=-=-=-=-=

CTS(0-2) works (depending on where), but not the optimal choise (but it does not exist here eather). CTS(3) can work, but usualy does not. CTS3 is XYZ-space, which can do a lot of dumb things.

 

Hmm.. I really want a CTS-algorithm like whre colorspace is LAB:

 

Result := (abs(l - l1[0]) <= (tol*2) AND          abs(a - a1[1]) <= tol AND          abs(b - b1[2]) <= tol);

 

AKA create a CTS(4) with one extra tolerance-parameter just for lightness. Simply to get the correct light-differance countering-effect, which runescape-botting requres A LOT of places (for accuracy). LAB-space allows us to just grab a color, and the colorspace is incredible similar to human vision. And also, CTS(3) in Simba is LAB-space (that is why I assumed it was LAB in SCAR).

Freddy... Please!! :P

Edited by slacky
Much needed correction.

Share this post


Link to post
Share on other sites

Haha holy crap i cannot believe someone did this. I'm still in a great shock:P

I wish i could help but I know absolutley nothing in python so hopefully someone can help you to get this 100% working:)

Share this post


Link to post
Share on other sites

It's pretty similar to my python-version now.

Due to some differances between my macro-tool (in python) and SCAR, this version MIGHT not be as accurate as I intended for it to be... Time will tell.

 

 

- - - UPDATED - - -

I finished porting it, it now works at ANY compassangle. Now I will still call it an early beta/prototype. -No security checks.

Edited by slacky

Share this post


Link to post
Share on other sites

Wohoo! I just walked from lumby to "varroc gate" and back again. I registerd a lot of points that "missed" by +/- 10-15px, but the endresult was (about) 10+/- pixels from where I started..

Not to bad, not to bad... And considering the bad/inaccurat map I used to create the points it was an unexpeced (promising) result!

 

PS: GetCompassAngle is a little of ++, walking with rotated compass sould be considered and only used in special cases..

Edited by slacky

Share this post


Link to post
Share on other sites
Wohoo! I just walked from lumby to "varroc gate" and back again. I registerd a lot of points that "missed" by +/- 10-15px, but the endresult was (about) 10+/- pixels from where I started..

Not to bad, not to bad... And considering the bad/inaccurat map I used to create the points it was an unexpeced (promising) result!

 

PS: GetCompassAngle is a little of ++, walking with rotated compass sould be considered and only used in special cases..

Damn! That really does sound promising mate, from lumby to varrock gate and back - AWESOME! :D

 

Looks like things are rolling? :P

Share this post


Link to post
Share on other sites

Also, I suggest that you'll think about using bitmap finding for flag detection, because it works really great (very stable)..

 

var
 bmpMinimapFlag: TSCARBitmap;

begin
 bmpMinimapFlag := TSCARBitmap.Create('deNpjYmBh+M/AEGssCCGPhbHASQBa8gbJ');
 bmpMinimapFlag.SaveToBMP(ScreenPath + 'bmpMinimapFlag.bmp');
 DebugBitmap(bmpMinimapFlag);
end.

 

That one.. :) Check it out in the ScreenPath folder, because DebugBitmap() adds that black space around it for that small bitmaps.

 

Then you'll just need to add the correct positioning in, with offsetting from the found bitmap point.

 

      pt.X := (pt.X + 1);
     pt.Y := (pt.Y + 9);

 

That's the offset I used. :P Not sure if you used the same point for minimap flag though, so you could check it.

 

-Jani

Share this post


Link to post
Share on other sites
Looks like it uses the "Flags" to mapwalk, with the points being points on the minimap or main map. Am I right?

- It does use the flag, but only to check if we have reached the target. Points are on the minimap.

 

I will take a look at using that bitmap for flag-detection, it will without a doubt be more accurate than just searchin' for "a bunch of gethered colors"! :)

But I don't think i'll be needing thoes offsets, as the function I use only checks if the flag exists - Anything else is unneeded (if i wasn't to make the function multipurpose).

Edited by slacky

Share this post


Link to post
Share on other sites

slacky, 1 suggestion considering FindFlagBitmap, it could be tweaked slightly:

 

  function FindFlagBitmap: Boolean;
 var
   FlagBMP: TSCARBitmap;
   x,y: Integer;
 begin
   FlagBMP := TSCARBitmap.Create('deNpjZ+BnwAT/sYn8xybyH5vIf2wi/7GJ4EKY4FgYyyAUBACnrST7');
   FlagBMP.TranspColor := 0; 
   Result := FindBitmapTol(x,y, FlagBMP, 570, 6, 714, 151,4); 
   FlagBMP.Free; // Added this here
 end;

 

Of course, the FlagBMP would be a lot better as global TSCARBitmap (that way, it would be loaded at startup and free'd in the script termination)

Anyways, now while it is local, I added you there FlagBMP.Free, so that the bitmap is free'd from memory (not sure if this is automatically done by SCAR, though?). :)

 

-Jani

Share this post


Link to post
Share on other sites

This look fantastic.

 

Just in time for the release of the 2nd half of Map tonight.

 

I've been experimenting pretty much all last night trying to get the most accurate compass angle possible.

 

Will have Flag etc. done soon. I don't know why ya'll need a bmp, FindColorSpiral should work.

Share this post


Link to post
Share on other sites
This look fantastic.

 

Just in time for the release of the 2nd half of Map tonight.

 

I've been experimenting pretty much all last night trying to get the most accurate compass angle possible.

 

Will have Flag etc. done soon. I don't know why ya'll need a bmp, FindColorSpiral should work.

Does any of the minimap objects have color 255 in em?

 

Anyways, I personally used bitmap mostly because with it I can make sure flag doesn't get mixed up with anything else in minimap + the fact that it can be easily converted to accurate position (point) in minimap, for any walking purposes. :P

 

But you are right James, for hes case in this project, simple color would be the best solution IF there just isn't those minimap objects with color code 255.

I mean, FlagExists() wouldn't require bitmap (or TPA) detection, whereas FindFlag() does.

 

-Jani

Share this post


Link to post
Share on other sites

The most accurate thing in the business would be:

 

DTM map walking with SPS fall back:

 

DTM's establish shape, and extreme accuracy. SPS is just comparing parts of the map for accuracy (with the main map and minimap). Then the last fall back could be Flag walking.

 

For now though, flag walking is good enough. I mean they don't change them that often (I don't think). SPS has been down for a while (idea behind it though map comparing). And DTM rotation finding (and with speed) isn't the best yet.

 

The only problem with DTM is, how do you update them? Like when the map changes. I can think of a way this could work, but I don't think we are able to do that last I checked. DTM's are better just because of customization and SPEED. The speed could be potentially better, and yeah what if there is no flags around? You could argue around with dtms.

 

The ideal thing would be to have DTMs, and flags that you could automatically update somehow. Then if none of those are found have the SPS fall back. SPS in itself was extremely accurate though. I think it was 90% or better at times...

 

Idk how the "point" system works. How do you know where you are on the map? Well anyway here is an idea:

1. When you go to the full map, you can choose to have the Flags displayed or not. So with SCAR you could gather ALL THE FLAGS by taking the DIFFERENCE of the two bitmaps :D

 

 

I still lean toward the SPS method a little. It would be cool though if you could identify shapes on the minimap. Like I remember some bots could identify the shape of ROADS, and then highlight them (RSBot or w/e). Then it would just walk on the road.

Edited by LordJashin

Share this post


Link to post
Share on other sites

Sooo tested and created some points on the Map creator website you supplied. It seemed very off alittle (atleast on the varrock map)

but still, AMAZING to just know this is almost working!:D

 

Can't wait for a full functioning map walking:)

Share this post


Link to post
Share on other sites
* ... *

Most maps I got on that site is junk, create your own, or modify the points manually... I got an idea for a better method of getting points, but all in good time. But give it some time, I need to sort out some errors and mistakes, and get trough th tough parts.

 

=-=-=-=-=-=-=-=-=

 

I found that Jagax is doing all in thier power to prevent simple TPA-color-point walking algorithoms like mine... Frustrating (<2007 was som much simpler).

1. Changing colors... Ok, not to hard to win againt.

2. Rotating compass (13-14 degrees of in any dirrection)... Still possible, just requres a little more.

3. The minimap is being scaled - from 90% til 110%.... Now that is worse, and it's giving me a lot of truble. F***!!

 

It can be solved, but I do not know I'm motivated enough for it. If some ideas comes up, I can try to make am work. But for now, this method is somewhat useless as it stands. (might work sometimes, while other times not at all). Well, well, well....

 

Edit: I got a few ideas I can put to the test.

One of the methods I tought of includes using objects (objDTM) to triangulate the offset/fail from the coordinate we initally was to walk and corect it if needed, I believe that could work quite well, and actually result in a quite decent and accurate walking method... Now, how to implement it will be a small challange: Pascal + slacky = False..

 

And yet again, I ask you gus for help.. If anyone is willing to help with the above, please feel free to do : )

Edited by slacky

Share this post


Link to post
Share on other sites
Does any of the minimap objects have color 255 in em?

 

Anyways, I personally used bitmap mostly because with it I can make sure flag doesn't get mixed up with anything else in minimap + the fact that it can be easily converted to accurate position (point) in minimap, for any walking purposes. :P

 

But you are right James, for hes case in this project, simple color would be the best solution IF there just isn't those minimap objects with color code 255.

I mean, FlagExists() wouldn't require bitmap (or TPA) detection, whereas FindFlag() does.

 

-Jani

 

Why would you use 255? The part of the flag where you land is at the stem. Stem color spiraled from MMCX, MMY2.

Edited by Wanted

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...