• 🏆 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!

Object Data Generator v0.1

This system will generate long lists of key map objects.
It will generate 4 seperate lists:
-Items
-Abilities
-Heroes
-Buffs

This can be pretty Useful for Save/Load systems that depend on lists like this :)

To reduce lag, I'm using a periodic lister :)
It takes 1 second to generate a list of 1000 objects.

Here's the code:
JASS:
//*********************************************************************
//
//                              Object
//                        Database Generator
//                               v0.5
//                          Magtheridon96
//
//*********************************************************************
//
//      This is a system that generates lists of the items, buffs, 
//      abilities and heroes in a map.
//
//      It will create a database for the names of the objects and 
//      another parallel database for the raw codes.
//
//      Example:
//
//      In a map:
//          There are 5 items ('I000' 'I002' 'I%$$' 'I700' and 'I009')
//
//          This system will create something like this:
//
//          iData[0] = 'I000'     ;    iObj[0] = Ring
//          iData[1] = 'I002'     ;    iObj[1] = Brace
//          iData[2] = 'I009'     ;    iObj[2] = Belt
//          iData[3] = 'I700'     ;    iObj[3] = Sword
//          iData[4] = 'I%$$'     ;    iObj[4] = Mace
//
//      This system can only be useful for maps with HUGE amounts of 
//      object data (Thousands)
//
//      WC3 only supports up to 16777216 items, abilities, buffs, etc...
//
//      This system loops through ALL of them ONLY ONCE to generate the 
//      database. I'm sorry for this inconvinience, but that's the only
//      way it could be done (especially since most people command the 
//      object merger to create objects with raw codes like 'A@@#' or 
//      'A!!!'...
//
//      This system depends on the NUMBER of objects you have.
//      It will NOT affect performance if you have objects like:
//
//      'A009' ; 'A0FF' ; 'A052'
//
//      But if you have objects like:
//
//      'A000' ; 'AXXF' ; 'A##2'
//
//      It will LAG LIKE HELL!
//
//      It's all map dependent. If you don't have huge gaps between raw codes, 
//      this may be pretty useful. Lists like these are essential especially 
//      for Save/Load systems :]
//
//      Benchmarks:
//
//      3000 objects => 3 seconds ; >57 FPS
//      30000 objects => 16 seconds ; >47 FPS
//      90000 objects => 90 seconds ; <5 FPS
//
//      That's actually pretty good :)
//      Most maps barely have 1000 objects!
//
//*********************************************************************
//
//                               Changelog
//
//      *** v0.1 ***
//      -Released
//
//      *** v0.2 ***
//      -Made it faster
//
//      *** v0.3 ***
//      -Fixed Timer Leaks
//      -Made it much more efficient
//      -Shortened variable names
//      -Removed the pesky underscores :P
//      -This system is now much safer and doesn't require a dummy unit
//
//      *** v0.4 ***
//      -Very minor fix
//      -Improved readability
//
//      *** v0.5 ***
//      -Fixed a bug (Thanks Troll-Brain)
//      -New Functions used to get the indices given the name.
//
//*********************************************************************
library DatabaseGen initializer Init
    globals
        // configurable:
        
        // Number of Nightelf based heroes
        private constant integer NE     = 15
        // Number of Human based heroes
        private constant integer NH     = 3
        // Number of Orc based heroes
        private constant integer NO     = 7
        // Number of Undead based heroes
        private constant integer NU     = 4
        // Number of Neutral based heroes
        private constant integer NN     = 2
        // Number of items
        private constant integer NI        = 234
        // Number of abilities
        private constant integer NA        = 287
        // Number of Buffs
        private constant integer NB        = 323
        
        // end configurables
        
        // Base Ability
        private constant integer BA  = 'A000'
        // Base Buff
        private constant integer BB  = 'B000'
        // Base Item
        private constant integer BI  = 'I000'
        // Base Hero
        private integer BH = 'E000'
        
        // Indices
        private integer i = 0
        private integer j = 0
        
        private constant player P = Player(12)
        
        // Data Storage
        integer array iData    // item data
        integer array aData    // ability data
        integer array bData    // buff data
        integer array hData    // hero data
        
        string array iObj      // item name data
        string array aObj      // ability name data
        string array bObj      // buff name data
        string array hObj      // hero name data
    endglobals
    
    private function GenB takes nothing returns nothing
        local string s = GetObjectName(BB + i)

        if s != "" and s != null then
            set bData[j] = BB + i
            set bObj[j] = s
            set j = j + 1
        endif

        set i = i + 1

        if j > NB then
            call DestroyTimer(GetExpiredTimer())
            set P = null
        endif

        set s = ""
    endfunction
    
    private function GenH takes nothing returns nothing
        local unit u

        if j > NE then
            set BH = 'H000'
            set j = 0
        elseif j > NH then
            set BH = 'N000'
            set j = 0
        elseif j > NN then
            set BH = 'O000'
            set j = 0
        elseif j > NO then
            set BH = 'U000'
            set j = 0
        elseif j > NU then
            set i = 0
            set j = 0
            call TimerStart(GetExpiredTimer(),0.001,true,function GenB)
            return
        endif

        set u = CreateUnit(P,BH + i,0,0,0)

        if u != null then
            set hData[j] = BH + i
            set hObj[j] = GetObjectName(BH + i)
            set j = j + 1
            call RemoveUnit(u)
        endif

        set i = i + 1

        set u = null
    endfunction
    
    private function GenA takes nothing returns nothing
        local string s = GetObjectName(BA + i)

        if s != "" and s != null then
            set aData[j] = BA + i
            set aObj[j] = s
            set j = j + 1
        endif

        if j > NA then
            set i = 0
            set j = 0
            call TimerStart(GetExpiredTimer(),0.001,true,function GenH)
            return
        endif

        set i = i + 1

        set s = ""
    endfunction
    
    private function GenI takes nothing returns nothing
        local item h = CreateItem(BI + i,0,0)

        if h != null then
            set iData[j] = BI + i
            set iObj[j] = GetObjectName(BI + i)
            set j = j + 1
            call RemoveItem(h)
        endif

        if j > NI then
            set i = 0
            set j = 0
            call TimerStart(GetExpiredTimer(),0.001,true,function GenA)
            return
        endif

        set i = i + 1

        set h=null
    endfunction
    
    private function Init takes nothing returns nothing
        call TimerStart(CreateTimer(),0.001,true,function GenI)
    endfunction

    // These functions are used to retrieve the index 
    // of the raw codes given the name of the object.
    //! textmacro getIndex takes name, char, char2
    function Get$name$Index takes string s returns integer
        local integer g = 0
        loop
            exitwhen g>N$char2$
            if $char$Obj[g]==s then
                return g
            endif
            set g = g + 1
        endloop
        return -1
    endfunction
    //! endtextmacro

    //! runtextmacro getIndex("Ability","a","A")
    //! runtextmacro getIndex("Item","i","I")
    //! runtextmacro getIndex("Buff","b","B")
    //! runtextmacro getIndex("Hero","h","H")

