NeonBlack

Useful Functions, Commands and Code Snippets

Recommended Posts

Because I could not find any Thread like this on the Forums I decided to start one on myself. :D

You may post your Useful Functions, Commands and Code Snippets here so that the others can use them and don't have to script on their own.

I'll start with three useful functions concerning Date and Time.

int GetTimestamp([int year, int month, int day, int hour, int minute, int second])

Returns the UNIX Timestamp of any Date after 1st January of 1970 ignoring Summertime(!) (of course as an Integer)

All Arguments are optional, GetTimestamp() returns the UNIX Timestamp of the actual Datetime.

function GetTimestamp(year, month, day, hour, minute, second) 
    local i 
    local timestamp = 0 
    local time = getRealTime() 
    local monthDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
     
    if (not year or year < 1970) then 
        year = time.year + 1900 
        month = time.month + 1 
        day = time.monthday 
        hour = time.hour 
        minute = time.minute 
        second = time.second 
    else 
        month = month or 1 
        day = day or 1 
        hour = hour or 0 
        minute = minute or 0 
        second = second or 0 
    end 
     
    for i=1970, year-1, 1 do 
        timestamp = timestamp + 60*60*24*365 
        if (IsYearALeapYear(i)) then 
            timestamp = timestamp + 60*60*24 
        end 
    end 
     
    if (IsYearALeapYear(year)) then 
        monthDays[2] = monthDays[2] + 1 
    end 
     
    for i=1, month-1, 1 do 
        timestamp = timestamp + 60*60*24*monthDays[i] 
    end 
     
    timestamp = timestamp + 60*60*24 * (day - 1) + 60*60 * hour + 60 * minute + second 
     
    return timestamp 
end 

table GetDateTime(int timestamp)

Needs the IsYearALeapYear() Function!

Complement to GetTimestamp. Returns a Table like GetRealTime does but without yearday and isdst.

Note: It returns a year like 2008, you will not have to add 1900 like in getRealTime()! Also the Months start at 1 and end at 12!

function GetDateTime(timestamp) 
    local i 
    local time = {} 
    local monthDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
     
    time.year = 1970 
    while (timestamp >= 60*60*24*365) do 
        timestamp = timestamp - 60*60*24*365 
        time.year = time.year + 1 
         
        if (IsYearALeapYear(time.year - 1)) then 
            if timestamp >= 60*60*24 then 
                timestamp = timestamp - 60*60*24 
            else 
                timestamp = timestamp + 60*60*24*365 
                time.year = time.year - 1 
                break 
            end 
        end 
    end 
     
    if (IsYearALeapYear(time.year)) then 
        monthDays[2] = monthDays[2] + 1 
    end 
     
    local month, daycount 
    for month, daycount in ipairs(monthDays) do 
        time.month = month 
        if (timestamp >= 60*60*24*daycount) then 
            timestamp = timestamp - 60*60*24*daycount 
        else 
            break 
        end 
    end 
     
    time.monthday = math.floor(timestamp / (60*60*24)) + 1 
    timestamp = timestamp - 60*60*24 * (time.monthday - 1) 
     
    time.hour = math.floor(timestamp / (60*60)) 
    timestamp = timestamp - 60*60 * time.hour 
     
    time.minute = math.floor(timestamp / 60) 
    timestamp = timestamp - 60 * time.minute 
     
    time.second = timestamp 
     
    local monthcode = time.month - 2 
    local year = time.year 
    local yearcode 
     
    if (monthcode < 1) then 
        monthcode = monthcode + 12 
        year = year - 1 
    end 
    monthcode = math.floor(2.6 * monthcode - 0.2) 
     
    yearcode = year % 100 
    time.weekday = time.monthday + monthcode + yearcode + math.floor(yearcode / 4) 
    yearcode = math.floor(year / 100) 
    time.weekday = time.weekday + math.floor(yearcode / 4) - 2 * yearcode 
    time.weekday = time.weekday % 7 
     
    return time 
end 

boolean IsYearALeapYear(int year)

Returns true if the year was/is/will be a Leapyear, else false.

