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 Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  5. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  6. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  7. The results are out! Check them out.
    Dismiss Notice
  8. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  9. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    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.

Unit Naming System 1.2

Submitted by Pinzu
This bundle is marked as approved. It works and satisfies the submission rules.
This is a simple little system I made using the new natives from patch 1.29. It gives regular units hero names that can be manipulated by the user. It's core functionality however, is to give units random hero names as they enter the map.

Requirements
  • GUI version requires patch 1.30+
  • vJass version requires patch 1.29+

Features
  • Allows for manipulation of both unit and hero proper names.
  • Enables creation of name pools that you attach to different unit types.
  • Combines pairs of name pools to generate a random full name that can be given to a unit.
  • Both GUI and vJass compatible
Setup:

0) Copy the necessary libraries, minimum is Table and UnitNaming.

1) To automatically name prelaced units and entering units you need a featued UnitIndexer: [System] UnitDex - Unit Indexer or (GUI Unit Indexer 1.4.0.0 with [vJASS] - [Snippet] GUI Unit Indexer -> vJass Plugin). Alternativly, you can set
INCLUDE_UNAME_EXTENDED = false
to just name units at start or when they enter the map.

2) If you wish to include extended features you must set
INCLUDE_UNAME_EXTENDED = true
in the configuration block. This will save first name, last name, unit name to units. Note that
if you aren't using any of the featured UnitIndexers you must manage deallocation yourself by using:
call DestroyUnitName(udg_yourUnit)


3) Styling
Code (vJASS):

        boolean keepNormalNames     = true                    // Flag for if the unit-type name should be kept
          boolean colorHeroes         = true                    // Flag for if hero names should be colored
          string color                = "|cffffcc00"            // The unit proper name color
       

4) Finally you need to setup your Name pools and associate them with unit types.

  • SetupNamesGUI
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Group 1 --------
      • -------- First Names --------
      • Set nFirstNames[0] = Peter
      • Set nFirstNames[1] = Kenny
      • Set nFirstNames[2] = John
      • Set nFirstNames[3] = Storm
      • Set nFirstNames[4] = Pudge
      • Set nFirstNames[5] = Kyle
      • Set nFirstNames[6] = Jimbo
      • -------- First Names --------
      • Set nLastNames[0] = Bigley
      • Set nLastNames[1] = Crook
      • Set nLastNames[2] = Hartley
      • -------- Last Names --------
      • Set nUnitTypes[0] = Ghoul
      • Set nUnitTypes[1] = Abomination
      • Set nUnitTypes[2] = Necromancer
      • Set nUnitTypes[3] = Zombie
      • Trigger - Run NamePoolSetupGUI <gen> (ignoring conditions)
      • -------- Group 2 --------
      • Set nFirstNames[0] = Sarah
      • Set nFirstNames[1] = Margret
      • Set nFirstNames[2] = Victoria
      • Set nFirstNames[3] = Sophie
      • Set nFirstNames[4] = Sylvana
      • -------- First Names --------
      • Set nLastNames[0] = Bigley
      • Set nLastNames[1] = Crook
      • Set nLastNames[2] = Hartley
      • -------- Last Names --------
      • Set nUnitTypes[0] = Banshee
      • Set nUnitTypes[1] = Sylvanas
      • Trigger - Run NamePoolSetupGUI <gen> (ignoring conditions)
Code (vJASS):
scope UnitNamingSetup initializer Init
    globals
        NamePool firstNamesMale
        NamePool firstNamesFem
        NamePool lastNames
        TypeNamePool females
        TypeNamePool males
    endglobals
    /*     This is the configuration of the name pools, each UniTypePool consist of two pools of first names
        and which unit-types that it should be applied to.
    */

    private function Init takes nothing returns nothing
        set firstNamesFem = NamePool.create()
        set firstNamesMale = NamePool.create()
        set lastNames = NamePool.create()
        set females = TypeNamePool.create(firstNamesFem, lastNames)
        set males = TypeNamePool.create(firstNamesMale, lastNames)
        call females.add('nvlw').add('nhef').add('hsor')
        call males.add('nvil').add('nvl2').add('Hpal').add('nvk2').add('hfoo').add('hrif')
        call ExecuteFunc("SetupFemaleFirstNames")
        call ExecuteFunc("SetupMaleFirstNames")
        call ExecuteFunc("SetupLastNames")
        endfunction
    function SetupLastNames takes nothing returns nothing
        call lastNames.add("Alcaraz")
            call lastNames.add("Anand")
            call lastNames.add("Appiah")
            call lastNames.add("Appleton")
            call lastNames.add("Archer")
            call lastNames.add("Atkins")
            call lastNames.add("Baldwin")
            call lastNames.add("Ball")
            call lastNames.add("Barker")
            call lastNames.add("Barnes")
            call lastNames.add("Barrett")
            call lastNames.add("Bartley")
            call lastNames.add("Beard")
            call lastNames.add("Beard")
            call lastNames.add("Bell")
              call lastNames.add("Betteridge")
                call lastNames.add("Biddle")
            call lastNames.add("Binns")
            call lastNames.add("Bishop")
            call lastNames.add("Boothroyd")
            call lastNames.add("Boyle")
            call lastNames.add("Briggs")
            call lastNames.add("Bright")
            call lastNames.add("Brinn")
            call lastNames.add("Brown")
            call lastNames.add("Brown")
            call lastNames.add("Capell")
            call lastNames.add("Carden")
            call lastNames.add("Carter")
            call lastNames.add("Cass")
            call lastNames.add("Casson")
            call lastNames.add("Childs")
            call lastNames.add("Clark")
            call lastNames.add("Collyer")
            call lastNames.add("Cooch")
            call lastNames.add("Cook")
            call lastNames.add("Cox")
            call lastNames.add("Cross")
            call lastNames.add("Cunningham")
            call lastNames.add("Cuthbert")
            call lastNames.add("Daines")
            call lastNames.add("Davies")
            call lastNames.add("Davis")
    endfunction
 
    function SetupMaleFirstNames takes nothing returns nothing
        call firstNamesMale.add("Paul")
            call firstNamesMale.add("Brian")
              call firstNamesMale.add("Russell")
            call firstNamesMale.add("Jurgen")
            call firstNamesMale.add("Robert")
            call firstNamesMale.add("Michael")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("Harry")
            call firstNamesMale.add("Jeremy")
            call firstNamesMale.add("Ronald")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Harpal")
            call firstNamesMale.add("Adrian")
            call firstNamesMale.add("Timothy")
            call firstNamesMale.add("Mike")
            call firstNamesMale.add("Timothy")
            call firstNamesMale.add("James")
            call firstNamesMale.add("Alastair")
            call firstNamesMale.add("Jonathan")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("Peter")
            call firstNamesMale.add("William")
            call firstNamesMale.add("Colin")
            call firstNamesMale.add("Craig")
            call firstNamesMale.add("Lucus")
            call firstNamesMale.add("Simon")
            call firstNamesMale.add("David")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Stephen")
            call firstNamesMale.add("Jason")
            call firstNamesMale.add("Doug")
            call firstNamesMale.add("Simon")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("Dennis")
            call firstNamesMale.add("Adrian")
            call firstNamesMale.add("Arthur")
            call firstNamesMale.add("William")
            call firstNamesMale.add("Daniel")
            call firstNamesMale.add("Charles")
            call firstNamesMale.add("Stephen")
            call firstNamesMale.add("John")
            call firstNamesMale.add("David")
            call firstNamesMale.add("Andrew")
            call firstNamesMale.add("Ibrar")
            call firstNamesMale.add("Richard")
            call firstNamesMale.add("Samuel")
            call firstNamesMale.add("Paul")
            call firstNamesMale.add("Patrick")
            call firstNamesMale.add("Shawn")
            call firstNamesMale.add("Gordon")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Glenn")
            call firstNamesMale.add("Edward")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("David")
            call firstNamesMale.add("Jonathan")
            call firstNamesMale.add("Moray")
            call firstNamesMale.add("Paul")
            call firstNamesMale.add("Philip")
            call firstNamesMale.add("Stefan")
            call firstNamesMale.add("Christopher")
            call firstNamesMale.add("Jp")
            call firstNamesMale.add("Simmy")
            call firstNamesMale.add("Steven")
            call firstNamesMale.add("Andrew")
            call firstNamesMale.add("Robert")
            call firstNamesMale.add("Christopher")
            call firstNamesMale.add("Paul")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Gordhanbhai")
              call firstNamesMale.add("Douglas")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Evan")
            call firstNamesMale.add("Stuart")
            call firstNamesMale.add("Timothy")
            call firstNamesMale.add("Craig")
            call firstNamesMale.add("Lukman")
            call firstNamesMale.add("David")
            call firstNamesMale.add("John")
            call firstNamesMale.add("Steven")
            call firstNamesMale.add("Baljinder")
            call firstNamesMale.add("Jag")
            call firstNamesMale.add("Nigel")
            call firstNamesMale.add("Kenneth")
            call firstNamesMale.add("Gavin")
            call firstNamesMale.add("Martin")
            call firstNamesMale.add("Anthony")
            call firstNamesMale.add("Richard")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("Christopher")
            call firstNamesMale.add("Paul")
            call firstNamesMale.add("Mark")
            call firstNamesMale.add("Philip")
            call firstNamesMale.add("Peter")
            call firstNamesMale.add("David")
            call firstNamesMale.add("Keith")
            call firstNamesMale.add("Ben")
    endfunction
 
    function SetupFemaleFirstNames takes nothing returns nothing
        call firstNamesFem.add("Mary")
            call firstNamesFem.add("Seema")
            call firstNamesFem.add("Alice")
            call firstNamesFem.add("Carolyn")
            call firstNamesFem.add("Jacqueline")
            call firstNamesFem.add("Christine")
            call firstNamesFem.add("Lesley")
            call firstNamesFem.add("Kay")
            call firstNamesFem.add("Margaret")
            call firstNamesFem.add("Inez")
            call firstNamesFem.add("Sara")
            call firstNamesFem.add("Rose")
            call firstNamesFem.add("Helena")
            call firstNamesFem.add("Sheila")
            call firstNamesFem.add("Lisa")
            call firstNamesFem.add("Pamela")
              call firstNamesFem.add("Ann")
            call firstNamesFem.add("Kandiah")
            call firstNamesFem.add("Lucia")
            call firstNamesFem.add("Sarah")
            call firstNamesFem.add("Louise")
            call firstNamesFem.add("Barbara")
            call firstNamesFem.add("Julie")
            call firstNamesFem.add("May")
            call firstNamesFem.add("Deirdre")
            call firstNamesFem.add("Susan")
            call firstNamesFem.add("Diane")
            call firstNamesFem.add("Inger")
            call firstNamesFem.add("Clare")
            call firstNamesFem.add("Yvonne")
            call firstNamesFem.add("Angela")
            call firstNamesFem.add("Charlotte")
            call firstNamesFem.add("Sally")
            call firstNamesFem.add("Elaine")
            call firstNamesFem.add("Maureen")
            call firstNamesFem.add("June")
            call firstNamesFem.add("Cheryl")
            call firstNamesFem.add("Anne")
            call firstNamesFem.add("Katie")
            call firstNamesFem.add("Judith")
            call firstNamesFem.add("Adele")
            call firstNamesFem.add("Janine")
            call firstNamesFem.add("Julia")
            call firstNamesFem.add("Rosamund")
            call firstNamesFem.add("Anna")
            call firstNamesFem.add("Anthea")
            call firstNamesFem.add("Patricia")
            call firstNamesFem.add("Jane")
            call firstNamesFem.add("Anne")
            call firstNamesFem.add("Melissa")
            call firstNamesFem.add("Carol")
            call firstNamesFem.add("Beverly")
            call firstNamesFem.add("Elizabeth")
            call firstNamesFem.add("Susan")
            call firstNamesFem.add("Margaret")
            call firstNamesFem.add("Mary")
            call firstNamesFem.add("Phyllis")
            call firstNamesFem.add("Lesley")
            call firstNamesFem.add("Ebonie")
            call firstNamesFem.add("Irene")
            call firstNamesFem.add("Maria")
            call firstNamesFem.add("Lesley")
            call firstNamesFem.add("Kim")
            call firstNamesFem.add("Amy")
            call firstNamesFem.add("Katie")
              call firstNamesFem.add("Jane")
            call firstNamesFem.add("Gillian")
            call firstNamesFem.add("Kathleen")
              call firstNamesFem.add("Claire")
            call firstNamesFem.add("Yvonne")
            call firstNamesFem.add("Mildred")
            call firstNamesFem.add("Dawn")
            call firstNamesFem.add("Brenda")
            call firstNamesFem.add("Patricia")
            call firstNamesFem.add("Elizabeth")
            call firstNamesFem.add("Susan")
            call firstNamesFem.add("Lorraine")
            call firstNamesFem.add("Pamela")
            call firstNamesFem.add("Sharon")
            call firstNamesFem.add("Maria")
            call firstNamesFem.add("Suzanne")
            call firstNamesFem.add("Susan")
            call firstNamesFem.add("Jackie")
            call firstNamesFem.add("Victoria")
            call firstNamesFem.add("Valerie")
            call firstNamesFem.add("Nichola")
            call firstNamesFem.add("Susan")
            call firstNamesFem.add("Sarah")
            call firstNamesFem.add("Rebecca")
            call firstNamesFem.add("Patricia")
            call firstNamesFem.add("Belinda")
            call firstNamesFem.add("Anne")
            call firstNamesFem.add("Sarah")
            call firstNamesFem.add("Claudia")
            call firstNamesFem.add("Harriet")
            call firstNamesFem.add("Jennifer")
            call firstNamesFem.add("Winnie")
    endfunction