endlibrary

Give credits if used :)
 
Last edited:
Level 10
Joined
Jul 12, 2009
Messages
318
Firstly, this seems to depend on the user inputting how many of each type of object they have in the map. That's not very convenient, or safe; if the user puts in a number larger than the actual amount (or forgets to decrease it after deleting an object), it looks like this has the potential to run forever. Also, won't this erroneously count default abilities if the rawcode gap overlaps them?

It'd be nice if this also stored objects in a hashtable keyed to the StringHash of their name (it would probably have to be an indexed list because names are not unique), so I could do something like GetAbilityIdByName("Omnislash", 2) which would return the rawcode of the second instance of an ability named "Omnislash".

JASS:
function GenerateBuffs takes nothing returns nothing
        if UnitAddAbility(DummyU, BASE_BUFF + I) then
            set aData_[J] = BASE_BUFF + I
            set aObj_[J] = GetObjectName(BASE_BUFF + I)
            set J = J + 1
            call UnitRemoveAbility(DummyU, BASE_BUFF + I)
        endif
Are you sure this works? When I try if UnitAddAbility(GetTriggerUnit(), 'BHbn' /* Banish */) then ; call BJDebugMsg("test") ; endif, it does not display anything, so I have my doubts.

JASS:
    function GenerateAbilities takes nothing returns nothing
        if UnitAddAbility(DummyU, BASE_ABIL + I) then
