Jump to content
Janilabo

Intro to MSSL's Project RS06 commands

Recommended Posts

Introduction to MSSL's useful Project RS06 stuff!

 

Example script skeleton:

 

{$DEFINE PRS06}

{$I MSSL\MSSL.scar}

procedure ScriptTerminate;
begin
 MSSL_Unsetup;
end;

// Place your procedures & functions here

begin
 MSSL_Setup;                            
 // Place your main loop stuff here
end.

 

NOTE: This introduction/documentation is based on MSSL 0.99-70(+). :)

 

Login

 

function PRS06_LoggedIn: Boolean;

-This function returns True if our character is logged in to Project RS06, else the result will be False.

 

function PRS06_AtLogin: Boolean;

-This function returns True if we are at Login page, it's the page that contains New User & Existing User options.

bwgfOtP.png

Image of Login page

 

function PRS06_AtLoginNewUserPage: Boolean;

-This function returns True if we are at New User page of Login.

RXEJqAO.png

Image of New User page

 

function PRS06_AtLoginDetails: Boolean;

-This function returns True if we are at Login Details, the page with Username and Password fields.

FRJ5qgf.png

Image of Login Details page

 

function PRS06_GetLoginMessage: string;

-This function returns the current message of Login Details page as string. List of Messages below!

-Connecting to server...
-Your account is already logged in. Try again in 60 secs...
-Error connecting to server.
-Invalid username or password.
-Account banned.
-Enter your username & password.

 

function PRS06_SetLoginDetails(username, password: string): Boolean;

-This function sets your character's Username and Password to Login Details page.

NOTE: This works ONLY if your character is at Login Details page!

Returns True with success.

 

function PRS06_LogIn(username, password: string): Boolean;

-This function login's with Username and Password.

NOTE: Doesn't matter which Login page you have open, as long as you are logged out of game! Function is fully failsafed.

Returns true with success.

If you have set incorrect user details, this function will set PRS06_InvalidUserDetails variable to True after certain amount of tries (3 = default)

If your character has been banned, this function will set PRS06_Banned variable to True.

 

function PRS06_LogOut: Boolean;

-This function logs out of game.

Returns true with success (keeps trying for up to 1 minute)!

 

GameTab

 

t1HwHP5.png

Image of GameTab indexes

 

function PRS06_GameTabOpen(i: Integer): Boolean;

-This function returns True if tab (i) from GameTab is open (as in, active). List of tab constants below.

  PRS06_GAMETAB_ATTACK_STYLE = 0;
 PRS06_GAMETAB_STATS = 1;
 PRS06_GAMETAB_QUEST_JOURNAL = 2;
 PRS06_GAMETAB_INVENTORY = 3;
 PRS06_GAMETAB_EQUIPMENT = 4;
 PRS06_GAMETAB_PRAYERS = 5;
 PRS06_GAMETAB_SPELLS = 6;
 PRS06_GAMETAB_FRIEND_LIST = 7;
 PRS06_GAMETAB_IGNORE_LIST = 8;
 PRS06_GAMETAB_LOGOUT = 9;
 PRS06_GAMETAB_GAME_OPTIONS = 10;
 PRS06_GAMETAB_PLAYER_CONTROLS = 11;
 PRS06_GAMETAB_MUSIC_PLAYER = 12;

 

function PRS06_OpenGameTab(i: Integer): Boolean;

-Tries to activate game tab by ID (i). Returns true with success!

 

procedure PRS06_QuickOpenGameTab(i: Integer);

-This procedure performs a quick mouse movement to game tab (i). Contains no failsafes!

Small note: Works quickly (but you need to add failsafes/checks yourself).

 

function PRS06_ActiveGameTab: Integer;

-This function returns the currently open/active game tab as integer (ID).

Returns -1 if none of the tabs are open (like, if we are not logged in OR bank is active)

 

Minimap

 

procedure PRS06_RotateCompass(rotate: PRS06_TRotateCompassDirection);

-This procedure is used for Compass rotating. Uses PRS06_TRotateCompassDirection type for direction.

NOTE: This keeps rotating the compass until you stop it with rcd_Nil (..or with VKeyUp(VK_LEFT) / VKeyUp(VK_RIGHT))

Directions below.

