1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  5. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  6. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  7. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  8. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  9. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Chest System

Submitted by Chaosy
This bundle is marked as substandard. It may contain bugs, not perform optimally or otherwise be in violation of the submission rules.
My first vJASS system that I uploaded. Don't be too harsh.

Even though the system itself is fully vJASS, I tried to make the demo as GUI friendly as possible.

Code (vJASS):
library unitChestLib
/*
    Chest System by Chaosy
   
    Desp:
    The system easily allows you to give random drop chances to a unit when it is unlock. Even though
    the system is designed for chests to drop 'random' items once unlocked, it can also be used as a general
    system to drop items from every unit in the map.
   
    Methods:
        method spawn takes real x, real y returns nothing
        method SetChest takes unit u returns nothing
        method addItem takes integer i, integer chance returns nothing
        method addKey takes integer i returns nothing
        method removeKey takes nothing returns nothing  
        method addGold takes integer i returns nothing
        method removeGold takes integer i returns nothing
        method addLumber takes integer i returns nothing
        method removeLumber takes integer i returns nothing
        method removeItem takes integer someItem returns nothing
        method UnlockChest takes unit u returns nothing
*/


// -- Config --

globals
    //maxindex allowed, with other words a chest can drop 999 items by default
    //note that you can extend the variable above the normal cap (8191)
    private constant integer MAX_INDEX = 999
    //unit type of the chests used
    private constant integer CHEST_TYPE = 'h000'
    //animation played when opened
    private constant string OPEN_ANIMATION = "stand alternate"
endglobals

// -- System --

struct UnitChest

    private integer current = 0
    private integer gold = 0
    private integer lumber = 0
    private integer neededKey = 0
    private integer array items[MAX_INDEX]
    private integer array dropChance[MAX_INDEX]
    unit chest
   
   
    static method create takes nothing returns thistype
        return thistype.allocate()
    endmethod
   
    method spawn takes real x, real y returns nothing
        set .chest = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), CHEST_TYPE, x, y, 0)
    endmethod
   
    method SetChest takes unit u returns nothing
        set .chest = u
    endmethod
   
    method addItem takes integer i, integer chance returns nothing
        set .current = .current + 1
        set .items[.current] = i
        set .dropChance[.current] = chance
        if .current >= MAX_INDEX then
            call BJDebugMsg("Error - Chest System: Max index reached, you might want to re-config the system.")
        endif
    endmethod
   
    method addKey takes integer i returns nothing
        set .neededKey = i
    endmethod
   
    method removeKey takes nothing returns nothing  
        set .neededKey = 0
    endmethod
    method addGold takes integer i returns nothing
        set .gold = .gold + i
    endmethod
   
    method removeGold takes integer i returns nothing
        set .gold = .gold - i
    endmethod
   
    method addLumber takes integer i returns nothing
        set .lumber = .lumber + i
    endmethod
   
    method removeLumber takes integer i returns nothing
        set .lumber = .lumber - i
    endmethod
   
    method removeItem takes integer someItem returns nothing
        local integer i = 0
        local integer i2
        loop
        exitwhen i > .current
            if .items[i] == someItem then
                set i2 = i + 1
                loop
                    exitwhen i2 > .current
                    set .items[i2] = .items[i2 - 1]
                    set .dropChance[i2] = .dropChance[i2 -1]
                    set i2 = i2 + 1
                endloop
                set .current = .current - 1
            endif
            set i = i + 1
        endloop
    endmethod
   
    method UnlockChest takes unit u returns nothing
        local integer rng
        local integer i = 0
        local integer i2 = 0
        local real x = GetUnitX(.chest)
        local real y = GetUnitY(.chest)
        local player p = GetOwningPlayer(u)
        loop
            exitwhen i2>= 6
                if GetItemTypeId(UnitItemInSlotBJ(u, i2)) == .neededKey or .neededKey == 0 then
                    set i2 = 6
                    set udg_CS_gold = .gold
                    set udg_CS_lumber = .lumber
                    set udg_CS_chest = .chest
                    set udg_CS_unlocker = u
                    set udg_CS_event = 1
                    set udg_CS_event = 0
                    call UnitAddAbility(.chest, 'Aloc')
                    call QueueUnitAnimation(.chest, OPEN_ANIMATION)
                    if .gold > 0 then
                        call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + .gold)
                    endif
                    if .lumber > 0 then
                        call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) + .lumber)
                    endif
                    loop
                        exitwhen i > .current
                        set rng = GetRandomInt(1,100)
                        if rng < .dropChance[i] then
                            call CreateItem(.items[i], x, y)
                        endif
                        set i = i + 1
                    endloop
                endif
            set i2 = i2 + 1
        endloop
    endmethod