endscope


Library: UnitNaming
Code (vJASS):

/*
    Naming System v 1.1
        by Pinzu

    This library enables the creation of name pools that can be combined to give regular units
    hero names, and a few other basic things.
 
    Requirements:

        Table by Bribe
          https://hiveworkshop.com/threads/snippet-new-table.188084/
   
    Configuraton
 
        1)  After copying the library to your map you can change public globals to your chosing.
        2)  To use it you have to create a setup trigger somewhere. See "UnitNamingOnEvent" for more.

    API
 
    function UnitHasNamePool takes unit u returns boolean
        returns true if the unit exists in any TypeNamePool.

    function ChangeUnitFullName takes unit u, string firstname, string lastname, string unitname returns nothing
        Changes the units name to provided firstname, lastname and unitname.
   
    function GetUnitNamePool takes unit u returns TypeNamePool
        Returns the name pool that a certain unit belongs to. Useful if you want access to random names from there.
   
    function SetUnitRandomNameSimple takes unit u returns boolean
        Will assign a unit a random name if it belongs to any random unit name pool. Does not store any data about the unit names.
   
    A simple struct containing only a list of strings. You can create it, destroy it, add names to it and draw a random name from it.

    struct NamePool
   
        static method create takes nothing returns thistype
   
        method destroy takes nothing returns nothing
   
        method add takes string name returns thistype
   
        method getRandom takes nothing returns string
 
 
    This struct contains 2 NamePools and a list of unit-types that the name pool belongs to.
 
    struct TypeNamePool
 
        static method create takes NamePool firstPool, NamePool lastPool returns thistype
            Creates it. Requires 2 NamePool to draw first and last names from.
       
        method destroy takes nothing returns nothing
            Deallocates it.
   
        method remove takes integer unitTypeId returns boolean
            Removes a saved unit-type from the pool.
   
        method add takes integer unitTypeId returns thistype
            Adds a unit-type to the pool. Note a unit can only belong to one NamePool at a time.
       
        method getRandomFirstName takes nothing returns string
            Draws a random first name from the pool.
       
        method getRandomLastName takes nothing returns string
            Draws a random last name from the pool.
       
 
    //    
    //    Extended features provides additional API used to replace the regular natives for name management.
    //    Among other things allows to set units first and last name separately, and give units hero proper names. '
    //
 
    API
 
    function SetUnitNormalName takes unit u, string normalname returns nothing
        Basically the same as 'BlzSetUnitName' except that it keeps the proper name if any such is set
 
    function GetUnitNormalName takes unit u returns string
        Basically the same as 'GetUnitName', excludes the proper names if such exist
        Note that it only works for units that have been configured by the system
 
    function SetUnitProperName takes unit u, string firstname, string lastname returns nothing
        Same as setting a heroes name, devided in first and last names
 
    function GetUnitProperName takes unit u returns string
        Returns the units proper name with both first and last name if such exist
   
    function GetUnitFirstName takes unit u returns string
        Returns the first name of the unit
 
    function SetUnitFirstName takes unit u, string firstname returns nothing
        Changes the units first name
 
    function GetUnitLastName takes unit u returns string
        Returns the last name of the unit
 
    function SetUnitLastName takes unit u, string lastname returns nothing
        Changes the units last name

    function SetUnitRandomName takes unit u returns boolean
        Gives a unit a random name based on the unit-type. WARNING: Will store data about the named unit which needs to be removed when the
        unit is no longer used to prevent leaks.
        Note that if the unit-type is not configured to any unit pool the unit wont be affected

    function SaveDefaultName takes unit u returns nothing
        This will save the equivalent of GetUnitName and BlzGetHeroProperName the first time its
        used by the unit. Any times called after that will be ignored, the purpose is to be able
        to reset the units name to the default name.

    function DestroyUnitName takes unit u returns nothing
        Used to deallocate unit names from units. Should be used on deindex
*/