-rcd_Nil (stops the compass rotation)
-rcd_Left (rotates compass at left, anticlockwise)
-rcd_Right (rotates compass at right, clockwise)
-rcd_Random (rotates compass to either left or right - RANDOMLY)

 

gtAomm6.png

Image for showing the rotating direction "rcd_Left"

 

XsAViP6.png

Image for showing the rotating direction "rcd_Right"

 

function PRS06_GetCompassAngle: Extended;

-This function returns the current Compass angle as degrees, 0.0 => 360.0.

 

CJLnmdo.png

Image displaying N, S, E, W, NE, NW, SE, SW angle degrees

 

function PRS06_SetCompass(dgrs: Extended): Boolean;

-This function sets compass to angle by dgrs.

Returns true with success. NOT perfect compass angle (obviously!), but does work OK.

NOTE: Do NOT include that "°" to dgrs extended! You'll get an error if you do. ;)

The dgrs can be anything from 0.0 to 360.0..

Well, it can be higher aswell, as it will be then auto-corrected by function to range of 0-360.

 

var PRS06_MinimapBounds: TBox;

-This variable contains the Minimap area bounds stored to TBox (as X1, Y1, X2, Y2 coordinates).

 

ivO7m6S.png

Image displaying minimap bounds

 

var PRS06_MinimapTPA: TPointArray;

-This variable contains ALL pixels from Minimap stored to TPointArray.

 

EeSaKOG.png

Image displaying TPointArray of Minimap pixels (in white)

 

function PRS06_OnMinimap(pt: TPoint): Boolean;

-Returns true if pt is inside Minimap pixels.

 

function PRS06_SetCompassDir(direction: string): Boolean;

-This function sets compass to facing direction, returns true with success.

Supported directions: 'N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'.

 

 

Text

 

function PRS06_UpText: string;

-This function returns current up text (the text at top-left corner).

 

hHfogQw.png

Image of UpText

 

function PRS06_UpText2: string;

-This function also returns current up text, but using other method (NOTE: in most cases this is worse!).

 

function PRS06_IsUpText(text: string): Boolean;

-Returns true if UpText matches text. Case-sensitive. UpText must start with this text!

 

function PRS06_UpTextContains(text: string): Boolean;

-Returns true if UpText contains our string (text) at any position. CASE-SENSITIVE!

 

function PRS06_UpTextContainsEx(texts: TStrArray): Boolean;

-Returns true if UpText contains ANY of the strings in texts. CASE-SENSITIVE!

 

function PRS06_ChooseOptionInUse(var x, y: Integer): Boolean;

-Returns true AND stores X, Y coordinates to x, y, if Choose Option is in use (available/shown at screen).

9f6C93z.png

Image of Choose Option (including the indexes on the left-side)

 

function PRS06_ChooseOptionAvailable: Boolean;

-Returns true if Choose Option is in use (available/shown at screen).

 

function PRS06_ChooseOptionItems: TStrArray;

-Returns ALL of the items from Choose Option menu as an TStrArray (array of string).

 

function PRS06_ChooseOptionItems2: TStrArray;

-Same as above, but using different methods. A little slower, but could be (in rare cases!) a bit more reliable.

 

procedure PRS06_ChooseOptionUseItemQuick(ID: Integer);

-Uses Choose Option item by list index (ID) quickly. NO FAILSAFES!

 

procedure PRS06_ChooseOptionUseQuick(name: string)

-Uses Choose Option item by name quick. Must not be full name, but Choose Option item must AT LEAST start with the name.

CASE-SENSITIVE, NO FAILSAFES!

-Example way to use this procedure: PRS06_ChooseOptionUse('Attack Chicken'); (See, no level used, but this works great!)

 

function PRS06_ChooseOptionTarget: Integer;

-Returns the currently selected/targeted item from Choose Option menu, as integer (list index).

List index starts with 0.

If this function for some reason fails, the result will be -1 (eg. Choose Option is/was not in use)

 

function PRS06_OpenChooseOption(x, y: Integer): Boolean;

-Tries to open Choose Option to x, y coordinates and returns true with success (if menu opens within 1,5 s).

NOTE: x, y coordinates is the position where the mouse click is performed at!

 

function PRS06_HideChooseOption: Boolean;