endstruct
endlibrary
 


demo examples

  • test
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: local UnitChest c
      • Hashtable - Create a hashtable
      • Set hash = (Last created hashtable)
      • -------- - --------
      • Set u = Chest 0001 <gen>
      • Set item = Shadow Orb Fragment
      • Custom script: set c = UnitChest.create()
      • Custom script: call c.SetChest(udg_u)
      • Custom script: call c.addItem(udg_item, 100)
      • Custom script: call c.addGold(100)
      • Set item = Blood Key
      • Custom script: call c.addKey(udg_item)
      • Custom script: call SaveInteger(udg_hash, GetHandleId(c.chest), 1, c)

  • drop
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Unlock Chest
      • (Unit-type of (Target unit of ability being cast)) Equal to Chest
    • Actions
      • Custom script: local unit u = GetSpellTargetUnit()
      • Custom script: local UnitChest c = LoadInteger(udg_hash, GetHandleId(u), 1)
      • Set u = (Triggering unit)
      • Custom script: call c.UnlockChest(udg_u)

  • removetest
    • Events
      • Player - Player 1 (Red) types a chat message containing remove as An exact match
    • Conditions
    • Actions
      • Custom script: local UnitChest c = udg_some_chest
      • Custom script: call c.removeItem(udg_item)

  • spawn
    • Events
      • Player - Player 1 (Red) types a chat message containing spawn as An exact match
    • Conditions
    • Actions
      • Custom script: local UnitChest c = UnitChest.create()
      • Set loc = (Random point in (Playable map area))
      • Set x = (X of loc)
      • Set y = (Y of loc)
      • Custom script: call c.spawn(udg_x, udg_y)
      • Set item = Claws of Attack +3
      • Custom script: call c.addItem(udg_item, 100)
      • Custom script: call SaveInteger(udg_hash, GetHandleId(c.chest), 1, c)

  • event
    • Events
      • Game - CS_event becomes Equal to 1.00
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (A chest was opened by + ((Name of CS_unlocker) + ( and gained + ((String(CS_gold)) + ( gold and + ((String(CS_lumber)) + lumber))))))



Keywords:
Chaosy, system, vjass, vJASS, chest, drop, loot
Contents

Just another Warcraft III map (Map)