library UnitNaming uses Table optional UnitDex, optional UnitIndexerGUI optional WorldBounds

    /* Configurables */
 
    globals
          boolean keepNormalNames     = false                    // Flag for if the unit-type name should be kept
   
          boolean colorHeroes         = true                    // Flag for if hero names should be colored
   
          string color                = "|cffffcc00"            // The unit proper name color
   
   
        // Used to include extended features, such as being able to change different names associated with a unit and retrieving it.
        //
        constant boolean INCLUDE_UNAME_EXTENDED             = false
   
        // This will save the default name of the unit when it is given a random name upon being indexed.
        // Recommended setting for this is off, unless you have a special reason for using it.
        // What it does is basically when you destroy allocated unit name it will reset to default.
        //
        private constant boolean SAVE_DEFAULT_NAME            = false
   
    endglobals

    /* This is the implementation of the name design, you can switch things around here to whatever you prefer, if you know what you are doing. */
    function ChangeUnitFullName takes unit u, string firstname, string lastname, string unitname returns nothing
        if IsUnitType(u, UNIT_TYPE_HERO) then
            if keepNormalNames then
                call BlzSetUnitName(u, unitname)
            else
                call BlzSetUnitName(u, " ")
            endif
            if colorHeroes then
                call BlzSetHeroProperName(u, color + firstname + " " + lastname + "|r")
            else
                call BlzSetHeroProperName(u, firstname + " " + lastname)
            endif
        else
            if StringLength(firstname) > 0 or StringLength(lastname) > 0 then
                if keepNormalNames then
                    call BlzSetUnitName(u, color + firstname + " " + lastname + "|r|n" + unitname)
                else
                    call BlzSetUnitName(u, color + firstname + " " + lastname + "|r|n")
                endif
            else
                call BlzSetUnitName(u, unitname)
            endif
        endif
    endfunction
 
    /* This is the simple method of giving a unit a random name, it will not store any data to the unit. */
    function SetUnitRandomNameSimple takes unit u returns boolean
        local TypeNamePool namepool = TypeNamePool.typesNameTable[GetUnitTypeId(u)]
        if namepool != 0 then
            call ChangeUnitFullName(u, namepool.getRandomFirstName(), namepool.getRandomLastName(), GetUnitName(u))
            return true
        endif
        return false
    endfunction

    function GetUnitNamePool takes unit u returns TypeNamePool
        return TypeNamePool.typesNameTable[GetUnitTypeId(u)]
    endfunction
 
    function UnitHasNamePool takes unit u returns boolean
        return TypeNamePool.typesNameTable.has(GetUnitTypeId(u))
    endfunction

    struct TypeNamePool
        private Table types
        private integer size
        private NamePool firstNamePool
        private NamePool lastNamePool
 
        static Table typesNameTable = 0
 
        static method setup takes nothing returns nothing
            if .typesNameTable == 0 then
                set typesNameTable = Table.create()
            endif
        endmethod
 
        method getRandomFirstName takes nothing returns string
            return firstNamePool.getRandom()
        endmethod
 
        method getRandomLastName takes nothing returns string
            return lastNamePool.getRandom()
        endmethod
 
        static method create takes NamePool firstPool, NamePool lastPool returns thistype
            local thistype this = .allocate()
            set this.types = Table.create()
            set this.firstNamePool = firstPool
            set this.lastNamePool = lastPool
            set this.size = 0
            return this
        endmethod
 
        private method indexOf takes integer unitTypeId returns integer
            local integer i = 0
            loop
                exitwhen i == .size
                if .types[i] == unitTypeId then
                    return i
                endif
                set i = i + 1
            endloop
            return -1
        endmethod
 
        method remove takes integer unitTypeId returns boolean
            if thistype.typesNameTable[unitTypeId] == this then
                call thistype.typesNameTable.remove(unitTypeId)
                set .size = .size - 1
                set .types[.indexOf(unitTypeId)] = .types[.size]
                call .types.remove(.size)
                return true
            endif
            debug call BJDebugMsg("error - TypeNames.remove: Unit Type does not belong to this name pool.")
            return false
        endmethod
 
        method add takes integer unitTypeId returns thistype
            local thistype prev = thistype.typesNameTable[unitTypeId]
            if prev != 0 then
                call prev.remove(unitTypeId)
            endif
            set thistype.typesNameTable[unitTypeId] = this
            set .types[.size] = unitTypeId
            set .size = .size + 1
            return this
        endmethod
 
        method destroy takes nothing returns nothing
            call types.destroy()
            call .deallocate()
        endmethod
    endstruct

    struct NamePool
        private Table names
        private integer size
 
        static method create takes nothing returns thistype
            local thistype this = .allocate()
            set this.names = Table.create()
            set this.size = 0
            return this
        endmethod
 
        method getRandom takes nothing returns string
            debug if .size <= 0 then
                debug call BJDebugMsg("error - NamePool.getRandom: No names to draw from.")
                debug return ""
            debug endif
            return .names.string[GetRandomInt(0, .size - 1)]
        endmethod
 
        method add takes string name returns thistype
            set .names.string[.size] = name
            set .size = .size + 1
            return this
        endmethod
 
        method destroy takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i == .size
                set .names.string[i] = null
                set i = i + 1
            endloop
            call names.destroy()
            call .deallocate()
        endmethod
    endstruct
 
/*
    Extension Library
*/

 
static if INCLUDE_UNAME_EXTENDED then

    globals
        private constant integer H_NAME     = -1
        private constant integer U_NAME     = 1
    endglobals
   
    struct UnitName

        static Table uTable = 0
        static Table defaultName = 0

        string firstname
        string lastname
        string unitname
        private integer uid

   
        static method create takes unit u, string first, string last returns thistype
            local thistype this = .allocate()
            set this.firstname = first
            set this.lastname = last
            if not (keepNormalNames)then
                set this.unitname = " "
            else
                set this.unitname = GetUnitName(u)
            endif
            set this.uid = GetHandleId(u)
            set uTable[this.uid] = this
            return this
        endmethod
   
        method destroy takes nothing returns nothing
            call uTable.remove(.uid)
            set .firstname = null
            set .lastname = null
            set .unitname = null
            call .deallocate()
        endmethod
   
        static method setup takes nothing returns nothing
            if .uTable == 0 then
                set .uTable = Table.create()
            endif
        endmethod
    endstruct
 
    function SaveDefaultName takes unit u returns nothing
        local integer id = GetHandleId(u)
        if UnitName.defaultName == 0 then
            set UnitName.defaultName = Table.create()
        endif
        if UnitName.defaultName.string.has(U_NAME*id) then
            return
        endif
        set UnitName.defaultName.string[U_NAME*id] = GetUnitName(u)
        if IsUnitType(u, UNIT_TYPE_HERO) then
            set UnitName.defaultName.string[H_NAME*id] = GetHeroProperName(u)
        endif
    endfunction
 
    function SetUnitNormalName takes unit u, string normalname returns nothing
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        static if SAVE_DEFAULT_NAME then
            call SaveDefaultName(u)
        endif
        if name == 0 then
            if normalname == "" then
                call BlzSetUnitName(u, " ")
            else
                call BlzSetUnitName(u, normalname)
            endif
        else
            set name.unitname = normalname
            if not keepNormalNames then
                set keepNormalNames = true    // allows an exception
                call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
                set keepNormalNames = false
            else
                call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
            endif
        endif
    endfunction
 
    function SetUnitProperName takes unit u, string firstname, string lastname returns nothing
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        static if SAVE_DEFAULT_NAME then
            call SaveDefaultName(u)
        endif
        if name == 0 then
            set name = UnitName.create(u, firstname, lastname)
        else
            set name.firstname = firstname
            set name.lastname = lastname
        endif
        call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
    endfunction
 
    function SetUnitFirstName takes unit u, string firstname returns nothing
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        static if SAVE_DEFAULT_NAME then
            call SaveDefaultName(u)
        endif
        if name == 0 then
            set name = UnitName.create(u, firstname, "")
        else
            set name.firstname = firstname
        endif
        call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
    endfunction
 
    function SetUnitLastName takes unit u, string lastname returns nothing
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        static if SAVE_DEFAULT_NAME then
            call SaveDefaultName(u)
        endif
        if name == 0 then
            set name = UnitName.create(u, "", lastname)
        else
            set name.lastname = lastname
        endif
        call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
    endfunction
 
    function GetUnitProperName takes unit u returns string
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        if name == 0 then
            if (IsUnitType(u, UNIT_TYPE_HERO)) then
                return GetHeroProperName(u)
            endif
            return ""
        endif
        return name.firstname + " " + name.lastname
    endfunction
 
    function GetUnitFirstName takes unit u returns string
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        if name == 0 then
            return ""
        endif
        return name.firstname
    endfunction
 
    function GetUnitLastName takes unit u returns string
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        if name == 0 then
            return ""
        endif
        return name.lastname
    endfunction
 
    function GetUnitNormalName takes unit u returns string
        local UnitName name = UnitName.uTable[GetHandleId(u)]
        if name == 0 then
            return ""
        endif
        return name.unitname
    endfunction

    function DestroyUnitName takes unit u returns nothing
        local integer id = GetHandleId(u)
        local UnitName name = UnitName.uTable[id]
        if name != 0 then
            call name.destroy()
        endif
        if UnitName.defaultName.string.has(U_NAME*id) then
            call BlzSetUnitName(u, UnitName.defaultName.string[U_NAME*id])
            call UnitName.defaultName.string.remove(U_NAME*id)
        endif
        if UnitName.defaultName.string.has(H_NAME*id) then
            call BlzSetHeroProperName(u, UnitName.defaultName.string[H_NAME*id])
            call UnitName.defaultName.string.remove(H_NAME*id)
        endif
    endfunction
 
    function SetUnitRandomName takes unit u returns boolean
        local UnitName name
        local TypeNamePool namepool = TypeNamePool.typesNameTable[GetUnitTypeId(u)]
        if namepool == 0 then
            return false
        endif
        static if SAVE_DEFAULT_NAME then
            call SaveDefaultName(u)
        endif
        set name = UnitName.uTable[GetHandleId(u)]
        if name == 0 then
            set name = UnitName.create(u, namepool.getRandomFirstName(), namepool.getRandomLastName())
        else
            set name.firstname = namepool.getRandomFirstName()
            set name.firstname = namepool.getRandomLastName()
        endif
        call ChangeUnitFullName(u, name.firstname, name.lastname, name.unitname)
        return true
    endfunction