-Hides (closes) the Choose Option, if its available, using mouse movements.

Returns true with success. Failsafed!

 

function PRS06_ChooseOptionItemCount: Integer;

-Returns count of items from showing Choose Option menu (0-21).

 

function PRS06_ChooseOptionArea(var area: TBox): Boolean;

-This function looks out for Choose Option and if its available this will store Choose Option area bounds to area as TBox. Returns true with success.

 

LmLl8xS.png

Image displaying Choose Option's area bounds in screen (bounds with red color)

Edited by Janilabo
Link to comment
Share on other sites

Inventory

 

Iijtiul.png

Image of Inventory including the indexes (0-27)

 

function PRS06_InventoryOpen: Boolean;

-This function returns True if inventory tab is active/open

 

function PRS06_OpenInventory: Boolean;

-This function tries to activate inventory tab - returns true if it succesfully activated it.

 

procedure PRS06_QuickOpenInventory;

-Simply clicks to the inventory tab area, so quickly tries to open it. Quick, but this doesn't contain any failsafes!

 

function PRS06_GetInventorySlot(id: Integer): TBox;

-This function returns the inventory slot's (by id) area/bounds as TBox.

 

function PRS06_PointToInventorySlot(p: TPoint): Integer;

-Converts point (p) to inventory slot index, if p is inside ANY of the inventory slot areas.

 

function PRS06_GetInventorySlotID(R, C: Integer): Integer;

-Converts Row ® and Column © to index. Small note: Rows and Columns start from 0, NOT from 1!

 

function PRS06_GetInventorySlotCenter(ID: Integer): TPoint;

-This function returns the center point of item slot (by ID), as TPoint.

 

function PRS06_InventorySlotUsed(ID: Integer): Boolean;

-Returns true, if inventory slot by ID contains item.

 

function PRS06_GetUsedInventorySlots: TIntArray;

-This function returns ALL of the inventory slots that are currently used (contain items)

 

function PRS06_UsedInventorySlots: Integer;

-Returns the amount/count of used inventory slots.

 

function PRS06_DropInventoryItem(ID: Integer): Boolean;

-Tries to drop item from inventory by slot ID. Returns true, if "Drop X" was used from Choose Option menu on the item.

 

Bank

 

function PRS06_BankOpen: Boolean;

-Returns true, if bank window is open.

 

function PRS06_CloseBank: Boolean;

-Closes the bank window and returns true if it was succesfully closed.

NOTE: Returns false, if bank wasn't open in the first place!

 

function PRS06_OpenBank(bankID: Integer): Boolean;

-Opens bank by ID (bankID) and returns true with success.

NOTE: You MUST be near/clost to bank booth/NPC for this function to work. ..the closer, the better!

 

Constants for IDs of all currently supported banks below:

  PRS06_BANK_DRAYNOR = 0;
 PRS06_BANK_VARROCK_EAST = 1;
 PRS06_BANK_VARROCK_WEST = 2;
 PRS06_BANK_EDGEVILLE = 3;
 PRS06_BANK_FALADOR_EAST = 4;
 PRS06_BANK_FALADOR_WEST = 5;
 PRS06_BANK_AL_KHARID = 6;     
 PRS06_BANK_TZHAAR = 7;
 PRS06_BANK_CATHERBY = 8;
 PRS06_BANK_SEERS_VILLAGE = 9;
 PRS06_BANK_ARDOUGNE_EAST = 10;
 PRS06_BANK_ARDOUGNE_WEST = 11;
 PRS06_BANK_YANILLE = 12;

 

Combat

 

function PRS06_GetAttackStyle(var style: Integer): Boolean;

-Stores the currently selected attack style, of 4 possible attack styles, as integer (list index) to variable "style". Returns true, if Attack Style could be detected/captured.

 

function PRS06_SetAttackStyle(style: Integer): Boolean;

-Tries to set Attack Style as style (list index, 0-3), returns true with success.

 

DwopL2R.png

Image of Attack Style boxes, including the indexes of em (0-3)

 

function PRS06_GetAttackStyleBoxes: TBoxArray;

-This function returns the currently shown Attack Style boxes as TBoxArray.

 

function PRS06_InFightHA: Boolean;