Reviews
Moderator
15th Feb 2015 IcemanBo: map is updateable again. 23th April 2015 IcemanBo: http://www.hiveworkshop.com/forums/spells-569/chest-system-258181/index2.html#post2678108 7th Jan 2015 IcemanBo:...
  1. bowser499

    bowser499

    Joined:
    Jul 20, 2009
    Messages:
    782
    Resources:
    7
    Tools:
    1
    Maps:
    3
    Spells:
    3
    Resources:
    7
    Prove it with some facts, please.
    My benchmarking tells me other thing.
    True but tell that to JassHelper v0.A.2.B of mine which states that create method must be declared.

    I wonder, why can't we tell 'for the sake of completion'? :)

    Reasonable.

    Seriously. My opinion is that people mustn't use snippets for making other snippets. Snippets are provided just for usage of them in spells or map but I think it's fair enough if the person wants to do it his own, unique way. Let's not force people to use templates. :)

    These are mostly the things which were said by me in the post above.
     
  2. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,741
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Here is my source Link. Since you mentioned it so straight foreward, I would like to see your tests proofing TriggerHappys thread, which I linked should be re-worked.
     
  3. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,608
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I am glad that you guys have so many suggestions, makes my rookie life easier. I've hold unto some of the suggestions because of split opinions though so I will wait to implement them.

    Because variables are faster than using direct calls? Or am I wrong?

    Doesn't that make it un-modifiable? I have enver used that keyword before so I don't know what it does exactly. I can guess by logic though.

    Why? Those are what I consider 'good' BJ functions since they actually do something useful. It makes the code 4 lines shorter at the price of one extra function call. I'd say it's an acceptable trade.

    I changed most method names, but aren't methods supposed to be capitalized? I think I read that in the jass structure rules. (it's not called that but I don't remember the correct name JPAG or something -.-)
     
    Last edited: Apr 14, 2015
  4. bowser499

    bowser499

    Joined:
    Jul 20, 2009
    Messages:
    782
    Resources:
    7
    Tools:
    1
    Maps:
    3
    Spells:
    3
    Resources:
    7
    Every variable requires extra space in a memory. Direct call is faster than assigning it to a variable, at least in your case this is true since you only need it once.
    It can still be modified by your
    setChest
    method. It is modifiable inside the struct but is not outside of it.
    Own function are tons better.
    UnitItemInSlotBJ
    don't do too-much-useful operation. :D
     
    Last edited: Apr 14, 2015
  5. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,741
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    This is a more or less hardcoded version of what you should try to achieve.
    What can you do different( better) than the code below.
    - remove all that texttag, sound, locust etc... stuff
    - users can run this code onChestOpen trigger evaluation.
    - So you have to provide wrapper function to get TriggerChestPlayer, TriggerChestUnit and TriggerChestResource
    - TriggerChestResource doesn't have to be gold or lumber, but any integer, if you leave PlayerState manipulation to the user onChestOpen event.

    Take whatever you need from this piece of code. Ask questions if you have. :)

    Code (vJASS):
    library Chest /* v1.0
    *************************************************************************************
    *
    *   Create units, acting like treasure chests.
    *
    *************************************************************************************
    *
    *   */
    uses/*
    *
    *       */
    Table                 /* Works with Bribes and Vexorians Table.
    *       */
    optional ErrorMessage /* [url]https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage[/url]
    *       */
    optional Init         /* -
    *
    ************************************************************************************
    *
    *   1. Import instruction
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    *       Copy the Chest script into your map.
    *
    *   2. API
    *   ¯¯¯¯¯¯
    *       API goes here
    *
    *   3. Configuration
    *   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    */


        globals
            private constant boolean ADD_LOCUST_WHEN_OPEN       = true// Comment goes here
            private constant string PREFIX_RECIEVE_GOLD         = "|cffffcc00"
            private constant string PREFIX_RECIEVE_LUMBER       = "|cff006400"
            private constant real TEXT_TAG_HEIGHT               = 0.023
            private constant real TEXT_TAG_HEIGHT_OFFSET_GOLD   = 32.
            private constant real TEXT_TAG_HEIGHT_OFFSET_LUMBER = 80.
            private constant real TEXT_TAG_VELOCITY_X           = 16*0.0005546875*Cos(90*bj_DEGTORAD)
            private constant real TEXT_TAG_VELOCITY_Y           = 16*0.0005546875*Sin(90*bj_DEGTORAD)
            private constant real TEXT_TAG_LIFESPAN             = 2.5
            private constant real TEXT_TAG_FADEPOINT            = 1.75
        endglobals

    // Configurate how you detect in your map an existing chest key.
        private function UnitHasRequiredKey takes unit whichUnit, integer whichKey returns boolean
            local integer index   = 0
            loop
                if (GetItemTypeId(UnitItemInSlot(whichUnit, index)) == whichKey) then
                    return true
                endif

                set index = index + 1
                exitwhen index >= bj_MAX_INVENTORY        
       
            endloop
            return false
        endfunction

    //Script code. Do not edit anything below.
    static if not LIBRARY_Init then
        private module CHEST_INITIALIZER
            private static method onInit takes nothing returns nothing
                call thistype.init()
            endmethod
        endmodule
    endif

        struct Chest
            private static sound error          = null
            private static sound onOpenSound    = null
       
            readonly static trigger openEvent   = CreateTrigger()
            readonly static thistype eventIndex = 0
            private static Table table          = 0
            private static integer goldSlot     = -1
            private static integer lumberSlot   = -2
            readonly unit chestUnit
            private itempool pool
            private Table chestTable
            private integer itemCounter
            private boolean isOpen
            integer requiredKey
           
            method destroyEx takes boolean wantDestroyPool returns nothing
                call deallocate()
                static if Table.flush2D.exists then
                    call table.flush(GetHandleId(chestUnit))
                else
                    call table.remove(GetHandleId(chestUnit))
                endif
                call chestTable.destroy()
                if (wantDestroyPool) then
                    call DestroyItemPool(pool)
                endif
                set pool      = null
                set chestUnit = null
            endmethod
           
            private method manipulateResource takes unit forUnit, playerstate whichPlayerState, integer childKey, string prefix, real offset returns nothing
                local player forPlayer = GetOwningPlayer(forUnit)
                local integer amount   = chestTable[childKey]    
                local string text      = prefix + "+"  

                if (amount != 0) then
                    if (amount < 0) then
                        set text = prefix + "-"
                    endif
                    set text = text + I2S(amount)
                       
                    call SetPlayerState(forPlayer, whichPlayerState, GetPlayerState(forPlayer, whichPlayerState) + amount)
                   
                    if (GetLocalPlayer() == forPlayer) then
                        set bj_lastCreatedTextTag = CreateTextTag()
                        call SetTextTagText(bj_lastCreatedTextTag, text, TEXT_TAG_HEIGHT)
                        call SetTextTagPosUnit(bj_lastCreatedTextTag, forUnit, offset)
                        call SetTextTagVelocity(bj_lastCreatedTextTag, TEXT_TAG_VELOCITY_X, TEXT_TAG_VELOCITY_Y)
                        call SetTextTagPermanent(bj_lastCreatedTextTag, false)
                        call SetTextTagLifespan(bj_lastCreatedTextTag, TEXT_TAG_LIFESPAN)
                        call SetTextTagFadepoint(bj_lastCreatedTextTag, TEXT_TAG_FADEPOINT)
                        call SetTextTagVisibility(bj_lastCreatedTextTag, true)
                    endif
                    set bj_lastCreatedTextTag = null
                endif
            endmethod
           
            // Uses operating unit as argument.
            method open takes unit trysToOpen, integer itemDropAmount, boolean ignoreKey returns boolean
                local thistype prev = eventIndex
                local real x        = GetUnitX(chestUnit)
                local real y        = GetUnitY(chestUnit)
                if (isOpen) then
                    return false
                endif
               
                // Check for wrong usage.
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((trysToOpen == null), "Chest", "open", "unit trysToOpen", this, "Invalid Unit Argument (null)!")
                    call ThrowError((itemDropAmount < 0), "Chest", "open", "itemDropAmount",  this, "Invalid Integer Argument (<0)!")
                endif
               
                // Check if a key is required
                if (requiredKey != 0) and not (ignoreKey) and not (UnitHasRequiredKey(trysToOpen, requiredKey)) then
                    // Error message.
                    if GetLocalPlayer() == GetOwningPlayer(trysToOpen) then
                        call StartSound(error)
                        call ClearTextMessages()
                    endif
                    call DisplayTimedTextToPlayer(GetOwningPlayer(trysToOpen), 0.52, 0.96, 2., "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00Required key:|r " + GetObjectName(requiredKey))
                    return false
                endif
                set isOpen = true

                // Visuals + Sounds
                call SetUnitAnimationByIndex(chestUnit, 0)
               
                static if ADD_LOCUST_WHEN_OPEN then
                    call UnitAddAbility(chestUnit, 'Aloc')
                    // Makes sense to me.
                    call SetTerrainPathable(x, y, PATHING_TYPE_WALKABILITY, false)
                endif
               
                call AttachSoundToUnit(onOpenSound, chestUnit)
                call SetSoundVolume(onOpenSound, 50)
                call StartSound(onOpenSound)

                // Generate Items.
                loop
                    exitwhen (itemDropAmount == 0)
                    call PlaceRandomItem(pool, x, y)
                    set itemDropAmount = itemDropAmount - 1
                endloop
               
                call manipulateResource(trysToOpen, PLAYER_STATE_RESOURCE_GOLD,   goldSlot,   PREFIX_RECIEVE_GOLD, TEXT_TAG_HEIGHT_OFFSET_GOLD)
                call manipulateResource(trysToOpen, PLAYER_STATE_RESOURCE_LUMBER, lumberSlot, PREFIX_RECIEVE_LUMBER, TEXT_TAG_HEIGHT_OFFSET_LUMBER)

                loop
                    exitwhen (itemCounter == 0)
                    set itemCounter = itemCounter - 1
                    call CreateItem(chestTable[itemCounter], x, y)
                endloop
           
                // Fire Event.
                set thistype.eventIndex = this
                call TriggerEvaluate(thistype.openEvent)
                set thistype.eventIndex = prev
               
                // Return boolean
                return true
            endmethod
           
            static method operator [] takes unit whichUnit returns thistype
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowWarning((whichUnit == null),        "Chest", "[]", "whichUnit", 0, "Invalid Unit (null)!")
                    call ThrowWarning((not table.has(whichUnit)), "Chest", "[]", "whichUnit", 0, GetUnitName(whichUnit) + " Is Not A ChestUnit!")
                endif
           
                return table[GetHandleId(whichUnit)]
            endmethod
           
            method addLumber takes integer minLumber, integer maxLumber returns nothing
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((minLumber > maxLumber), "Chest", "addLumber", "min > max", this, "Passed In Minimum Lumber > Passed In Maximum Lumber!")
                endif
               
                set chestTable[thistype.lumberSlot] = GetRandomInt(minLumber, maxLumber)
            endmethod
           
            method addGold takes integer minGold, integer maxGold returns nothing
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((minGold > maxGold), "Chest", "addGold", "min > max", this, "Passed In Minimum Gold > Passed In Maximum Gold!")
                endif
               
                set chestTable[thistype.goldSlot] = GetRandomInt(minGold, maxGold)
            endmethod
           
            method addItemIdExplicit takes integer itemid returns nothing
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((itemid == 0), "Chest", "addItemIdExplicit", "itemid", this, "Invalid Integer Argument (0)!")
                endif            
               
                set chestTable[itemCounter] = itemid
                set itemCounter             = itemCounter + 1
            endmethod
           
            method applyItemPool takes itempool whichItempool returns nothing
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((whichItempool == null), "Chest", "applyItempool", "itempool", this, "Invalid Itempool (null)!")
                    call ThrowWarning((pool != null),        "Chest", "applyItempool", "itempool", this, "Over-writting Itempool Handle!")
                endif
               
                set pool = whichItempool
            endmethod
           
            static method create takes player forWho, integer unitid, real x, real y, real face returns thistype
                local thistype this = thistype.allocate()
               
                set chestUnit       = CreateUnit(forWho, unitid, x, y, face)
                set chestTable      = Table.create()
                set table[GetHandleId(chestUnit)] = this  
                set requiredKey     = 0
                set itemCounter     = 0
                set isOpen          = false
                call PauseUnit(chestUnit, true)
               
                static if DEBUG_MODE and LIBRARY_ErrorMessage then
                    call ThrowError((GetUnitTypeId(chestUnit) == 0), "Chest", "create", "unitid", unitid, "Invalid unitid! Created Chest Unit Is null!")
                endif
               
                return this
            endmethod

            private static method init takes nothing returns nothing
                set Chest.table = Table.create()
                set onOpenSound = CreateSound("Sound\\Ambient\\DoodadEffects\\DoorSlam1.wav", false, false, true, 10, 10, "CombatSoundsEAX")
                set error       = CreateSoundFromLabel("InterfaceError", false, false, false, 10, 10)
            endmethod
           
            implement optional CHEST_INITIALIZER
            implement optional Init
           
        endstruct
       
        function GetTriggerChest takes nothing returns Chest
            return Chest.eventIndex
        endfunction

    endlibrary
     
  6. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,165
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Well there is quite some of critique/suggestions by other users, but let' just work slowly.

    Code (vJASS):
     //maxindex allowed, with other words a chest can drop 999 items by default
        //note that you can extend the variable above the normal cap (8191)
        private constant integer MAX_INDEX = 999
        //unit type of the chests used
        private constant integer CHEST_TYPE = 'h000'
        //animation played when opened
        private constant string OPEN_ANIMATION = "stand alternate"
     

    You note the systems can work for any unit in map. Then the config seems kind of pointless to act as static variables.

    Atm I don't really agree with the coding. Neighter with the concept, nor with efficiency.

    Actually, read stuff suggested by others already.
    For example BPower's code. You don't need to make like him, but you should understand the concept.
    Also I stress his point again:
     
  7. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,608
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Meh. Seems like too much work. Sad as it is since I considered it as one of my better creations.

    Place it as rejected instead, I wont update this.
     
  8. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,165
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22