endif
 
    /*
        Setup
    */

    private struct Init
 
        static if LIBRARY_UnitIndexerGUI then
            private static method onIndex takes nothing returns boolean
                local unit u = GetIndexedUnit()
                call BJDebugMsg("indexed")
                static if INCLUDE_UNAME_EXTENDED then
                    call SetUnitRandomName(u)
                else
                    call SetUnitRandomNameSimple(u)
                endif
                set u = null
                return false
            endmethod
           
            static if INCLUDE_UNAME_EXTENDED then
                private static method onDeindex takes nothing returns boolean
                    local unit u = GetIndexedUnit()
                    call DestroyUnitName(u)
                    set u = null
                    return false
                endmethod
            endif
        endif
        static if LIBRARY_UnitDex and not LIBRARY_UnitIndexerGUI then
            private static method onIndex takes nothing returns boolean
                local unit u = GetIndexedUnit()
                static if INCLUDE_UNAME_EXTENDED then
                    call SetUnitRandomName(u)
                else
                    call SetUnitRandomNameSimple(u)
                endif
                set u = null
                return false
            endmethod
           
            static if INCLUDE_UNAME_EXTENDED then
                private static method onDeindex takes nothing returns boolean
                    static if INCLUDE_UNAME_EXTENDED then
                        local unit u = GetIndexedUnit()
                        call DestroyUnitName(u)
                        set u = null
                    endif
                    return false
                endmethod
            endif
        endif
   
        static if not LIBRARY_UnitIndexerGUI and not LIBRARY_UnitDex and not INCLUDE_UNAME_EXTENDED then
            private static method onEnterMap takes nothing returns boolean
                call SetUnitRandomNameSimple(GetTriggerUnit()) // No memory saved to unit
                return false
            endmethod
        endif
   
        private static method onTimer takes nothing returns nothing
                local timer t = GetExpiredTimer()
                local group g = CreateGroup()
                local rect r
                local unit u
                local trigger trg
                static if LIBRARY_WorldBounds then
                    set r = WorldBounds.world
                else
                    set r = GetWorldBounds()
                endif
                call GroupEnumUnitsInRect(g, r, null)
                loop
                    set u = FirstOfGroup(g)
                    exitwhen u == null
                    call GroupRemoveUnit(g, u)
                    static if INCLUDE_UNAME_EXTENDED then
                        static if LIBRARY_UnitIndexerGUI then
                            call SetUnitRandomName(u)
                        elseif LIBRARY_UnitDex then
                            call SetUnitRandomName(u)
                        else
                            call SetUnitRandomNameSimple(u)
                        endif
                    else
                        call SetUnitRandomNameSimple(u)
                    endif
                endloop
                static if LIBRARY_UnitIndexerGUI then
                    call OnUnitIndex(function thistype.onIndex)
                    static if INCLUDE_UNAME_EXTENDED then
                        call OnUnitDeindex(function thistype.onDeindex)
                    endif
                endif
                static if LIBRARY_UnitDex and not LIBRARY_UnitIndexerGUI then
                    call RegisterUnitIndexEvent(Filter(function thistype.onIndex)  , EVENT_UNIT_INDEX)
                    static if INCLUDE_UNAME_EXTENDED then
                        call RegisterUnitIndexEvent(Filter(function thistype.onDeindex), EVENT_UNIT_DEINDEX)
                    endif
                endif
                // If no UnitIndexer and extended features disabled then handle entering units
                static if not LIBRARY_UnitDex and not LIBRARY_UnitIndexerGUI and not INCLUDE_UNAME_EXTENDED then
                    set trg = CreateTrigger()
                    call TriggerRegisterEnterRectSimple(trg, r)
                    call TriggerAddAction(trg, function thistype.onEnterMap)
                    set trg = null
                endif
                static if not LIBRARY_WorldBounds then
                    call RemoveRect(r)
                endif
                call DestroyGroup(g)
                call PauseTimer(t)
                call DestroyTimer(t)
                set t = null
                set g = null
                set r = null
        endmethod

        private static method onInit takes nothing returns nothing
            local timer t = CreateTimer()
            call TimerStart(t, 0.2, false, function thistype.onTimer)    // We delay setup slightly
            set t = null
            call TypeNamePool.setup()
            static if INCLUDE_UNAME_EXTENDED then
                call UnitName.setup()
            endif
        endmethod
    endstruct

endlibrary

 


Credits

Bribe for his [Snippet] New Table resource.

Changelog

0.10
- Made a vJass version
- GUI portion disabled
0.12
- Added default returns of of hero and unit name if the provided unit is not recorded in the table.
- Now nulls UnitName first, last, and unit names onDestroy.
- Now removes UnitName from the table if the data saved there can be represented through normal blizzard API.
- Removed the OnEvent part from the library and placed it in a separate trigger.
- Removed HasUnitName
- WorldBounds moved from required to recomended.
0.13
- Removed a needless allocation from SetUnitNormalName
- Fixed a bug with SetUnitNormalName not changing unit name for some cases.
0.14
- Made the vJass version into the main submission.
- Changed the Testing triggers.
- DestroyUnitName now reverts back the unit naming somewhat to mimic blizzard standard.
0.15
- Changed from onDestroy to destroy
- NamePool;destroy added a loop for removing all instances also stored inside typeTable.
- UnitName destroy now resets to *warcraft default* with the data it currently sits on.
- NamePool;onInit added to setup typeTable and uTable.
- Separated uTable into typeTable(NamePool) and uTable(UnitName)
- UnitName:setNormal, setFirst and setLast removed.
- UnitName is now public and designed to be useable on its own.
- Added unit as member variable to UnitName.
- Added a few debug messages
- IsUnitTypeInPool --> NamePool.containsType
- Moved recomended documentation.
1.0
- Remade the system from scratch and divided the system into two parts. The first part handles name pools and changing unit names without being able to remeber what names a unit has configured. The extention contains utility functions to manipulate unit names through getters and setters.
- Remade the GUI setup trigger so it’s easier to implement and also made it compatible with the vJass code (using patch 1.30) - It's now back.
- Fixed a minor issue with test commands.
- The setup now splits names up into first and last names and then pairs a random combination of names into a new full name.
1.1
- Changed example setup to use UnitDex
- If the unit has a saved default name it will be reset when destroy is called.
- Removed the debug message, if one wants to catch errors they just have to use the boolean that is returned.
- Added: function UnitHasNamePool takes unit u returns boolean
- Added: function SaveDefaultName takes unit u returns nothing
- Made the extended version be the default for the test map
- Removed Bribe's Unit Indexer from the GUI version.
- Removed all none configured units from the test map (except for MK)
- Added GUI trigger for changing unit names manually.
- Modifed the GUI setup slightly.
- Fixed a bug where using the random command would only change firstname after the setproper command had been used.
- Fixed so that you can now enter longer last names than one word.
- Added constant to UnitnamingExtended to toggle if the default name should be saved automatically whenver a set-function is used (default is false).
v 1.2
- Merged tables defaultUnitName and defaultHeroName into 1
- Merged library into same file with a static if to enable extended features
- Made the GUI and Jass version work together without any major conflicts
- Included optional UnitDex
- Automatically handles preplaced units and enter map event
- Can be configured to automatically setup simple unit names
- Can be configured to automatically save default names (not recomended)