function IsYearALeapYear(year) 
    if ((year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0) then 
        return true 
    else 
        return false 
    end 
end 

You may also post feedback.

Greetz, Neon

Edited by Guest

Share this post


Link to post

Because I could not find any Thread like this on the Forums I decided to start one on myself. :D

You may post your Useful Functions, Commands and Code Snippets here so that the others can use them and don't have to script on their own.

I'll start with three useful functions concerning Date and Time.

int GetTimestamp([int year, int month, int day, int hour, int minute, int second])

Returns the UNIX Timestamp of any Date after 1st January of 1970 ignoring Summertime(!) (of course as an Integer)

All Arguments are optional, GetTimestamp() returns the UNIX Timestamp of the actual Datetime.

function GetTimestamp(year, month, day, hour, minute, second)    local i    local timestamp = 0    local time = getRealTime()    local monthDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }        if (not year or year < 1970) then        year = time.year + 1900        month = time.month + 1        day = time.monthday        hour = time.hour        minute = time.minute        second = time.second    else        month = month or 1        day = day or 1        hour = hour or 0        minute = minute or 0        second = second or 0    end        for i=1970, year-1, 1 do        timestamp = timestamp + 60*60*24*365        if (IsYearALeapYear(i)) then            timestamp = timestamp + 60*60*24        end    end        if (IsYearALeapYear(year)) then        monthDays[2] = monthDays[2] + 1    end        for i=1, month-1, 1 do        timestamp = timestamp + 60*60*24*monthDays[i]    end        timestamp = timestamp + 60*60*24 * (day - 1) + 60*60 * hour + 60 * minute + second        return timestampend

table GetDateTime(int timestamp)

Needs the IsYearALeapYear() Function!

Complement to GetTimestamp. Returns a Table like GetRealTime does but without yearday and isdst.

Note: It returns a year like 2008, you will not have to add 1900 like in getRealTime()! Also the Months start at 1 and end at 12!

function GetDateTime(timestamp)    local i    local time = {}    local monthDays = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }        time.year = 1970    while (timestamp >= 60*60*24*365) do        timestamp = timestamp - 60*60*24*365        time.year = time.year + 1                if (IsYearALeapYear(time.year - 1)) then            if timestamp >= 60*60*24 then                timestamp = timestamp - 60*60*24            else                timestamp = timestamp + 60*60*24*365                time.year = time.year - 1                break            end        end    end        if (IsYearALeapYear(time.year)) then        monthDays[2] = monthDays[2] + 1    end        local month, daycount    for month, daycount in ipairs(monthDays) do        time.month = month        if (timestamp >= 60*60*24*daycount) then            timestamp = timestamp - 60*60*24*daycount        else            break        end    end        time.monthday = math.floor(timestamp / (60*60*24)) + 1    timestamp = timestamp - 60*60*24 * (time.monthday - 1)        time.hour = math.floor(timestamp / (60*60))    timestamp = timestamp - 60*60 * time.hour        time.minute = math.floor(timestamp / 60)    timestamp = timestamp - 60 * time.minute        time.second = timestamp        local monthcode = time.month - 2    local year = time.year    local yearcode        if (monthcode < 1) then        monthcode = monthcode + 12        year = year - 1    end    monthcode = math.floor(2.6 * monthcode - 0.2)        yearcode = year % 100    time.weekday = time.monthday + monthcode + yearcode + math.floor(yearcode / 4)    yearcode = math.floor(year / 100)    time.weekday = time.weekday + math.floor(yearcode / 4) - 2 * yearcode    time.weekday = time.weekday % 7        return timeend

boolean IsYearALeapYear(int year)

Returns true if the year was/is/will be a Leapyear, else false.

function IsYearALeapYear(year)    if ((year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0) then        return true    else        return false    endend

You may also post feedback.

Greetz, Neon

Edited by Guest

Share this post


Link to post

These are useful, they may be better placed on the wiki somewhere, though I'm not sure where. You could create a page or category for them. They're probably a bit too 'off topic' to be examples... Good job :)

Share this post


Link to post

These are useful, they may be better placed on the wiki somewhere, though I'm not sure where. You could create a page or category for them. They're probably a bit too 'off topic' to be examples... Good job :)

Share this post


Link to post

More Functions. This time for Strings and formatting Numbers.

int Encrypt(string string)