-This function returns true, if we are in combat (our HP bar is showing on screen).

NOTE: This function works ONLY with highest camera angle (hence why, InFight"HA").

 

Camera

 

procedure PRS06_RotateCamera(rotate: PRS06_TRotateCameraCourse);

-Rotates camera UP or DOWN using PRS06_TRotateCameraCourse type.

Courses below:

-rcc_Nil (STOPS the camera rotation)
-rcc_Down (rotates camera DOWN)
-rcc_Right (rotates camera UP)

 

procedure PRS06_RotateCameraMS(rotate: PRS06_TRotateCameraCourse; MS: Integer);

-Rotates camera from start for total amount of MS milliseconds, then stops camera rotation after MS.

 

procedure PRS06_StopCameraRotation;

-Stops the camera rotation.

 

procedure PRS06_RotateCameraDown;

-Rotates camera down until PRS06_StopCameraRotation is called (sets down DOWN arrow).

 

procedure PRS06_RotateCameraUp;

-Rotates camera up until PRS06_StopCameraRotation is called (sets down UP arrow).

 

procedure PRS06_SetCameraAngleH;

-Sets camera angle to highest angle.

 

procedure PRS06_SetCameraAngleL;

-Sets camera angle to lowest angle.

Edited by Janilabo
Link to comment
Share on other sites

Game Options

 

6ndujji.png

Image of Game Options tab

 

function PRS06_GetScreenBrightness: Integer;

-Returns the current Screen Brightness mode index as Integer. If mode couldn't be detected, result will be -1.

 

function PRS06_SetScreenBrightness(x: Integer): Boolean;

-Sets Screen Brightness mode to x (list index). Returns true, if mode was succesfully set as x.

 

Constants for IDs of all Screen Brigtness mode's below:

  PRS06_SCREEN_BRIGHTNESS_DARK = 0;
 PRS06_SCREEN_BRIGHTNESS_NORMAL = 1;
 PRS06_SCREEN_BRIGHTNESS_BRIGHT = 2;
 PRS06_SCREEN_BRIGHTNESS_VERY_BRIGHT = 3;

 

function PRS06_GetMouseButtons: Integer;

-Returns the current Mouse Buttons style index as Integer. If mode couldn't be detected, result will be -1.

 

function PRS06_SetMouseButtons(x: Integer): Boolean;

-Sets Mouse Buttons style to x (list index). Returns true, if style was succesfully set as x.

 

Constants for IDs of both Mouse Buttons style's below:

  PRS06_MOUSE_BUTTONS_ONE = 0;  
 PRS06_MOUSE_BUTTONS_TWO = 1;

 

function PRS06_GetMoveSpeed: Integer;

-Returns the current Move Speed index as Integer. If speed couldn't be detected, result will be -1.

 

function PRS06_SetMoveSpeed(x: Integer): Boolean;

-Sets Move Speed to x (list index). Returns true, if speed was succesfully set as x.

 

Constants for IDs of both Move Speed's below:

  PRS06_MOVE_SPEED_WALK = 0;
 PRS06_MOVE_SPEED_RUN = 1;

 

function PRS06_SetRun: Boolean;

-Tries to set run as move speed, returns true, if it was succesfully set.

Edited by Janilabo
Link to comment
Share on other sites

I'm impressed! :)

 

You R doing a fine job, Janilabo! You're right up my ***, on my python-based ( project06 )-library. But your ChooseOption-stuff seems to be better then what I got so far, I bet it's faster as well. I'll remake mine soon enough, without using bitmap this time :P

 

Regarding PRS06_SetCompass() :

>> You cant simply calculate diffireanse like abs(angle1 - angle2);... Then differanse between 359.9 and 0 will be 359.9... Not a big problem, but it will result in setting the compas further off then it already is, and unneeded rotating.

Edited by slacky
  • Like 1
Link to comment
Share on other sites

I'm impressed! :)

 

You R doing a fine job, Janilabo! You're right up my ***, on my python-based ( project06 )-library. But your ChooseOption-stuff seems to be better then what I got so far, I bet it's faster as well. I'll remake mine soon enough, without using bitmap this time :P

 

Regarding PRS06_SetCompass() :