1.2b
- Fixed a bug with hiding unit type names causing a crash.
- Hiding unit names now also work without extended features.
- Hiding unit names but changing unit-type name will now work when using extended features.
- Fixed a bug with names not being configured properly due to order of onInit


Future works
Nothing at this point.


Keywords: NPC, RP, LOAP, RPG, Custom, Naming, Name.
Contents

NPC Unit Naming System 1.2b (Map)

Reviews
MyPad
Tested the demo map again. This time, the system works with no issues that I have had previously encountered. These issues are now resolved, and everything seems to be in order, from the code itself, to the documentation. There is no reason not to...
  1. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,611
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    What. The. actual. fuck

    How have I missed this native O_O
    Honestly, this is a must have in any RPG map.

    I think the init could be improved greatly however.
    Instead of making copy pasted function calls after every variable, use an array of names and loop through them at the end. This will make the trigger considerably shorter
     
  2. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Good catch. Will add that as well as more documentation I suppose, either later today or more likely tomorrow.

    It's a bit bare bones at the moment but I'm not sure what more configurable or use cases that are needed. I'd appreciate any ideas.

    Update: 0.03

    Added support for Unit Indexer event, as well as different ways to set a random name of a unit. You can now also give a specific name to any unit, and keep track of the default name or the rp name of a unit using two variables.

    I'm considering adding show/hide unit name functionality as a final thing, which made me wonder if local unit name changes would work or cause a desync. Something like this:

    Code (vJASS):

    if (p == GetLocalPlayer()) then
         call BlzSetUnitName(u, name)
    endif


    Update: 0.06

    I consider it somewhat finished now, don't have any pressing changes that I wish to add other than enabling prioritization of unused names over used ones, but that's something I'm not looking to start on any time soon.
     
    Last edited: Jul 26, 2018
  3. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,611
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I still think you're complicating the setup.

    I'd imagine the following:
    unit = paladin
    name[1] = "something"
    name[2] = "something else"
    call setupUnit()

    setupUnit would loop through the name variable and store the names with the unit id as primary key.

    number of custom scripts (that the user have to use) should be limited for GUI systems
     
  4. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    The biggest argument for this would be to reduce the number of function calls (which if the setup is long might be a problem). But the downside would be that you'd have to edit 2 things instead of 1 (name and number) if you want something changed. You'd also need a array list for the unit-types in case you want multiple units with similar names. Can't really decide which optimization I prefer.
     
  5. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    What about a //! call that allows you to load an external .txt file with unit types and names in like a csv format? I'm not all about that but there has to be a way to parse the file into arrays of unit names. Maybe even a few proper user-end textmacroes would help here.
     
  6. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,611
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    It is just that I'd never used this, thinking back to when I was a filthy GUI user.
    Editing custom scripts was a huge turnoff and was something I only would consider if I was really desperate.

    So, I think you should decide on your target audience here.
    If you target jass users this is a bothersome way of doing it, and for pure GUIers it is also not ideal.
    So it is kinda inconvenient for both instead of good for one and bad for the other.
     
  7. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    I would love to see something like that and can think of many use cases for it, but I'm afraid I won't be the one able to implement it.

    The options are the following each with pros and cons, perhaps the left and right ones being the clearer options. I can see why a jasser would prefer the left one as text editing isnt as annoying but I would probably just add parameters to the existing functions or lifting out the HashSaving entierly rather than storing them in temporary variables.
    upload_2018-7-28_8-7-19.png

    What about this one then? upload_2018-7-28_20-15-39.png
     
    Last edited: Jul 28, 2018
  8. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,304
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    I'd think of having them just run a trigger that ends up calling said function. The drawback is that the function must be moved to the map header.
    It's something I learned while making GUI Friendly systems.
     
  9. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Could I get any feedback on the vJass version i made? I attached a test map to it and code below temporarily until I decide what to do with the two versions.

    1) Index/deindex event. Currently there is no such thing but there are API-functions to easily configure that to whatever the user has.

    2) How to manage units that are not touched by the system. For example if the player uses "GetUnitNormalName" should it return null or say, GetUnitName() if not yet recorded...?

    3) Is it necessary to have first name and last name as separate strings, perhaps just assume that the first name is always one word?

    4) How to configure the customizeable parts

    5) Would adding the possibility of picking name by frequency be a valuable addition or mostly bloat?

    vJass version
    Code (vJASS):

    /*
        UnitNaming v 0.12
            by Pinzu
        Giving units proper hero names and other naming utilities.
     
        Requirements:
            Table by Bribe
              https://hiveworkshop.com/threads/snippet-new-table.188084/
            WorldBounds by Nestharus
              https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
         
            Fantasy Name Generators by nerds all over
              http://www.fantasynamegenerators.com/
           
        Configuraton
     
            1)  After copying the library to your map you can change public globals to your chosing.
            2)  At the bottom of the code there is the Setup function, you may chose to exclude a few of
                the textmacros in case you don't wish to include things as destroy name onDeath, name
                unit onEnterMap or name all preplaced units.
        API
        ------------
        struct NamePool
        |
        | static method create takes nothing returns NamePool
        |     create a new NamePool
        |  
        | method destroy takes nothing returns nothing
        |     destroy it
        |  
        | method getRandomName takes nothing returns FullName
        |     returns a random struct containing the proper name that was configured
        |  
        | method addType takes integer unitTypeId returns thistype
        |     Adds a unit-type to this name pool, used for identification by the system
        |  
        | method removeType takes integer unitTypeId returns boolean
        |     Removes a unit-type by index from the name pool
        |  
        | method addName takes string name returns thistype
        |     Simple add (hero) name to pool, does not have any configured last name
        |  
        | method addProperName takes string firstName, string lastName returns thistype
        |     Adds a (hero) name to pool where the first name is separated from the last
        |
        | method getName takes integer index returns string
        |     Returns a proper (hero) name saved at a specific index
        |
        | method removeName takes integer index returns boolean
        |     Removes a name at a specific index
        |
        ----------------
        |
        | function SetUnitNormalName takes unit u, string normalname returns nothing
        |     Basically the same as 'BlzSetUnitName' except that it keeps the proper name if any such is set
        |
        | function GetUnitNormalName takes unit u returns string
        |     Basically the same as 'GetUnitName', excludes the proper names if such exist
        |     Note that it only works for units that have been configured by the system
        |
        | function SetUnitProperName takes unit u, string firstname, string lastname returns nothing
        |     Same as setting a heroes name, devided in first and last names
        |
        | function GetUnitProperName takes unit u returns string
        |     Returns the units proper name with both first and last name if such exist
        |
        | function GetUnitFirstName takes unit u returns string
        |     Returns the first name of the unit
        |
        | function SetUnitFirstName takes unit u, string firstname returns nothing
        |     Changes the units first name
        |
        | function GetUnitLastName takes unit u returns string
        |     Returns the last name of the unit
        |
        | function SetUnitLastName takes unit u, string lastname returns nothing
        |     Changes the units last name
        |
        | function SetUnitRandomName takes unit u returns boolean
        |     Gives a unit a random name based on the unit-type
        |     Note that if the unit-type is not configured to any unit pool the unit wont be affected
        |
        | function HasUnitName takes unit u returns boolean
        |     If the unit exists in the systems records it will return true
        |
        | function DestroyUnitName takes unit u returns nothing
        |     Used to deallocate unit names from units. Should be used on deindex event.
        |
        | function UnitTypeIsInPool takes integer unitTypeId returns boolean
        |     If the unit type has been configured to any unit pool it will return true
        ----------------
    */

    library UnitNaming initializer Init uses Table, WorldBounds
        globals
            // Configuration
            public boolean keepNormalNames     = true        // considering moving this to each pool
            public boolean colorHeroes         = true         // considering moving this to each pool
            public string color                = "|cffffcc00" // considering  moving this to each pool
         
            private Table uTable  
            private integer MAX_STR_LEN        = 1000
        endglobals
     
        private struct FullName
            string first
            string last
        endstruct
     
        struct NamePool
            private Table names
            private integer namesIndex
            private Table types
            private integer typesIndex
         
            method getRandomName takes nothing returns FullName
                local integer rdm = GetRandomInt(0, .namesIndex - 1)
                return .names[rdm]
            endmethod
            private method indexOfType takes integer unitTypeId returns integer
                local integer i = 0
                loop
                    exitwhen i == .typesIndex
                    if (.types[i] == unitTypeId) then
                        return i
                    endif
                    set i = i + 1
                endloop
                return -1
            endmethod
         
            method removeType takes integer unitTypeId returns boolean
                if (uTable[unitTypeId] == this) then
                    call uTable.remove(unitTypeId)
                    set .typesIndex = .typesIndex - 1
                    set .types[.indexOfType(unitTypeId)] = .types[.typesIndex]
                    call .types.remove(.typesIndex)
                    return true
                endif
                return false
            endmethod
         
            method addType takes integer unitTypeId returns thistype
                local NamePool prev = uTable[unitTypeId]
                if (uTable[unitTypeId] != null) then
                    call prev.removeType(unitTypeId)
                endif
                set uTable[unitTypeId] = this
                set .types[.typesIndex] = unitTypeId
                set .typesIndex = .typesIndex + 1
                return this
            endmethod
     
            method getName takes integer index returns string
                local FullName name
                if (index >= namesIndex or index < 0) then
                    return null
                endif
                set name = .names[index]
                return name.first + " " + name.last
            endmethod
         
            method addProperName takes string firstName, string lastName returns thistype
                local FullName fullName = FullName.create()
                set fullName.first =  firstName
                set fullName.last = lastName
                set .names[.namesIndex] = fullName
                set .namesIndex = .namesIndex + 1
                return this
            endmethod
         
            method addName takes string name returns thistype
                call .addProperName(name, "")
                return this
            endmethod
         
            method removeName takes integer index returns boolean
                local integer i
                local FullName fullName
                if (index >= .namesIndex or index < 0) then
                    return false
                endif
                set fullName = .names[index]
                call fullName.destroy()
                loop
                    exitwhen index == .namesIndex
                    set .names[index] = .names[index + 1]
                    set index = index + 1
                endloop
                set .namesIndex = .namesIndex - 1
                call .names.remove(.namesIndex)
                return true
            endmethod
         
            static method create takes nothing returns thistype
                local thistype this = .allocate()
                set this.names = Table.create()
                set this.namesIndex = 0
                set this.types = Table.create()
                set this.typesIndex = 0
                return this
            endmethod
         
            method onDestroy takes nothing returns nothing
                call .names.destroy()
                call .types.destroy()
            endmethod
        endstruct
     
        private struct UnitName
            string nfirst
            string nlast
            string normal
         
            method onDestroy takes nothing returns nothing
                set .nfirst = null
                set .nlast = null
                set .normal = null
            endmethod
         
            static method create takes unit u, string first, string last returns thistype
                local thistype this = .allocate()
                set this.nfirst = first
                set this.nlast = last
                if (keepNormalNames) then
                    set this.normal = GetUnitName(u)
                else
                    set this.normal = ""
                endif
                set uTable[GetHandleId(u)] = this
                return this
            endmethod
         
            static method get takes unit u returns thistype
                local integer id = GetHandleId(u)
                if (uTable.has(id)) then
                    return uTable[id]
                endif
                return 0
            endmethod
            method setFirst takes string name returns thistype
                set .nfirst = name
                return this
            endmethod
         
            method setLast takes string name returns thistype
                set .nlast = name
                return this
            endmethod
         
            method setNormal takes string name returns thistype
                set .normal = name
                return this
            endmethod
         
            method apply takes unit u returns nothing
                if (IsUnitType(u, UNIT_TYPE_HERO)) then
                    call BlzSetUnitName(u, .normal)
                    if (colorHeroes) then
                        call BlzSetHeroProperName(u, color + .nfirst +  " " + .nlast + "|r")
                    else
                        call BlzSetHeroProperName(u, .nfirst +  " " + .nlast)
                    endif
                else
                    if (StringLength(.nfirst) > 0 or StringLength(.nlast) > 0) then
                        call BlzSetUnitName(u, color + .nfirst + " " + .nlast + "|r" + "|n" + .normal)
                    else
                        call BlzSetUnitName(u, .normal)
                    endif
                endif
            endmethod
        endstruct
     
        function SetUnitNormalName takes unit u, string normalname returns nothing
            local UnitName uname
            local integer id = GetHandleId(u)
            if (not(uTable.has(id))) then
                if (IsUnitType(u, UNIT_TYPE_HERO)) then
                    set uname = UnitName.create(u, GetHeroProperName(u), "")
                else
                    set uname = UnitName.create(u, "", "")
                endif
            else
                set uname = uTable[id]
            endif
            call uname.setNormal(normalname).apply(u)
        endfunction
     
        function SetUnitProperName takes unit u, string firstname, string lastname returns nothing
            local UnitName uname
            local integer id = GetHandleId(u)
            if (not(uTable.has(id))) then
                set uname = UnitName.create(u, firstname, lastname)
            else
                set uname = uTable[id]
            endif
            call uname.setFirst(firstname).setLast(lastname).apply(u)
        endfunction
     
        function UnitTypeIsInPool takes integer unitTypeId returns boolean
            return uTable.has(unitTypeId)
        endfunction
     
        function HasUnitName takes unit u returns boolean
            return uTable.has(GetHandleId(u))
        endfunction
     
        function SetUnitRandomName takes unit u returns boolean
            local UnitName uname
            local FullName name
            local NamePool np
            local integer id = GetUnitTypeId(u)
            if (not(UnitTypeIsInPool(id))) then
                return false
            endif
            set np = uTable[id]
            set name = np.getRandomName()
            if (HasUnitName(u)) then
                set uname = uTable[GetHandleId(u)]
                call BlzSetUnitName(u, uname.normal)
                call uname.setFirst(name.first).setLast(name.last).apply(u)
            else
                set uname = UnitName.create(u, name.first, name.last)
                call uname.apply(u)
            endif
            return true
        endfunction
     
        function GetUnitProperName takes unit u returns string
            local integer id = GetHandleId(u)
            local UnitName uname
            if (not(HasUnitName(u))) then
                if (IsUnitType(u, UNIT_TYPE_HERO)) then
                    return GetHeroProperName(u) // The unit doesn't have a record
                endif
                return null
            endif
            set uname = uTable[id]
            return uname.nfirst + " " + uname.nlast
        endfunction
     
        function SetUnitFirstName takes unit u, string firstname returns nothing
            local UnitName uname
            if (not(HasUnitName(u))) then
                set uname = UnitName.create(u, firstname, "")
            else
                set uname = uTable[GetHandleId(u)]
                set uname.nfirst = firstname
            endif
            call uname.apply(u)
        endfunction
     
        function SetUnitLastName takes unit u, string lastname returns nothing
            local UnitName uname
            if (not(HasUnitName(u))) then
                set uname = UnitName.create(u, "", lastname)
            else
                set uname = uTable[GetHandleId(u)]
                set uname.nlast = lastname
            endif
            call uname.apply(u)
        endfunction
     
        function GetUnitFirstName takes unit u returns string
            local UnitName uname
            if (not(HasUnitName(u))) then
                return null
            endif
            set uname = uTable[GetHandleId(u)]
            return uname.nfirst
        endfunction
     
        function GetUnitLastName takes unit u returns string
            local UnitName uname
            if (not(HasUnitName(u))) then
                return null
            endif
            set uname = uTable[GetHandleId(u)]
            return uname.nlast
        endfunction
     
        function GetUnitNormalName takes unit u returns string
            local integer id = GetHandleId(u)
            local UnitName uname
            if (not(HasUnitName(u))) then
                return GetUnitName(u) // The unit doesn't have a record
            endif
            set uname = uTable[id]
            return uname.normal
        endfunction
     
        function DestroyUnitName takes unit u returns nothing
            local integer id = GetHandleId(u)
            local UnitName uname = uTable[id]
            call uname.destroy()
            call uTable.remove(id)
        endfunction
     
        private function Init takes nothing returns nothing
            set uTable = Table.create()
        endfunction
    endlibrary
     


    New Setup
    Code (vJASS):

    scope UnigNamingSetup initializer Init
        globals
            NamePool array np[10]
        endglobals
     
        private function Main takes nothing returns nothing
            local trigger t = GetTriggeringTrigger()
            local integer i
     
            // Allocating the name pool
            set i = 0
            set np[i] = NamePool.create()
     
            // Here we are adding which unit-types will be attached to this name pool
            call np[i].addType('hfoo').addType('hkni').addType('Hpal')
     
            // We can either set up names like this,
            // which will set the proper name to only consit of a first name
            call np[i].addName("Pinzu, Just Pinzu")
     
            // Or like, which sets up names as first and last names respectivly.
            // This can then be used to address units by their first name or last name.
            // However, if you don't need this feature I'd recommend you stick to the first option.
            call np[i].addProperName("Pinzu", "San")
            set i = 1
            set np[i] = NamePool.create()
            call np[i].addType('nvil').addType('nvl2')
            call np[i].addName("Kendall Werner").addName("Raul Tindall").addName("Rodhlann Brent")
            call np[i].addName("Rodhlann Brent").addName("Quinten Reed").addName ("Wingate Burlingame")
            call np[i].addName("Jefrem Swett").addName("Hayo Dryden").addName("Tyeson Harley")
            call np[i].addName("Jan Lee").addName("Ogden Acton")
            set i = 2
            set np[i] = NamePool.create()
            call np[i].addType('nvlw').addType('nhef').addType('hsor')
            call np[i].addName("Cher Whitney").addName("Merry Sue").addName("Ashli Cooper").addName("Joanna Smythe")
            call np[i].addName("Marquise Kendall").addName("Sahra Astley").addName("Lisbeth Salander")
     
            set i = 3
            set np[i] = NamePool.create()
            call np[i].addType('nvlk').addType('nvk2')
            call np[i].addName("Dinbush Quietneedle").addName("Teendic Mekkamix").addName("Fucuzz Puddleheart").addName("Henidu Battlebrake")
     
            // deallocating the name pool
            call np[9].destroy()
     
            // Read the full API list for more functions and methods to use
     
            call DisableTrigger(t)
            call DestroyTrigger(t)
            set t = null
        endfunction
     
        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterTimerEvent(t, 0.01, false)
            call TriggerAddAction(t, function Main)
            set t = null
        endfunction
    endscope
     


    Sample Usage
    Code (vJASS):

    scope UnitNamingTest initializer Init
     
        globals
            private string array commands
            private integer array len
            private group g = CreateGroup()
            private integer max
        endglobals
     
        function GetOneSelectedPlayerUnit takes player p returns unit
            local integer count = 0
            local unit u
            local unit found
            call SyncSelections()
            call GroupEnumUnitsSelected(g, p, null)
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null
                call GroupRemoveUnit(g, u)
                if GetOwningPlayer(u) == p then
                    set count = count + 1
                    if count == 1 then
                        set found = u
                    else
                        set found = null
                    endif
                endif
            endloop
            return found
        endfunction
     
        /* Changes first name */
        private function cmd0 takes string s returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call SetUnitFirstName(u, s)
            set u = null
        endfunction
     
        /* Changes last name */
        private function cmd1 takes string s returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call SetUnitLastName(u, s)
            set u = null
        endfunction
     
        /* Changes unit name */
        private function cmd2 takes string s returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call SetUnitNormalName(u,s)
            set u = null
        endfunction
     
        /* Changes unit proper name  */
        private function cmd3 takes string s returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call SetUnitProperName(u, "Proper firstname", "Proper lastname")
            set u = null
        endfunction
     
        /* Set new random name */
        private function cmd4 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            local boolean success = SetUnitRandomName(u)
            if (not(success)) then
                call BJDebugMsg("This unit cannot be renamed")
            else
                call BJDebugMsg("Renaming unit from " + GetUnitNormalName(u))
            endif
            set u = null
        endfunction
     
        /* Retrieves first name */
        private function cmd5 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call BJDebugMsg("First name: " + GetUnitFirstName(u))
            set u = null
        endfunction
     
        /* Retrieves first name */
        private function cmd6 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call BJDebugMsg("Last name: " + GetUnitLastName(u))
            set u = null
        endfunction
     
        /* Retrieves unit name */
        private function cmd7 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call BJDebugMsg("Unit name: " + GetUnitNormalName(u))
            set u = null
        endfunction
     
        /* Retrieves unit name */
        private function cmd8 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call BJDebugMsg("Proper name: " + GetUnitNormalName(u))
            set u = null
        endfunction
     
        /* Checkes the unit state as it relates to the system */
        private function cmd9 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            if (HasUnitName(u)) then
                call BJDebugMsg("Has name")
            else
                call BJDebugMsg("No name")
            endif
            if (UnitTypeIsInPool(GetUnitTypeId(u))) then
                call BJDebugMsg("Unit type exists")
            else
                call BJDebugMsg("Unit type doesn't exist")
            endif
            set u = null
        endfunction
     
        /* Destroys UnitName (does not affect the current unit name though) */
        private function cmd10 takes nothing returns nothing
            local unit u = GetOneSelectedPlayerUnit(GetTriggerPlayer())
            call BJDebugMsg("Proper name: " + GetUnitNormalName(u))
            set u = null
        endfunction
     
        private function Main takes nothing returns nothing
            local string s = GetEventPlayerChatString()
            local integer i = 0
            loop
                exitwhen i == max + 1
                if (SubString(s, 0, len[i]) == commands[i]) then
                    if (i == 0) then
                        call cmd0(SubString(s, len[i] + 1, 33))
                    elseif (i == 1) then
                        call cmd1(SubString(s, len[i] + 1, 33))
                    elseif (i == 2) then
                        call cmd2(SubString(s, len[i] + 1, 33))
                    elseif (i == 3) then
                        call cmd3(SubString(s, len[i] + 1, 33))
                    elseif(i == 4) then
                        call cmd4()
                    elseif(i == 5) then
                        call cmd5()
                    elseif(i == 6) then
                        call cmd6()
                    elseif(i == 7) then
                        call cmd7()
                    elseif(i == 8) then
                        call cmd8()
                    elseif(i == 9) then
                        call cmd9()
                    elseif(i == 10) then
                        call cmd10()
                    endif
                endif
                set i = i + 1
            endloop
        endfunction
        private function Init takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterPlayerChatEvent(t, Player(0), "-", false)
            call TriggerAddAction(t, function Main)
     
            set commands[0] = "-setfirst"
            set len[0] = StringLength(commands[0])
            set commands[1] = "-setlast"
            set len[1] = StringLength(commands[1])
            set commands[2] = "-setuname"
            set len[2] = StringLength(commands[2])
            set commands[3] = "-setproper"
            set len[3] = StringLength(commands[3])
            set commands[4] = "-random"
            set len[4] = StringLength(commands[4])
            set commands[5] = "-getfirst"
            set len[5] = StringLength(commands[5])
            set commands[6] = "-getlast"
            set len[6] = StringLength(commands[6])
            set commands[7] = "-getuname"
            set len[7] = StringLength(commands[7])
            set commands[8] = "-getproper"
            set len[8] = StringLength(commands[8])
            set commands[9] = "-check"
            set len[9] = StringLength(commands[9])
            set commands[10] = "-destroy"
            set len[10] = StringLength(commands[10])
            set max = 10
     
        endfunction
    endscope
     

     
    Last edited: Aug 4, 2018
  10. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    This would help you load unit names from an external .txt file without having to use lua objectmerger calls: [vJASS] - FileIO
     
  11. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,304
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    1.) I think such a feature, if wanted, should be introduced in another library or trigger.
    2.) If not yet recorded, default to the unit's name.
    3.) That is a design feature, which is up to you to manage.
    4 and 5.) I'm not sure I understand.

    I will test this later..
     
  12. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    I didn't manage to run it, also i question if that would work outside a single player realm since everyone must have the file. However, imported files could work.

    A solution I could offer is a solution like this:

    Code (vJASS):

            local NamePool namepool = NamePool.create()
            local string names = "
            Kendall Werner
            Raul Tindall
            Rodhlann Brent
            Jefrem Swett
            Hayo Dryden
            ...
            Wingate Burlingame
            Jefrem Swett
            Hayo Dryden
            Tyeson Harley
            Jan Lee
            Ogden Acton
            "

            call BJDebugMsg(names)
            call namepool.addAllNames(names)


    But this solution is capped to 1024 or so characters per batch and would be a bit volatile since some special characters are longer than 1 byte.
     
    Last edited: Aug 3, 2018
  13. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    The only computer that needs to have access to the name file is the computer that saves the map. If you save it and give it to a friend they will have unit names set up properly, but if they open it and try to save it again they will disappear. If you need to share the map with someone you could either import the file or save its text into a disabled trigger. This FileIO functionality I suggest helps the map developer who has many names they may want to change or edit on the fly, and they only must edit the .txt file before saving the map to push changes.
     
  14. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,611
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Yikes, that is a lot of code. That HAS to be inefficient somehow O-o
     
  15. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    Depends on what you want to do, If you only want to give NPC names at start then yes, most of the code would be useless, and you could probably get away by only using the methods provided by NamePool (struct)

    There is also lot of boiler plate code in the functions used to get/set firstname, lastname, unitname and stuff like that. All this could be done directly through the struct UnitName probably, but since units are not indexed i chose to use wrapper functions that the user can utilize rather than doing something like:

    set unitname[unitIndex].firstname = entered substring
     call unitname[unitIndex].apply()


    A option would be to add a static method to find UnitName struct based on a unit and then just put all the wrappers in there directly but it makes for uglier use. Example:

    Code (vJASS):

        private function OnSetLastName takes nothing returns nothing
            local player p = GetTriggerPlayer()
            local unit u = GetOneSelectedPlayerUnit(p)
            local string substr = GetLastPlayerChat(GetPlayerId(p)).subStr
     
            // This
            call SetUnitLastName(u, substr)
            // Becomes this and then it's not even checking if UnitName is null or not to allocate a new one.
            call UnitName.get(u).setLast(substr).apply(u)
     
            call BJDebugMsg("Setting Last Name: " + substr)
            set p = null
        endfunction
       


    Finally you have the 80 lines of code which I've now lifted out.
    hmm
    Code (vJASS):

      // Optional Code Below
        private function OnUnitEntersMap takes nothing returns nothing
            call SetUnitRandomName(GetTriggerUnit())
        endfunction
        private function OnDeath takes nothing returns nothing
            local unit u = GetTriggerUnit()
            if (HasUnitName(u)) then
                call DestroyUnitName(u)
            endif
            set u = null
        endfunction
        private function FilterUnits takes nothing returns boolean
            return systemEnabled and uTable.has(GetUnitTypeId(GetFilterUnit()))
        endfunction
        private function Setup takes nothing returns nothing
            local timer tmr = GetExpiredTimer()
            local unit u
            local group g
            local trigger t
            local integer i
            /*
            *   Optional 1 - Allocate a unit name for all preplaced and configured units. Remove the
            *   textmacro below if you do  not wiish to include this feature.
            */

            set t = CreateTrigger()
            call TriggerRegisterEnterRegion(t, WorldBounds.worldRegion, Filter(function FilterUnits))
            call TriggerAddAction(t, function OnUnitEntersMap)
            /*
             *  Optional 2 - Allocates a unit name on entering to the world.
             *  If this does not suit your sensibilities you only have to remove this and call
             *  "SetUnitRandomName" for each and every unit you want named later.
             *  Remove the textmacro below if you have your own unit index events or for some other
             *  reason do not wish to include this feature.
             */

            set g = CreateGroup()
            call GroupEnumUnitsInRect(g, WorldBounds.world,  Filter(function FilterUnits))
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null
                /* Insert your filter here */
                if (true) then
                    call SetUnitRandomName(u)
                endif
                call GroupRemoveUnit(g, u)
            endloop
            call DestroyGroup(g)
            /*
             *  Optional 3 - Deallocates unit names on death
             *  if this does not suit your needs simply call "DestroyUnitName" when you wish to
             *  deallocate the unit, on deindex event as an example. And again remove the textmacro
             *  below.
             */

            set g = CreateGroup()
            call GroupEnumUnitsInRect(g, WorldBounds.world,  Filter(function FilterUnits))
            call PauseTimer(tmr)
            call DestroyTimer(tmr)
            set t = null
            set g = null
            set u = null
            set tmr = null
        endfunction
     
        private function Init takes nothing returns nothing
            local timer tmr = CreateTimer()
            set uTable = Table.create()
            call TimerStart(tmr, 0.03   , false, function Setup)
            set tmr = null
        endfunction


    A possible solution could be to devide the system in 2, one for the random NPC names, the other for a extended version of unit name manipulation. But that's not something I would look forward to do now that they are so closely interlinked... ^^

    Updated: 0.14
    Implemented some of MyPads suggested changes and attempted to optimize a few things.
     
    Last edited: Aug 4, 2018
  16. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,304
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    vJASS version

    Notes:


    • The global table uTable is not initialized within a module. This could cause unintended behavior with systems that use it within either a struct or a library initialization.
    • Using uTable as both the unit instance mapper (to UnitName instances) and unit type instance mapper (NamePool instances) is not likely to be at risk to the possibility of collisions within entries, but using two separate instances ensures safety of the user. (Assume a unit with the rawcode within the range of 1049072, not very likely but can be messy)
    • With the current setup of the unit type instance mapper, it seems that one cannot add it to at least two instances without causing it to be removed on one instance. Is this intended?
    • The boundary checks for the integer parameter in method NamePool(1).getName could print out a debug message in debug mode, since the returned value without it is likely going to be " ".
    • Method NamePool(1).addName could just use the returned result from addProperName. (This saves a line)
    • On a onDestroy method in struct NamePool, it is unlikely for the unit type instances stored therein to be unmapped from the global variable uTable. This means that certain unit types mapped to that instance will not be removed as well. (Assume instance 1 mapped out unit types hfoo, and hpea. If instance 1 is destroyed, hfoo and hpea stay in the global table uTable.)
    • More on the onDestroy method, you can just directly declare it as destroy. The downside is the manual declaration of deallocation, which shouldn't be too much. (Applies to both UnitName and NamePool)
    • Using a doubly-linked list may benefit the system in the long run. (It makes removal and insertion a breeze and is a possible solution to the problem in the onDestroy method). See [Containers] List<T> by Bannar.
    • Oddly enough, the recommended libraries aren't optional nor mandatory. Documentation on those particular libraries, if not optional or mandatory, should be moved to another library which uses said libraries.

    Nitpicks:


    • I suggest renaming UnitTypeIsInPool to IsUnitTypeInPool. It seems more intuitive that way.


    Both GUI and vJASS version:

    I think UnitEvent (for GUI by Bribe) or UnitDex (by TriggerHappy) are good replacements for the UnitIndexer requirement (unless its' by Nestharus, that is basically good enough on what it does, and maybe a bit better.)
     
    Last edited: Aug 5, 2018
  17. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    1) Okay added onInit to NamePool.

    2) Okidoks.

    3) It's intended. If you could add your unit-type to two pools there becomes a problem of determining which pool to draw the name from. So I figured best keep it to the last pool assigned.

    4) It would return "" (null) But i can add
    debug call BJDebugMsg("error - NamePool.getName:IndexOutOfBounds")
    if you like.

    5) I made it return thistype so that you can write like this:
    call namepool.addName(...).addName(...).addName()
    and so on.

    6) Ah good catch with UnitPool;onDestroy, fixed.
    solution
    Code (vJASS):

    method onDestroy takes nothing returns nothing
                local integer i = 0
                loop
                    exitwhen i == .typesIndex
                    call uTable.remove(.types[i])
                    set i = i + 1
                endloop
                call .names.destroy()
                call .types.destroy()
            endmethod


    7)

    8) I've seen ListT by banner and it didn't have IndexOf (or whatever my qualms were) and I figured it would just add another dependency for little benefit at this point. I'll consider it in the future if I find another scenario where it would be needed.

    On UnitIndexer, I have thought about using a UnitIndexer for storing the UnitNames rather than Table. But that argument is old, and I am rather lazy, so I prefer not to include any UnitIndexers in the vJass version unless people think that it would be a huge negative. As for the GUI one I'll be sticking to Bribe there I reckon, or in any case as much as possible decouple the unit indexer from the system code. A option would be to put most of the functions inside the UnitName struct thus the unit.id could be used to modify the unit. But I'm not a fan of this solution.

    Thanks for your long and rather extensive review! I appreciate it a lot.
     
    Last edited: Aug 5, 2018
  18. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,304
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    Looks like I had inadvertently changed the name from NamePool to UnitPool when reviewing :p. Sorry for the confusion.
     
    Last edited: Aug 5, 2018
  19. Pinzu

    Pinzu

    Joined:
    Nov 30, 2007
    Messages:
    1,177
    Resources:
    3
    Spells:
    2
    Tutorials:
    1
    Resources:
    3
    It was funny at first, but then I started writing it everywhere. I curse you and your shenanigans! o_O

    Posted 0.15. I'm not completely done though as I'm still working on splitting the system in 2 and perhaps implementing ListT for preventing randomization of same unit name twice (and reducing code base I hope). However I'm still not sure I want this dependency :d
     
    Last edited: Aug 5, 2018