Returns a Hash (Integer). For Example used to save Passwords so that nobody can read them. I used the Adler-32 Algorithm.

function Encrypt(string) 
    local length = #string 
    local s1, s2 = 0, 1 
     
    for n=1, length do 
        s1 = (s1 + string.byte(string:sub(n, n))) % 65521 
        s2 = (s2 + s1) % 65521 
    end 
     
    return (s2 * 2^16) + s1 
end 

table Explode(string separator, string ensemble)

Splits ensemble at all Occurrences of separator and stores the Pieces in a Table, which is returned.

function Explode(separator, ensemble) 
    local i, j = 1, ensemble:find(separator) 
    local pieces = {} 
     
    while (i) do 
        if (j) then j = j - 1 end 
        table.insert(pieces, ensemble:sub(i, j)) 
        if (j) then 
            i = j + #separator 
            j = ensemble:find(separator, i) 
        else break end 
    end 
     
    return pieces 
end 

string FormatNumber(number number, [int digits=0, int decimals=0, string komma=".", string milsep])

Needs the Explode() Function!

Formats any Number and returns the formatted Number as a String. If digits is greater than the Number of digits of number it will fill up with Zeroes. If it's smaller the Number of Digits won't be changed. decimals sets the Number of Digits after the Comma, if it's greater than the Number of Decimals of number it will fill up with Zeroes, else it will cut off the Rest without rounding. komma sets the String that shall replace the Decimal Point. Useful for People like me who come from Germany, where a "," is used to separate floating Point Numbers. Finally milsep sets the String that should separate Thousands. If you leave this nil Thousands won't be separated.