This isn't safe; certain abilities will crash the game if you try to add them to a unit (eg. Hero, Aatk, nonexistant rawcodes seemingly at random, or anything based on Engineering Upgrade when added to a non-hero such as this dummy...)

Instead, I might suggest using if GetObjectName() != null, "", or "Default String", and if a user has not given an object a name, that's their fault. :crazz:
 
I'll fix that :p

update

I fixed everything ^^

edit

Does anyone know what's the lowest possible timer interval?
This system needs to be faster..
Also, here's a tip: Most abilities that have raw codes like this: 'A!#!'
are used only for systems as dummy abilities, so you don't need to count them
when setting the globals (Unless you're doing something really wierd lol)
I'd say you'd probably need to go up to about 'A300' (If you have about 100 heroes :p)
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Time to be the nasty one, i guess.

Sorry but what's the point of this ? (at least in the current state)
You save rawcodes unit/item/abilities ... in a list, and then ?

I mean you argue this is useful when you have plenty of custom rawcodes, but actually you have to do a loop to retrieve the correct one, pretty lame if the loop is hudge.
You could use an (hash)table to make it basically O(1), but still what's the point ?

On the code by itself, you will store in aData each rawcodes, no matter what is it (unit, ability,upgrade,item ...)

Also, instead of making always the same loop, you could just use one with elseifs, because of the waste of unvalid rawcode checks that would be a major improvement.

Also i suppose there is an error in your function GenB, you would use bData and bObj instead of aData and aObj, but that's pretty the same function of GenA anyway.

Plus there is no way that you can "index" each rawcode fairly instant in map initalization, so it's kinda pointless to store and use data.

In fact only a prepocessor for this sort of thing could be really useful, especially because it can extract all wanted data from the object editor at map initialization without any problems.

Oh and nothing really revelant to your code but GetObjectName returns a localized string which depends the wc3 language (unless you have changed the default name of the rawcode)

And plz don't follow Nestharus with this ugly short-variables-names convention, it's so unreadeable, and if you care about speed you still can widgetize it (it's not so broken really, even with TriggerRegisterVariableEvent and ExecuteFunc, but i'm agree you have to know what you are doing, and if you care that much, someone could still make a super light version of wc3mapoptimizer without a GUI)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Nah, only portions that are unreadable are these-
JASS:
        private constant integer BA  = 'A000'
        private constant integer BB  = 'B000'
        private constant integer BI  = 'I000'
        
        private integer BH = 'E000'
        
        private integer I = 0
        private integer J = 0

        // Data Storage
        integer array iData
        integer array aData
        integer array bData
        integer array hData
        
        string array iObj
        string array aObj
        string array bObj
        string array hObj

Comments please =). The rest I got immediately ; p

troll, NU is number of undead. NH is number of humans. etc... it's pretty easy =)

But mag, all caps traditionally means constants, so please go lowercase or sumtin =P, or nU, however u want to do it, but not all caps =). In fact, variables shouldn't start with a capital anyways >.>.
 
@Troll-Brain

This can be VERY useful.
I'm using a huge list of raw codes in my map for some AI i'm working on.
Lists of raw codes like this can be used in Save/Load systems so that you don't
have to encode the actual raw codes, but the indices of the raw codes in the lists.

Some maps have way too many raw codes to set at Map Initialization.
Imagine having to create a function with about 2000 lines just to preset
raw codes when you can use this to create a huge list of them in 2 seconds.
(It can't be instant.. i've tried that, but the thread crashed and terminated.)