>> You cant simply calculate diffireanse like abs(angle1 - angle2);... Then differanse between 359.9 and 0 will be 359.9... Not a big problem, but it will result in setting the compas further off then it already is, and unneeded rotating.

Ello slacky,

 

Yeah, I see your point now with the compass function, I will add a fix on it right now. Thanks a lot! :)

 

Is your Python library available somewhere or are you keeping it as private lib? Would love to see (or even hear) what you have been working on. Sounds interesting at least, although I am sure you have had to work a lot to get certain things working.. Well, mostly writing color and bitmap routines from scratch, as I am not sure if Python has libraries for those already?

 

-Jani

Link to comment
Share on other sites

For now, I'm keep it private. It's a functional mess, and to many third party modules have been used, so no others then python "nerds" will appreciate it...

 

I don't really have to make to much my self. Color (pixel-data) stuff is just a call to windows-libraries, and then convert that to a real bitmap, Windows (win32api) also allows me to simply grab the RGB-color of a pixel on the (virtual) screen without converting it to a bitmap first, same with XLib on linux, even though i've not (yet) implmented linux-bindings. And my small list of functions can't even be compared to SCARs hugh set of functions.

 

TPA -> So far, I got about 20-25 functions for what you guys call "TPA", but I call am "TPL" (Tuple Point List), or LTPL (List of TPL), there are Tuples, dict and Lists in python, but not arrays.

- The one function that was a little hard to make was GroupTPL (read SplitTPA), I ended up making a function using Chebyshev distance. I believe it's very similar to SCARs SplitTPA-function

 

Color -> I got maybe 10 different algortims for color-finding. Scar uses HSL/B(?) as a default... I use CIE-LAB, and/or RGB. My algo for tolerance is similar to calculating the Euclidean distance.

 

Bitmap -> I'm still tryin' to find out how I should go about bitmap finding "subimage in image".. So far I've made a few functions where OpenCV is a keyword. Also made functions without OpenCV but when speed is of the essence OpenCV is prefered. I might need to write something in C, just to speed it up.

 

Every IO-units (mouse, keyboard, screen etc...) is just a call to the windows API, and then some processing to make it python friendly.. For simplicity I've use PIL for image/catching and processing, but i'm testing to call the windows API my self, but i seem to overflow the catche or something, atleast when I take multple screenshots in millisecounds, PIL it is for now...

 

I got three (four) different functions for mouse-movement, one of am is the algorithm made by BenLand100, it wasn't to hard to convert to python, as there are both pascal- and java version around.

 

OCR -> I got to versions of OCR... One of the the function is "AI"-OCR (not my work), it dosn't match chars to a set of bitmap-fonts, every char has a set of rules (and it can be trained to recognize new font-types) -Tesseract.. While the other OCR-function I got finds the contours of each char in a image, and matches it to a bitmap-font set. I wonder how SCAR does its OCR(?), would love to try to make something similar...

 

If/when I get everything sorted I will release it under the BSD or GPL-license.

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

Cought on tape, almost a year ago. When I started testing whether or not I could make a bot-library for RS06 (2006scape). The script behind this bot was less then 15 lines, but the library around it was a few thousen lines: youtube, this script took me to 80-85 mining in no time, but XP-rates was of the charts (5xRS i think), though the DropInventory-funtion (OCR, and bitmap) was slow, but was fixed after a few days of botting.. :P

 

A year old printscreen of the OCR (which is similar to (ONE of the OCR-methods) I use now), only a little improved over time..: http://oi44.tinypic.com/35jccpi.jpg...

- Only wish I could manage to make something similar to SMART for project06, but my limited knowledge of Java is keeping me from doing that. And i've tried, but damn, it's not as simple as it seems.

Edited by slacky
Link to comment
Share on other sites

Just a little tip: While in a fight, you can use the HP-bar to get current HP-percent.

 

I did something like this in Python:

   if not InFight(): return False;
   X1,Y1,X2,Y2 = HPBAR
   total = float(150) #HPBar = 30x*5y
   green = len(FindColorSpiralTolMulti(X1,Y1, X2,Y2, '00ff00', 2, 'All'))
   return round(green/total*100, 1)

 

Grab the whole HP-bar (minus the first top horizontal line, and bottom) - so you get a box which is 30x3, count all current green pixels and devide it by total pixels... times 100... simple and fully functional.

 

