• 0

    posted a message on A Galaxy Rumble was [Felt]

    @s3rius: Go

    if each action was threaded on its own without wait. One action might rely on the one previous to have finished fist. So arbitrarily threading everything would be a problem. The solve would be making sure previous action did not change any variables that future actions use. Again I don’t know galaxy enough to say how much of an advantage threading everything possible would be.

    Posted in: Galaxy Scripting
  • 0

    posted a message on A Galaxy Rumble was [Felt]

    @s3rius: Go

    Then maybe my terminoligy was incorrect by using the word optimization.

    RE-Writing was what I had in mind. Locate all actions, and thread them as trigger execute, no wait. The question would be when does an action rely on previous actions. Probably would have to index each variable used, making sure to only thread deep if variables are not shared.

    Posted in: Galaxy Scripting
  • 0

    posted a message on A Galaxy Rumble was [Felt]

    @s3rius: Go

    I like your idea a lot, but I feel looking at this right away is diving past a tutorial. So I have written the first stages of a Program that would do what you asked. The initial step is to read a library. We would need to read, recompile, and write the library.


    I understand what you are asking to do. But my confusion is settled by replacing what you call Fuction with Action. If we new thread a fuction call we are still tied waiting for the result. So a new thread is not really created. If we thread an Action, then we are no longer waiting for a result, and so the thread is free to run on its own.

    I would like to look at writing a program for optimization. Where it will read a library and recompile it for threading optimal.

    Check out my tutorial, and I would like to continue this descusion S2rius


    Sldprt (ItsAboutTime)

    Posted in: Galaxy Scripting
  • 0

    posted a message on [Tutorial] Visual Studio .Net <How To Read SC2Lib file, XML>

    ReadIng XML Library [ SC2Lib ] Visual Studio Tutorial 2

    This tutorial assums you know the basics from Tutorial 1.


    This topic is for advance users, and many areas will not be explained. The Functioning code traverses a SC2Lib file created with SC2 map editor.

    An SC2Lib file is essentialy a XML file. Using Visual studios System.Xml Imports lets us traverse the Structure of a SC2 Library.

    This tutorial will be limited in only ready the file into Flat XML. The reconstruction of the library into a SC2 Map Editor view may be saved for Tutorial 3.

    And now for the Show.

    An SC2Lib file contains 6 major areas.

    XML Opening Encoding, TriggerDATA, Root, Items, Elements, Element Value

    Each of these areas are essentialy nested. The structure looks like what follows.

    -XML Opening Encoding







    Element Value1

    Element Value2

    Element Value3


    Element Value1

    Element Value2

    Element Value3


    Element Value1

    Element Value2

    Element Value3

    The root is everything contained within the main Library Folder inside the SC2 Map Editor. Following the root are nested elements.

    To Traverse from opening the XML Document, we need to search Xelements, and Xelements Xelements. These Elements may contain Attributes, and if an Element does not contain Elements, then we search for Xnodes and its respective value must be used.

    The Following is a logical traverse of a SC2Lib file.

    Sub OpenXml()
        xmlDoc = XDocument.Load(XmlReader)
        For Each xE In xmlDoc.Elements
    End sub
    Sub xmlnode(ByRef xE As XElement)
        If Not xE.HasElements Then
            For Each xN In xE.Nodes
                xN.ToString, "Value"
        End If
        For Each xAtt In xE.Attributes
            xAtt.ToString, "Attribute"
        For Each xEnest In xE.Elements
    End Sub

    By following this path, we visit all areas of a SC2Lib file. Now here is the results of looking at Built in Library.

    f1 f2 f3 f4 f5

    The code for this project is commented below. There are a number of routines to help this be a fuctioning program.

    -Form Loading

    Prepare Image list for TreeView

    Set set a status picture on the form to blank

    -Resizing the form

    Scale the TreeView based on the form size

    -Load image list

    Search for image files located in Icons folder.

    Prepare treeview1

    -Set TreeNode

    Customize Settings

    Return New TreeNode

    -Add TreeNode to TreeView

    Create Delegate to handle Cross Thread comunication with the Form Thread

    Add TreeNode to TreeView

    Do other Thread Operations

    -Browse Button Clicked

    Open XML File

    -Traverse XML File

    Create a New Thread to Compute XML File

    Start Thread

    Open XML File

    Nested Loop Elements

    Loop Nodes



    The following code is commented directly from the Project File.

    Imports System.IO
    Imports System.Xml
    Imports System.Threading
    Imports System.Drawing
    Public Class frmSC2Lib_XML
        Dim xmlF As FileInfo 'xml file info
        Dim xmlDoc As New XDocument 'xml Document
        Dim ImgList As ImageList 'treeview image list
        Dim t1 As Thread 'Traverse Thread
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            LoadImageList() 'run sub Load treeview image collection
            Me.PictureBox1.Image = ImgList.Images("Blank") 'Form Status Image
        End Sub
        Sub LoadImageList() 'Load TreeView Icons (Every Gif, and BMP in folder Icons is loaded)
            ImgList = New ImageList
            ImgList.ImageSize = New Size(24, 24)
            Dim d As DirectoryInfo = New DirectoryInfo(Application.StartupPath & "\Icons")
            For Each f In d.GetFiles
                ImgList.Images.Add(f.Name.Replace(".bmp", "").Replace(".gif", ""), Image.FromFile(f.FullName))
            Me.TreeView1.ImageList = ImgList
        End Sub
        Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
            Me.TreeView1.Size = New Size(Me.Width - (347 - 307), Me.Height - (474 - 383)) 'TreeView Resize
        End Sub
        Private Sub btnBrowse_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBrowse.Click
            Dim dr As DialogResult = Me.OpenFileDialog1.ShowDialog 'Send Open File Dialog
            If dr = Windows.Forms.DialogResult.OK Then 'If file was selected
                Me.TreeView1.Nodes.Clear() 'Clear the TreeView
                xmlF = New FileInfo(OpenFileDialog1.FileName) 'Load the XML file info
                lblFileName.Text = xmlF.Name 'Change the form Label to match the file being opened
                TraverseXML() 'Start Travering the XML File
            End If
        End Sub
        Dim XMLImg As Image
        Sub TraverseXML() 'Starts a Thread for Traversing the XML file
            XMLImg = Image.FromFile(Application.StartupPath & "\Icons\Build.gif") 'get Animated Gif
            Me.PictureBox1.Image = XMLImg 'Form Status Image
            t1 = New Thread(AddressOf OpenXml) 'Create a Thread for opening xml
            t1.Start() 'Start OpenXml thread
        End Sub
        Sub OpenXml() 'Thread Sub
            Dim XmlReader As XmlReader = XmlReader.Create(xmlF.FullName) 'Create an XML Reader
            xmlDoc = XDocument.Load(XmlReader) 'Read the whole XML document
            Dim iNode As TreeNode = NodeSet(xmlF.Name, "XML") 'Create TreeNode for Document
            For Each xE In xmlDoc.Elements
                xmlnode(xE, iNode) 'Nested Loop Elements sending both element and parent TreeNode
            AddNodes(iNode) 'Send Parent Node to TreeView
        End Sub
        Public Delegate Sub AddNode(ByRef obj As Object) 'Delegate Instance on main Thread
        Sub AddNodes(ByRef obj As Object) 'Cross Thread activator, Add Main TreeNode to TreeView
            If Me.TreeView1.InvokeRequired Then 'Check for Cross Thread violation
                Dim ad As AddNode = New AddNode(AddressOf AddNodes) 'SetUp Delegate
                Me.TreeView1.Invoke(ad, obj) 'Invoke Thread on TreeView1 Thread
            Else 'Invoking on main Thread
                Me.PictureBox1.Image = ImgList.Images("Construct") 'Form Status Image
                Me.TreeView1.BeginUpdate() 'Ready the TreeView
                Me.TreeView1.Nodes.Add(obj) 'Add Parent Node to TreeView
                Me.TreeView1.EndUpdate() 'End Ready the TreeView
                Me.PictureBox1.Image = ImgList.Images("Built") 'Form Status Image
            End If
        End Sub
        Function NodeSet(ByRef Name As String, ByRef Type As String) As TreeNode 'Prepare TreeNode
            Dim TreeNode As TreeNode = New TreeNode(Name) 'Create a New TreeNode displaying text Name
            TreeNode.ImageKey = Type 'TreeNode Picture from Type
            TreeNode.SelectedImageKey = Type 'TreeNode Picture from Type
            Return TreeNode 'Return a prepared TreeNode
        End Function
        Sub xmlnode(ByRef xE As XElement, ByRef TreeNode As TreeNode)
            Dim iNode As TreeNode = NodeSet(xE.Name.ToString, "Element") 'Create TreeNode Element
            TreeNode.Nodes.Add(iNode) ' Add TreeNode Element to Parent TreeNode
            If Not xE.HasElements Then 'NLevel Element
                For Each xN In xE.Nodes 'Loop Nodes for Values
                    iNode.Nodes.Add(NodeSet(xN.ToString, "Value")) 'Add TreeNode Value to TreeNode Element
            End If
            For Each xAtt In xE.Attributes 'Loop Attributes for Values
                iNode.Nodes.Add(NodeSet(xAtt.ToString, "Attribute")) 'Add TreeNode Attribute to TreeNode Element
            For Each xEnest In xE.Elements 'Loop Elements for all of the above
                xmlnode(xEnest, iNode) 'Nested Elements
        End Sub
    End Class

    And that’s how simple it is. Where it would get complicated is traversing from the Root location. I may save this for Tutorial 3 depending on community support.

    Here is the asset when it is approved


    Happy Coding Sldprt (ItsAboutTime)

    Posted in: Tutorials
  • 0

    posted a message on Using an Text-Variable as Script?

    @progammer: Go

    you can use word of string. then use triggers and fucntions to handle the compile of the word string lines.

    this is very interesting topic I have been looking at myself.

    Posted in: Galaxy Scripting
  • 0

    posted a message on A Galaxy Rumble was [Felt]

    No not the fabric but let’s go to war on the entire galaxy. Since my thread The Galaxy Wars are ahead of us!


    With following [Tutorial] Creating a Visual Studio Program to write Galaxy Custom Scripts [Tutorial]


    I was glad to get all the basic startup understandings to design, write, and compile a visual studio program. Kicks are using it for sc2 galaxy code. I have quite a library of stuff on the same topic. I posted back in

    Space Traders – Entire Universe of variables. (Spreading Invention)


    This was written with the same concept. It contains 18,000 lines of galaxy code I cut and pasted into sc2mapeditor. The only values I had to work with were 900 planet names and elements from earth name values). All other values were more developed, and specific random number generated values. Every time the program runs, I have a new universe of values.

    NOW I CALL FOR THE RUMBLE. What kind of galaxy triggers would be useful, and need a massive amount of coding? Techniques for coding can be included. I am looking for the smart minds of galaxy to speak on what’s NEEDED?

    Sldprt (ItsAboutTime)

    Posted in: Galaxy Scripting
  • 0

    posted a message on [Tutorial]Creating a Visual Studio Program to write Galaxy Custom Scripts [Tutorial]

    Visual Studio VB.Net tutorial. I will be Visual Studio .Net thought this tutorial. You can download the Express version for free from Microsoft’s website. http://www.microsoft.com/express/Downloads/


    For those wanting to use C# or wanting to convert between VB.net and C#. Developer Fusion is an automated website for doing this. http://www.developerfusion.com/tools/convert/csharp-to-vb/


    Alright Lets dive into the tutorial. Assuming you already have visual studio installed. Keeping in mind I am using Visual Studio 2008 Professional.


    /File/New Project…


    From the new project window, we are going to start a Windows Forms Application. Name your Application; I will refer to my application as SC2Tutorial1.


    You have now created your first Application. It has everything it needs to compile. The downfall being its an empty Windows Form.


    When our project if first started, it is saved to a temp file location. I would suggest following these steps.

    Select the name “SC2Tutorial” at the top of the Solution Explorer window. Then From the “File” menu Drop-Down select Save “SC2Tutorial As”. I locate all my programs sub folder from C:\Programming\

    You can pick appropriate names, and I would suggest keeping the check box checked. It keeps the project neat, and allows space for things associated but should not be inside the Solution folders.


    From the Tool Menu on the Left, we will need a couple things added to our Form.



    With the Form selected, the “Properties” window shows all customizable setting for our windows form. The same can be said if we selected a Textbox Label or Button. I will refer to the properties window throughout the tutorial for changing settings. Let’s start off by changing the Text Property of the form from “Form1” to “SC2Tutorial”.


    We will continue to make changes to the properties of our form to match the following.

    Form1_Text: SC2Tutorial
    Label1_Text: Variable
    Label2_Text: Values
    Label3_Text: Galaxy Code
    Button1_Text: Run


    Now from the properties window, we are going to make some settings changes to the behavior of out textbox’s

    TextBox2: Multiline = True
    TextBox2: ScrollBars = Both
    TextBox3: Multiline = True
    TextBox3: ScrollBars = Both

    We also need to rearrange our form, and controls. By moving and resizing the items. Our form should look like the following when finished.


    For this tutorial we need Data. The type of Data is up to you, I am going to be using all the “World of Warcraft” Instances, and Level Requirements.



    The first step before we start writing our program. I would like to take us to the SC2 Map Editor. We need to understand how to read the required Values for our custom Script.

    From our list of Values, we will be entering 2 types of data. String for the instance Name, and Integer for the Level Required. We will also need a Test Trigger to hold what will later be our custom script.


    After Creating the variables, trigger, and setting the first instance of the variable arrays. We goto the “DATA” drop down menu, and select “View Script”. Scroll down to where our arrays index 0 is set to a value.

    We are only interested in these two lines of script.

        gv_Instance[0] = "TheStringInstance";
        gv_LevelReq[0] = 99;

    We will be generating the custom script to fill these values for us.


    Back to visual studio. We are going to need the run button to start our program. So by double clicking on the Run button (Button1). Visual studio will give us the event on button click event. All of our code will lie between Private Sub, and End Sub. To be simple in the tutorial, all variables will be created with the Sub.


    Without explaining all restrictions in strings, we come to a problem where Double Quotes(“) are not allowed in our code. In order to represent Double Quotes in our custom script as text, we need to use character codes instead. So I have replaced the Double Quotes with Character Code 34, and rejoined the string at the character code break.

                Dim Instance As String = "gv_Instance[0] = " & Chr(34) & "TheStringInstance" & Chr(34) & ";"
                Dim LevelReq As String = "gv_LevelReq[0] = 99;"

    I need to also note that in order to add line feeds (New Text Line) we need to use 2 character codes at the end of our string. Chr(13) & chr(10)

    After enter in all the code. The form code should look like this.

    Public Class Form1
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim int As Integer
            Dim BuildingScript As String = ""
            For int = 0 To Me.TextBox2.Lines.Count - 1
                Dim strLine As String = Me.TextBox2.Lines(int)
                If IsNumeric(strLine) Then
                    Dim LevelReq As String = "gv_LevelReq[0] = 99;"
                    LevelReq = LevelReq.Replace("LevelReq", Me.TextBox1.Text)
                    LevelReq = LevelReq.Replace("0", int)
                    LevelReq = LevelReq.Replace("99", strLine)
                    LevelReq = LevelReq & Chr(13) & Chr(10)
                    BuildingScript += LevelReq
                    Dim Instance As String = "gv_Instance[0] = " & Chr(34) & "TheString" & Chr(34) & ";"
                    Instance = Instance.Replace("Instance", Me.TextBox1.Text)
                    Instance = Instance.Replace("0", int)
                    Instance = Instance.Replace("TheString", strLine)
                    Instance = Instance & Chr(13) & Chr(10)
                    BuildingScript += Instance
                End If
            Me.TextBox3.Text = BuildingScript
        End Sub
    End Class


    When the button is clicked the code is executed in this order.

    Define variable int as integer
    Define variable BuildingScript as string = empty string
    For each line in textbox 2 (-1 to index array)
    Define variable strLine as string = current line for each
    If strline is numeric then custom script integer, else custom script string
    Define variable LevelReq as string = custom script code
    LevelReq = replace the word LevelReq with Textbox1.text
    LevelReq = replace the word 0 with int (current array index)
    LevelReq = replace the word 99 with strLine (the current line from textbox2)
    LevelReq = Chr(13) & Chr(10)  add a line feed to the currect script line code.
    BuildingScript += LevelReq (add the code to our custom script code)
    Similar function for String instead of integer.
    End if
    Next (read the next line of text in textbox2)
    Me.textbox3.text = BuildingScript (add all of our code to textbox3.text)
    End Sub

    And that’s it, now we can run our program, and start generating Custom Script code for Galaxy. From the debugging drop down in visual studio, click (Start Debugging)

    Name our variable; I am using the name “WOWInstances”.

    Paste in our Values.

    Click the run button.


    From here we can go back to SC2 Map Editor, and create our trigger with custom script.

    We use the action “customscriptaction” in order to write our custom script code into the editor.

    You could place the custom script anywhere, but for this example I will fire it in a test trigger with TriggerExecute action.



    Now when the map starts up, our variables WOWInstances, and Level are filled with their respective values.

    Here is a link to the Asset when its approved.


    Thanks you for reading my quick and dirty Visual Studio Galaxy Code example.

    Happy Coding,

    Sldprt (ItsAboutTime)

    Posted in: Tutorials
  • 0

    posted a message on The Galaxy Wars are ahead of us!
    Quote from SouLCarveRR: Go

    If you can get visual studio to write galaxy with syntax checking and "auto complete" drop down lists. I would be impressed.

    hey there is plenty of editors with syntax correction. why would you want the same thing only in visual studio?

    Posted in: Galaxy Scripting
  • 0

    posted a message on The Galaxy Wars are ahead of us!
    Quote from SouLCarveRR: Go

    If you can get visual studio to write galaxy with syntax checking and "auto complete" drop down lists. I would be impressed.

    this is a good idea, but not a small project at all.

    ok I will gadge the first tutorial at the need to know fo visual studio, and see what the feedback is.

    Posted in: Galaxy Scripting
  • 0

    posted a message on The Galaxy Wars are ahead of us!
    This thread was created to purely help you. The weekend is near, and I know my own project will take me through the weekend. But I want to still give something back to the community. Since my approach to map making is always automation. I decided to write a visual studio tutorial. I would like to hear from you, what kind of things do you want to know about Microsoft Visual studio .Net? What type of automation are you looking for in regards to making SC2 Maps? I plan on making my tutorial functional for all of course. The area I will surround this tutorial is using visual studio to write my own galaxy custom scripts. We will see how this goes with community support, and I will make tutorial 2 about using VS to write custom library’s that can be imported as xml. So I ask you, find your comrades, what do these solders need to know? Let’s all write 18,000 lines of galaxy script in 10 seconds. Let’s all build an English language library of over 500,000 words. Only to have it load fine, but not save. Everyone for themselves, what do you need to know? Where are our bases?
    Posted in: Galaxy Scripting
  • 0

    posted a message on Free name change!

    @xShaelis: Go

    Nice find! This needs to be on the main page.

    Sldprt: ItsAboutTime

    I will be saving my name change forever.

    Posted in: General Chat
  • 0

    posted a message on Card Game fantatics - Joinup here!

    @Fullachain: Go

    I am currently working on wow the trading card game, and I am far into the project. I have taken a tangent that will lead back. Right now I am reconstructing the M3 format. I plan on writing a SolidWorks exporter for M3 files. SolidWorks is much better at dealing with multiple configurations, and animations then 3ds or other animation packages. I am doing this to allow me to then create complicated models that are small in file size. Because I am handing 3500 cards in my wow trading card game, this is a very important step to me, to keep the quality up. If I had not already started my own card game I would be interested in joining you.

    I have both an asset and team for this project. SC2Mapsters WOW The trading card game.

    Posted in: Team Recruitment
  • 0

    posted a message on Variables

    Quote from obliviron:
    @Reach0: Go

    Not necessarily, but in most cases, variables help tremendously (mainly if you have two triggers and want to reference the same player in both, you'd have to create a variable).


    A global Variable outside of any trigger, that holds the unit, and probably and array of units[16]. You can also use a DataTable. A DataTable is in program terms a Collection. I want to steer away from saying it is a collection, because the Galaxy DataTable is much more simply eposed.

    So lets look at the datatable used for Collecting as a matrix.

    First of all instead of thinking of the datatable as a square matrix, like an array is. We need to picture the DataTable as a box that contains things we cant touch. Now when aproaching the box, we speak to it, asking for something I know it has. This starts of like the card game go fish, but I am a cheater, and know what the box contains.

    A datatable can hold anything. Even as simple as instructions on how to rebuild something. I have myself used datatables to redraw jpg’s from string arrays. So lets concider holding everything in the datatable, and knowing how to find it.

    An Array is grid based, and hold one type of variable. A DataTable can do the same, but does not structure as a grid, instead we ask the person in the box to give us what we are looking for.

    This being true, I will give an example of how we use, both types in combination.

    Players are static, and referenced by 1 integer.
    Player as integer[] = New Integer[7]
    Please note: 7 is 8 {01234567}

    Units are non linear, having units born and die. So an array does not make sense, as a place holder. But I want a quick way to check all my units. So I will still bring down my units to array level.

    Lets say each player has 200 unit’s limit, with 8 players. First we will set those units to either Alive, or Not(Yes or No)
    (Yes or No) is true or false, and in programming terms Boolean.
    So I make an array bolUnits[7][199] as boolean
    Within my “Trigger” create unit for player. I will search the array.

    Local Variables
    UnitNumber[7] as integer ‘We need to set default value to 10. Giving each player 10 units

    For each Player 0-7
    For each Unit 0-199
    If bolUnits[Player][Unit] is False Then
    If UnitNumber[Player] > 0 then

    ‘Here we need to write our code for creating a unit. Lets look at it after the Next Player

    End if
    End if
    Next Unit
    Next Player

    We step through each player, looking for if they have any more units to spawn. The rest of the values we are going to create will be in the datatable. We will have to name everything so that we can reverse get something back from the datable.

    Lets look at how to 100% get a unit back. In the code area of “Here we need to write our code for creating a unit”. We are doing 2 things.
    Visiting a Player, and visiting a free space for unit.
    For naming we are going to do 4 things. Name what we are naming for both player and unit.
    As a string it will look like Player1Unit58
    Some others may be Player5Unit32 and Player7Unit68

    here is an example code: ‘ will create a unit and store it in a datatable.

    Create unit facing whatever
    Datatableunit.saveUnit = [Join Strings{[Player][1][Unit][58]}] = Last Created Unit
    DatatableInteger.saveInteger = [Join Strings{[Player][1][Unit][58][“Level”]}] = 1
    DatatablePoint.savePoint = [Join Strings{[Player][1][Unit][58][“Point”]}] = position of unit
    DatatableInteger.saveInteger = [Join Strings{[Player][1][Unit][58][“Money”]}] = 30
    DatatableReal.saveReal = [Join Strings{[Player][1][Unit][58][“Boost”]}] = 5.75
    UnitNumber[Player] = UnitNumber[Player] -1

    ‘Now in order to get my unit back I can search the same way I created the unit. I Wont show this, but you will need to make a trigger for when unit dies trigger. And set its Global variable to False.

    Trigger fires periodicly every 30 seconds. Unit gains a level.

    <Local Variables>

    For each Player 0-7
    For each Unit 0-199
    If bolUnits[Player][Unit] is True Then
    LocalVariableUnit = Datatableunit.GetUnit [Join Strings{[“Player”][Player][“Unit”][Unit]}]
    LocalVariableLevel = DatatableInteger.GetInteger [Join Strings{[Player][1][Unit][58][“Level”]}]  + 1
    LocalVariableMoney = DatatableInteger.GetInteger [Join Strings{[Player][1][Unit][58][“Money”]}] + 10
    LocalVariableBoost = DatatableReal.GetReal [Join Strings{[Player][1][Unit][58][“Boost”]}] + 7.5

    LocalVariableUnit.IncreaseSize = LocalVariableUnit.Size + 2

    DatatableInteger.saveInteger = [Join Strings{[Player][1][Unit][58][“Level”]}] = LocalVariableLevel
    DatatablePoint.savePoint = [Join Strings{[Player][1][Unit][58][“Point”]}] = position of unit
    DatatableInteger.saveInteger = [Join Strings{[Player][1][Unit][58][“Money”]}] = LocalVariableMoney
    DatatableReal.saveReal = [Join Strings{[Player][1][Unit][58][“Boost”]}] = LocalVariableBoost

    End if
    Next Unit
    Next Player

    Oh so much where is the question now?

    Posted in: Triggers
  • To post a comment, please or register a new account.