I fixed that GenB bug (Thanks :])
And I'll add some comments for readability / understandability :p

@Nestharus
BH = BaseHero
BI = BaseItem
..

I and J are just indices


edit

update
I added some new functions so that you can retrieve the indices of the objects
by giving the name.

In an AI system, you could register objects like this: (Not on MapInit ofcourse)
Maybe at about 4 seconds of game-time. It depends on how many objects you have.
JASS:
    call RegisterItemAI(hero1,GetItemIndex("Ring"))
    call RegisterItemAI(hero1,GetItemIndex("Helm"))
    call RegisterItemAI(hero1,GetItemIndex("Sword"))

    call RegisterAbilityAI(hero1,GetAbilityIndex("Firebolt"))
    call RegisterAbilityAI(hero1,GetAbilityIndex("Mana Drain"))

That way, Trigger Editor work involving raw codes is much easier to manage.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
Comments please =). The rest I got immediately ; p

troll, NU is number of undead. NH is number of humans. etc... it's pretty easy =)

But mag, all caps traditionally means constants, so please go lowercase or sumtin =P, or nU, however u want to do it, but not all caps =). In fact, variables shouldn't start with a capital anyways >.>.

Or it could also be naked user and nasty hobbit, well ofc in this short code that's not to much a burden, but it's still a really bad practice.

Don't take me wrong i sometimes use short names like i/j/k for integers (mostly in loops), n for a stack indice, s for string, u for unit ...
But they are pretty common.

About the comments i myself only use really few or none (it's bad i know), but try to give explicit names of variables/functions/whatever ...
And i use comments when it's likely i could take sometimes if i read the code months later (could be hard to determine what part of the code matchs this case)

About the naming convention, i'm agree only constants SHOULD_BE_SPELLED_LIKE_THAT, though i myself begins global variables with an uppercase, to make easily the difference with a local, but instead i would use the prefix "_" if i could (but you can say that's ugly i won't deny it, in fact i rarely see jassers using this convention so i'm probably the black sheep here, i'm just personnaly fine with this convention).

Magtheridon96 said:
This can be VERY useful.
I'm using a huge list of raw codes in my map for some AI i'm working on.
Lists of raw codes like this can be used in Save/Load systems so that you don't
have to encode the actual raw codes, but the indices of the raw codes in the lists.

Ok, i suppose it's bad to allow the user to use any ability without having to edit the map script, but then again you would have the direct access of the ability index, just like that (strings are lame in jass, i avoid them as many times i can):

local integer i = AbilityData['Aloc']

A simple (hash)table does the work.

Some maps have way too many raw codes to set at Map Initialization.
Imagine having to create a function with about 2000 lines just to preset
raw codes when you can use this to create a huge list of them in 2 seconds.
(It can't be instant.. i've tried that, but the thread crashed and terminated.)

I know it can't be instant, but you can greatly reduce the needed time though, inside your timer callback you can make heavily loops, just make sure you didn't reach limit op with empiric tests and by displaying a debug message at the end of the timer callback (you have to consider the worst case : all rawcodes checked in the loop are valids)

But there is still no match against a prepocessor (much harder to develop i admit). So this way is probably fine if you don't use exotics rawcodes like 'Lù$@' but follow the defaults one given by the object editor when you create a new object.

I fixed that GenB bug (Thanks :])

Not really, you didn't understood what i said, now you have two clones pointless functions.
As i said in aData you will have ANY VALID RAWCODE (item,unit,ability,destructable ...), just because GetObjectName obviously returns a string for ANY OBJECT of the object editor.
If you don't believe me display the strings of aObj.

And again you don't really need several functions, you can make it all check in one with elseif, to make a major improvement (invalid rawcodes are checked only one time instead of 4)
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You're welcome, just to be clear i don't want to bitch you, just telling the truth so you can improve your skills.

Oh and for this short-variable-name thingy, you do realize that if you use private and public the names are no longer short, right ?
 
Top