• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Unit Naming System 1.2

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

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
JASS:
        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)
JASS:
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

JASS:
/*
    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


Bribe for his [Snippet] New Table resource.


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


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...
Level 15
Joined
Nov 30, 2007
Messages
1,202
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

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:

JASS:
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:

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,182
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
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
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

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.
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
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.
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,182
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.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
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.

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.

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.

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:
Level 15
Joined
Nov 30, 2007
Messages
1,202
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?

JASS:
/*
    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

JASS:
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

JASS:
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:
Level 15
Joined
Nov 30, 2007
Messages
1,202
This would help you load unit names from an external .txt file without having to use lua objectmerger calls: [vJASS] - FileIO

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:

JASS:
        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:
Level 38
Joined
Feb 27, 2007
Messages
4,951
i question if that would work outside a single player realm since everyone must have the file
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.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Yikes, that is a lot of code. That HAS to be inefficient somehow O-o

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:

JASS:
    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.
JASS:
  // 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:

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:
Level 15
Joined
Nov 30, 2007
Messages
1,202

Notes:

...

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.
JASS:
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:
Level 15
Joined
Nov 30, 2007
Messages
1,202
Looks like I changed the name from NamePool to UnitPool when reviewing :p. Sorry for the confusion.

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:
Level 15
Joined
Nov 30, 2007
Messages
1,202
I finished a spread sheet script for name to code conversion. If anyone could re-submit it for me on google sheet and provide a link it would be great (Message me).

To use it the user would have to clone it to their google drive and just modify the relevant cells and then copy the generated code into their map.

upload_2018-8-11_7-37-33.png


Also, I've decided to remove all the getters and setters (for different name-types) from the core of the system. It will be in a secondary library which then would be optional.

Update: v 1.0
- The GUI version has made a comeback with patch 1.30 so they now share the same library.
- Divided the system into two parts to please Chaosy.
- Removed alot of useless code.
 
Last edited:

Test Environment:


As the test map started, 6 error messages popped up. An error message popping up on startup could cast some doubt on the reliability of the resource, so I had managed to trace it down to the footmen and the Blademaster. It turns out they were not added into the setup trigger.

I am reporting this part as something that needs to be addressed.

Notes:

  • Destroying the unit's name does not actually reset the given name. Is this intended? (Tested on units. Link to the video footage: Here) {Needs fix}
  • The logic of adding of names to units on map initialization could be outsourced to an existing library that catches unit creation. (See [System] UnitDex - Unit Indexer)
    {Not so urgent fix}

Nitpicks:

  • None

Status:

  • Awaiting Update
 
Last edited:
Level 15
Joined
Nov 30, 2007
Messages
1,202
*cough* The double naming is due to having multiple events fiering for indexing in the test map. Seem to have forgotten to disable the GUI OnUnitIndexEvent, my bad. :)

As for the debug messages I could add a check function for if the unit type exists in a pool. It's not really an error though as it simply returns false if it failed to find a name pool.

I'm not sure if the destroy method should reset the name to whatever, it's just to free the associated memory from that unit, though it would be logical use that destroy resets the name I question the utility of such an implementation. I suppose I could save unit-type names in a table as they are first managed by the system, hero names would also have to be saved.

And on the setup/configuration part well Im unsure. There are currently 2 types of setups either one calls ChangeToRandomName or SetUnitRandomName it's not the same thing. The first simply will change the name and the latter will also configure it with the extended version. So if you use ChangeUnitFullName and then use a command like -setfirst Timmy, you will get double names and it won't work properly, but if you instead use SetUnitRandomName and then use the command it will work.
 
Last edited:
Level 15
Joined
Nov 30, 2007
Messages
1,202
When testing the commands, the one on top is the proper name, while the one in the bottom is the unit name, although that is not what I had intended to report.
You accuse me of false advertising? ^^

JASS:
scope NamingUnits initializer Init
 
    /* Caution:
    ChangeToRandomName does not work exactly the same as SetUnitRandomName. The former
    will simply rename the unit WITHOUT saving the unit name data. Using any of the extended
    functions AFTER using ChangeToRandomName will NOT work together properly, so keep that in mind
    when you chose how you want ur units to be named.
 
    The reason for the separation, is if you don't want to allocate memory that you won't need and
    simply want to rename the unit once.
    */
 
    function onIndex takes nothing returns boolean
    local unit u = GetIndexedUnit()
    // EX 1
    //if UnitHasNamePool(u) then
    //    call ChangeToRandomName(u)
    //endif
    // EX 2
    //if not ChangeToRandomName(u) then
        // Failed to rename the unit due to it not having a name pool
    //endif
 
    // EX 3 (Recomended)
    //call ChangeToRandomName(u)
 
    // Extremly optional but will save the units current name the first time it's called.
    call SaveDefaultName(u)
    if UnitHasNamePool(u) then
        call SetUnitRandomName(u)
    endif
        set u = null
        return false
    endfunction
    function onDeindex takes nothing returns boolean
    local unit u = GetIndexedUnit()
    call DestroyUnitName(u)        // Only necessary if using any of the extended features.
        set u = null
        return false
    endfunction
    private function Init takes nothing returns nothing
        call RegisterUnitIndexEvent(Filter(function onIndex)  , EVENT_UNIT_INDEX)
        call RegisterUnitIndexEvent(Filter(function onDeindex), EVENT_UNIT_DEINDEX)
    endfunction
endscope


- 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).


Updated: v 1.1

Note 1: if the indention looks bad I blame the vanilla editor and having to copy code over from notepad.
 
Last edited:
Tested the demo map again. This time, the system works with no issues that I have had previously encountered.

Notes:
  • Destroying the unit's name does not actually reset the given name. Is this intended? (Tested on units. Link to the video footage: Here) {Needs fix}
  • The logic of adding of names to units on map initialization could be outsourced to an existing library that catches unit creation. (See [System] UnitDex - Unit Indexer)
    {Not so urgent fix}

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 approve this any longer. However, any improvements to the system are welcome.

Status:

  • Approved
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Updated to 1.2b

Removed most of the vjass examples of how to use and instead created a GUI compatible command use so both can learn how to use it.

Added automatic setup for handling Unit Enter Map and Name Preplaced Units. It works for the following scenarios:

1) Library used UnitIndexerGUI (bribe)
2) Library used UnitDex
3) No UnitIndexer and no ExtendedFeatures

The only scenario that won't automatically setup properly is if you have extended features enabled but it didn't find any UnitIndexer. The reason for this is that Extended Features require data to be deallocated from units (i.e deindex events) this must then be handled manually by the user.

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
- Removed most vJass example use and instead created a GUI version
- 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)

Update fixed a few minor bugs
- Fixed a bug with hiding unit type names causing a crash.
- Hiding unit names (config) 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

I have an idea for including a third slot for unit kill count or rank display in the naming but I'm not sure if it fits in this library. Also, it probably would be possible to encode the different names inside the unit like so:

FirstName|r|rLastName|nUnit Name

Thus removing the need to store them inside a hashtabl, but I'm not sure if I have considered all the cases in which this can break. To retrieve the name one would simply loop through the string.
 
Last edited:
Top