function FormatNumber(number, digits, decimals, komma, milsep) 
    digits = digits or 0 
    decimals = decimals or 0 
    komma = komma or "." 
     
    local strNumber = Explode("%.", tostring(number)) 
    strNumber[2] = strNumber[2] or "0" 
     
    if (milsep) then 
        local revint = strNumber[1]:reverse() 
        strNumber[1] = "" 
        for block in revint:gmatch("%d%d%d") do 
            strNumber[1] = strNumber[1]..block..milsep 
        end 
        strNumber[1] = strNumber[1]..revint:sub(-(#revint % 3)) 
        strNumber[1] = strNumber[1]:reverse() 
        if (#strNumber[1] % 4 == 0) then 
            strNumber[1] = strNumber[1]:sub(2) 
        end 
    end 
     
    if (digits > #strNumber[1]) then 
        strNumber[1] = string.rep("0", digits - #strNumber[1])..strNumber[1] 
    end 
     
    if (decimals == 0) then return strNumber[1] end 
     
    if (decimals < #strNumber[2]) then 
        strNumber[2] = strNumber[2]:sub(1, decimals) 
    else 
        strNumber[2] = strNumber[2]..string.rep("0", decimals - #strNumber[2]) 
    end 
     
    return strNumber[1]..komma..strNumber[2] 
end 

Greetz, Neon

PS.: All of my Functions may have Bugs. If you finde some, report them in this Thread please. ;)

Edited by Guest

Share this post


Link to post

iterator explode2( string str, string separator )

It returns an iterator just like ipairs and pairs. See an example below to see how it works and how to use it.

function explode2( str, separator ) 
    local start = 1 
    local ret = nil 
    return function( ) 
        local s, e = string.find( str, separator, start, true ) 
        if s then 
            local retrn = string.sub( str, start, s-1 ) 
            start = e + 1 
            return retrn, start-1, e 
        else 
            local retrn = string.sub( str, start, -1 ) 
            if #retrn > 0 then 
                start = start + #retrn 
                return retrn 
            end 
            return nil 
        end 
    end 
end 

Example of usage:

  
for word, s, e in explode2( "hello,from,lua", "," ) do 
    print( word, s, e ) 
end 
  

the result will be:

hello    6    6 
from     11   11 
lua      nil  nil 

the first variable is a string found between separators or whole string before the separator (in the first call), the 1st number is where the separator was found and the 2nd number is where it ends. You don't have to use those numbers, you can use a simple for loop w/o them like so:

for word in explode2( "hello,from,lua", "," ) do 
    print( word ) 
end 

Share this post


Link to post

Respawn a Destroyed Vehicle Whit timer

function respawnDetroyedVehicle() 
   setTimer(respawnVehicle, 4000, 1, source) 
end 
addEventHandler("onVehicleExplode", getRootElement(), respawnDetroyedVehicle)    

Define a Pickup

  
house = createPickup(-2333.90234375, -1588.9066162109, 483.16543579102,3,1273) 
  
function expedicion(playerSource) 
    setPlayerSkin(playerSource, 123) 
    setPlayerMoney (playerSource, 50000) 
    setPlayerArmor ( playerSource, 20 ) 
end 
addEventHandler("onPickupHit", expedicion, house) 

Edited by Guest

Share this post


Link to post

Define a Pickup

  
house = createPickup(-2333.90234375, -1588.9066162109, 483.16543579102,3,1273) 
  
function expedicion(playerSource) 
    setPlayerSkin(playerSource, 123) 
    setPlayerMoney (playerSource, 50000) 
    setPlayerArmor ( playerSource, 20 ) 
end 
addEventHandler("onPickupHit", expedicion1, house) 

I can see an error on line 9.

NOTE: Test your code before you post it here!

Share this post


Link to post

change

addEventHandler("onPickupHit", expedicion1, house) 

to

addEventHandler("onPickupHit", getRootElement(), house) 

Share this post


Link to post
change
addEventHandler("onPickupHit", expedicion1, house) 

to

addEventHandler("onPickupHit", getRootElement(), house) 

Your code it wrong too. Read wiki.

Share this post


Link to post

NOTE: Who ever used my Explode Function before 27th June 2008 should get the new Version! The old one has a bad Bug! I edited the Explode Function.

I was tired of creating a new Event for every Function I wanted to call from Client to Server or vice versa, so I made a CallClientFunction and CallServerFunction Handler.

You'll have to add this Script server-side:

function CallClientFunction(client, funcname, ...) 
    triggerClientEvent(client, "OnServerCallsClientFunction", getRootElement(), funcname, unpack(arg)) 
end 
  
function CallServerFunction(funcname, ...) 
    _G[funcname](unpack(arg)) 
end 
  
addEvent("OnClientCallsServerFunction", true) 
addEventHandler("OnClientCallsServerFunction", getRootElement(), CallServerFunction) 

And this Script must be added client-side:

function CallClientFunction(funcname, ...) 
    _G[funcname](unpack(arg)) 
end 
  
function CallServerFunction(funcname, ...) 
    triggerServerEvent("OnClientCallsServerFunction", getRootElement(), funcname, unpack(arg)) 
end 
  
addEvent("OnServerCallsClientFunction", true) 
addEventHandler("OnServerCallsClientFunction", getRootElement(), CallClientFunction) 

Then you can use

void CallClientFunction(element player, string function, [arguments...]) SERVER-SIDE

and

void CallServerFunction(string function, [arguments...]) CLIENT-SIDE

NOTE: Neither CallClientFunction nor CallServerFunction return Anything! This is, no Data is passed by returning something if you use one of these Functions!

And here also the Complement to FormatNumber:

number UnformatNumber(string formattedNumber, string comma, string milsep)

function UnformatNumber(formattedNumber, comma, milsep) 
    if (milsep) then 
        formattedNumber = formattedNumber:gsub(milsep, "") 
    end 
    if (comma) then 
        formattedNumber = formattedNumber:gsub(comma, ".") 
    end 
    return tonumber(formattedNumber) 
end 

Share this post


Link to post

This topic need to be sticky!

build_query(queryType, sql_array)

Note: This can only be at the server side system, against security reason's

Code:

function build_query(queryType, sql_array) 
    if type(sql_array) ~= "table" then 
        return false  
    end 
     
    local queryString = "" 
     
    if queryType == "INSERT" or queryType == "INSERT_SELECT" then 
        local keyTable = {} 
        local valTable = {} 
         
        for k, v in pairs(sql_array) do 
            if k == "password" then 
                v = string.format("SHA1('%s')", v) 
            elseif k == "username_clean" then 
                v = string.format("LOWER('%s')", v) 
            else 
                v = string.format("'%s'", v) 
            end 
             
            table.insert(keyTable, k) 
            table.insert(valTable, v) 
        end 
         
        if queryType == "INSERT" then 
            queryString = " (" .. table.concat(keyTable, ", ") .. ") VALUES (" .. table.concat(valTable, ", ") .. ") " 
        elseif queryType == "INSERT_SELECT" then 
            queryString = " (" .. table.concat(keyTable, ", ") .. ") SELECT " .. table.concat(valTable, ", ") .. " " 
        end 
    elseif queryType == "UPDATE" or queryType == "SELECT" then 
        local valueTable = {} 
         
        for k, v in pairs(sql_array) do 
            if k == "password" then 
                v = string.format("SHA1('%s')", v) 
            elseif k == "username_clean" then 
                v = string.format("LOWER('%s')", v) 
            else 
                v = string.format("'%s'", v) 
            end 
            table.insert(valueTable, k .. " = " .. v) 
        end 
         
        if queryType == "UPDATE" then 
            queryString = table.concat(valueTable, ", ") 
        elseif queryType == "SELECT" then 
            queryString = table.concat(valueTable, " AND ") 
        end 
    end 
     
    return queryString 
end 

Example

data = { 
username = "Alexander", 
username_clean = "Alexander", 
password = "alexATmta" 
} 
  
query = mysql_query("INSERT_INTO users " .. build_query("INSERT", data) 
  

queryTypes:

INSERT

INSERT_SELECT

UPDATE

SELECT

username_clean will be automatically has a LOWER function of mysql.

password will be hashed to SHA1 automatically.

You need the mysql module. You can find it here

Good luck with it.

Share this post


Link to post

ConvertXYToRelative

function ConvertXYToRelative(Resx, Resy, x, y) 
    local Rx = (1/Resx)*x 
    local Ry = (1/Resy)*y 
    return Rx,Ry 
end 

Converts an XY co-ordinate to a relative size requires the resolution you tested it on (you can find it in gta display settings)

example:

  
function ResourceStarted(resource) 
    if (resource ~= getThisResource()) then 
        return 
    end 
    local x,y = ConvertXYToRelative(800, 600, 400, 300) 
    outputChatBox("Relative sizes:  "..x..", "..y) 
end 
addEventHandler("onResourceStart", getRootElement(), ResourceStarted) 
  

800x600 is the resolution i run gta on (more fps :P)

Share this post


Link to post

You'll have to add this Script server-side:

function CallClientFunction(client, funcname, ...) 
    triggerClientEvent(client, "OnServerCallsClientFunction", getRootElement(), funcname, unpack(arg)) 
end 
  
function CallServerFunction(funcname, ...) 
    _G[funcname](unpack(arg)) 
end 
  
addEvent("OnClientCallsServerFunction", true) 
addEventHandler("OnClientCallsServerFunction", getRootElement(), CallServerFunction) 

And this Script must be added client-side:

function CallClientFunction(funcname, ...) 
    _G[funcname](unpack(arg)) 
end 
  
function CallServerFunction(funcname, ...) 
    triggerServerEvent("OnClientCallsServerFunction", getRootElement(), funcname, unpack(arg)) 
end 
  
addEvent("OnServerCallsClientFunction", true) 
addEventHandler("OnServerCallsClientFunction", getRootElement(), CallClientFunction) 

These functions aren't working for me. I get an error:

INFO: Loading script failed: D:/mtaservers/lf/mods/deathmatch/resources/lexlife/server/core/utils.lua:6: '' expected near 'in'

And that line is:

_G[funcname](unpack(arg))

Alexander de Jong

Share this post


Link to post

Syntax

boolean containsText ( string lookingFor, string text )

Required Arguments

* lookingFor: the text you want to look for

* text: the text in which you want to scan if it contains the first argument

Returns true if the second string contains the first string

or

false if you haven't passed an argument or the text doesn't contains the first string.

function containsText ( lookingFor, text )
 
if lookingFor and text then  
if #lookingFor >= #text then
if lookingFor == text then
return true
else
return false
end
else
for i = 0, #text - #lookingFor + 1 do
local switch = false
for k = 1, #lookingFor do
if string.sub ( text, i+k, i+k ) == string.sub ( lookingFor, k, k ) then
					switch = true
else
					switch = false
break
end
end
if switch then
return true
end
end
end
else
return false
end
end

For e.g. it could be used for a script against insults:

function insultCheck ( msg )
 
if containsText ( "asshole", msg ) then
kickPlayer ( source, "Don't insult other people!" )
cancelEvent()
end
end
addEventHandler( "onPlayerChat", getRootElement(), insultCheck )

Share this post


Link to post
Syntax

boolean containsText ( string lookingFor, string text )

Uuh.. sorry to piss on your campfire, but..

function containsText(haystack, needle) return string.find(haystack,needle, 1, true) ~= nil; end

Looks like you just wasted some time writing that huge function.

Share this post


Link to post

Chatcommands - Ccmd

Needs to be tested

- For at most 2 arguments

(Yeah i think there is no need for more than 2 Arguments, most of the Commands only requires 1-2)

Ccmd Table

Syntax Chatcommandname, CommandHandler

c_cmds = {
"kick", "k_player",
"ban", "b_player"
}

Chatchecker

function c_CommandHandler( message, messageType )
local arg2
if(message == 0)then
	nBeginn["c_Start"], nEnd["c_End"] = string.find(message, "!")
if(nBeginn["c_Start"] ~= false and nBeginn["c_Start"] == 1)then
		message = string.lower(message) -- for accepting chatcommands like /KIcK or /kICK
if(string.len(string.sub(message, 2)) > 0)then
			l_pos = 1
for key, c_cmd in pairs(c_cmds), 2 do
				nBeginn["c_Arg"] = string.sub(message, 2, #c_cmds[l_pos]+2)
-- Word Symbols
				arg2 = #c_cmd[l_pos]+3 -- +1 next arg tab
				l_pos=l_pos+2
if(nBeginn["c_Arg"] == c_cmd)then
--c_cmd[l_pos+1]
					l_pos=l_pos-1
local c_Playa = getElementsByType( "player" )
for theKey, c_Target in ipairs(c_Playa) do
						nBeginn["c_Target"], nEnd["c_Target"] = string.find(message, c_Target)
if(nBeginn["c_Target"] == arg2 and nEnd["c_Target"] == #getPlayerName(c_Target)+arg2)then
if(c_Target ~= source)then
executeCommandHandler ( c_cmds[l_pos], source, c_Target )
else
return outputChatBox("* You are targeting yourself, Retard!", source)
end
else
return outputChatBox("* Player "..string.sub(message,arg2).." not found!", source)
end
break -- playerloop
end
break -- arg1 loop
else
return outputChatBox("* Command !"..nBeginn["c_Arg"].." not found in tha Resource \"{SMILIES_PATH}/icon_surprised.gif\" alt=\"\" title=\"Surprised\" />", source)
end
end
end
end
end
end
addEventHandler( "onPlayerChat", getRootElement(), c_CommandHandler)

Called Handler

function kickPlayerHandler ( sourcePlayer, commandname, kickedname )
local kicked = getPlayerFromNick ( kickedname )
if ( hasObjectPermissionTo ( sourcePlayer, "function.kickPlayer" ) ) then
kickPlayer ( kicked, sourcePlayer )
end
end
addCommandHandler ( "k_player", kickPlayerHandler )
 
function banPlayerCommand ( sourcePlayer, commandName, bannedName )
if ( hasObjectPermissionTo ( sourcePlayer, "function.banPlayer" ) ) then
local bannedPlayer = getPlayerFromNick ( bannedName )
banPlayer ( bannedPlayer, sourcePlayer )
outputChatBox ( "ban: " .. bannedName .. " successfully banned", sourcePlayer )
else
outputChatBox ( "ban: You don't have enough permissions", sourcePlayer )
end
 
end
addCommandHandler ( "b_player", banPlayerCommand )

Share this post


Link to post

what is outputChatBox("* Command !"..nBeginn["c_Arg"].." not found in tha Resource :o", source)

Share this post


Link to post
what is outputChatBox("* Command !"..nBeginn["c_Arg"].." not found in tha Resource :o", source)

<!-- s:o --><img src=\"{SMILIES_PATH}/icon_surprised.gif\" alt=\":o\" title=\"Surprised\" /><!-- s:o -->

This is phpBB that things he is smart by parsing smillies in the code tags :o.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.