Edit: It seems that you already have something similar to this... :)

Edited by slacky
Link to comment
Share on other sites

Just a little tip: While in a fight, you can use the HP-bar to get current HP-percent.

 

I did something like this in Python:

   if not InFight(): return False;
   X1,Y1,X2,Y2 = HPBAR
   total = float(150) #HPBar = 30x*5y
   green = len(FindColorSpiralTolMulti(X1,Y1, X2,Y2, '00ff00', 2, 'All'))
   return round(green/total*100, 1)

 

Grab the whole HP-bar (minus the first top horizontal line, and bottom) - so you get a box which is 30x3, count all current green pixels and devide it by total pixels... times 100... simple and fully functional.

 

Edit: It seems that you already have something similar to this... :)

The only problem is that its just not very reliable - damage splashes are often blocking the clear view.. :( Have you came up with any solution(s) for those? It is nice idea, though. :P

 

-Jani

Link to comment
Share on other sites

Depends on how much it's overlapped, when in areas where multifighting isn't allowed, and just traning on one NPC the HPBar will not be overlapped.

 

But places where it's allowed to fight multiple NPC at the same time (and also doing so...) or the NPC is FAST ("DDP"-fast), the hits-splashes will offen overlay the whole HP-bar... This method is in such areas useless, then it's smarter to use any OCR-method to read the current HP from the stats tab.

 

But for general botting (when not fighting multiple NPCs at once) this method should work fine..

And will most certainly be faster then OCR, and probably use less resources (not that it matters as much as speed).

 

But... One can check the total size of the HPBar, if it's less then 150px (30x*5y) minus acceptable fail-ratio, the method can call an OCR-method.

---

 

Almost done writing a new OCR-method in python, this one is a little less sensitive, but seems to yeld better results then my other two methods! :-)

Edited by slacky
Link to comment
Share on other sites

Гидроизоляция это ключевой элемент в строительстве, обеспечивающий защиту объектов от воздействия влаги и воды. В большой зависимости от критерий эксплуатации и материала конструкции, выбирается определенный тип гидроизоляции. Рассмотрим основные разновидности и их применения.

1. Рулонные материалы

Рулонные гидроизоляционные материалы используются для защиты кровель и фундаментов. Они бывают на основе битума и полимеров.
- Битумные рулоны знамениты благодаря своей доступности и безопасности. Используются на плоских крышах и в основании построек.
- Полимерные рулоны имеют более высокую крепкость и долговечность, подходят для сложных погодных критерий.

2. Жидкая гидроизоляция

Жидкие гидроизоляторы применяются для сотворения бесшовного покрытия. Они бывают на основе:
- Полимеров просто наносятся и образуют крепкую мембрану.
- Цемента идеально то что надо для ванной и кухни, обладают хорошими гидрофобными качествами.

3. Проникающая гидроизоляция

Этот тип просачивается в структуру бетона и заполняет микротрещины, обеспечивая надежную защиту. Применяется в большей степени для фундаментов и подвалов. Проникающая гидроизоляция эффективно совладевает с постоянным воздействием влаги.

4. Мембранная гидроизоляция

Мембранные системы часто используются для крыши и находящийся под землей конструкций. Такой метод обеспечивает надежную защиту от осадков и грунтовых вод.
- ЭПДМ и ТПО мембраны имеют высокую устойчивость к солнечному излучению и механическим повреждениям https://gidroizolyaciya-dlya-vsekh.ru

5. Гидрофобные добавки

Гидрофобные добавки в бетон или раствор помогают предупредить проникновение воды. Они совершенно то что надо для творения водонепроницаемых конструкций, в том числе бассейны и резервуары.

Выбор типа гидроизоляции

В момент выбора гидроизоляции главно учесть:
- Условия эксплуатации влажность, температура, вероятные нагрузки.
- Материалы конструкции для каждого типа материала существует свой лучший вариант гидроизоляции.
- Бюджет некие методы более накладные, но обеспечивают огромную долговечность.

В заключение, выбор гидроизоляции зависит от множества причин. Правильное решение поможет продлить срок эксплуатации строительных объектов и избежать серьезных проблем с влажностью.
Link to comment
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...