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

[vJASS] BJObjectId

Level 13
Joined
Nov 7, 2014
Messages
571
BJObjectId - makes working with the default WE object-id(s) easier

The "default WE object-id(s)" are those automatically assigned to objects after creation in the editor, i.e: e000, A001, I002, etc.

You've probably seen/written stuff like this:
JASS:
globals
    integer array my_items
    integer my_items_count
endglobals

...
// initialize the array
set my_items[1] = 'I000'
set my_items[2] = 'I001'
// ...
set my_items[10] = 'I009'
set my_items[11] = 'I00A'
set my_items_count = 11

...
// walk the array
local integer i = 1
local integer item_id
loop
    exitwhen i > my_items_count
    set item_id = my_items[i]
    // do stuff with item_id
    set i = i + 1
endloop

Which is a bit cumbersome when you have more than a dozen or so items, but we can simplify it:
JASS:
...
local BJObjectId item_id = BJObjectId('I000')
local BJObjectId last_item_id = BJObjectId('I00A')
loop
    exitwhen item_id > last_item_oid
    // do stuff with item_id
    set item_id = item_id.plus_1()
endloop
...

It seems to me that this scales better if you have to do it a lot in your map.

BJObjectId.j:
JASS:
library BJObjectId // 1.0

//! novjass

// creating:

    // from native object-id / integer
    set oid = BJObjectId('A000')

    // from string
    set oid = BJObjectId.from_str("I001")


// printing:

    call BJDebugMsg(oid.to_str())


// looping through a set of object-id(s)

    // forward
    local BJObjectId oid = BJObjectId('A000')
    local BJObjectId last_oid = BJObjectId('A010')
    loop
        exitwhen oid > last_oid
        // do stuff
        set oid = oid.plus_1()
    endloop

    // backward
    local BJObjectId oid = BJObjectId('A010')
    local BJObjectId last_oid = BJObjectId('A000')
    loop
        exitwhen oid < last_oid
        // do stuff
        set oid = oid.minus_1()
    endloop


// mapping BJObjectId(s) to array-indices / struct instances

    local integer index
    local MyStruct my_struct

    set index = BJObjectId('H000').to_unit_index()
    set my_struct = MyStruct( BJObjectId('h001').to_unit_index() )

    set index = BJObjectId('I000').to_item_index()
    set my_struct = BJObjectId('I001').to_item_index() // don't really need the MyStruct cast (because vJass =))

    set index = BJObjectId('B000').to_destructable_index()
    set my_struct = BJObjectId('B001').to_destructable_index()

    set index = BJObjectId('D000').to_doodad_index()
    set my_struct = BJObjectId('D001').to_doodad_index()

    set index = BJObjectId('A000').to_ability_index()
    set my_struct = BJObjectId('A001').to_ability_index()

    set index = BJObjectId('B000').to_buff_index()
    set my_struct = BJObjectId('B001').to_buff_index()

    set index = BJObjectId('R000').to_upgrade_index()
    set my_struct = BJObjectId('R001').to_upgrade_index()

    // for campaign objects the methods have a "c" after the "to_"
    set index = BJObjectId('A000').to_cunit_index()
    set index = BJObjectId('A000').to_cability_index()
    ...

// NOTE: the to_*_index methods break for object-id(s) > 'XXOZ', i.e
// 900 is the maximum number of objects you can have and still be able to use those methods;
// the reason is that the object-id(s) after 'XXOZ' have indices > 8190

//! endnovjass

struct BJObjectId extends array

static method from_str takes string oid returns thistype
    // '0' = 48 .. '9' = 57,
    // 'A' = 65 .. 'Z' = 90
    // 'a' = 97 .. 'z' = 122
    //
    // index(<chr>):
    // '0' = 0; chr(0 + 48) = '0' = 48
    // 'A' = 17; chr(17 + 48) = 'A' = 65
    // 'a' = 49; chr(49 + 48) = 'a' = 97
    //
    local string chars = "0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......abcdefghijklmnopqrstuvwxyz"
    local integer this = 0
    local integer i
    local integer j
    local integer ordinal
    local string chr
    local integer pow_256 = 1

    set i = 3
    loop
        exitwhen i < 0
        set chr = SubString(oid, i, i + 1)

        set j = 0
        loop
            exitwhen j >= 75

            if chr == SubString(chars, j, j + 1) then
                set this = this + (j + 48) * pow_256
                set pow_256 = pow_256 * 256
                exitwhen true
            endif

            set j = j + 1
        endloop

        set i = i - 1
    endloop

    return this
endmethod

method to_str takes nothing returns string
    local string chars = "0123456789.......ABCDEFGHIJKLMNOPQRSTUVWXYZ......abcdefghijklmnopqrstuvwxyz"
    local integer t = this
    local integer i
    local integer b
    local string result = ""

    set i = t
    set t = i / 0x100
    set b = i - t * 0x100 - 48
    set result = SubString(chars, b, b + 1) + result

    set i = t
    set t = i / 0x100
    set b = i - t * 0x100 - 48
    set result = SubString(chars, b, b + 1) + result

    set i = t
    set t = i / 0x100
    set b = i - t * 0x100 - 48
    set result = SubString(chars, b, b + 1) + result

    set t = t - 48
    set result = SubString(chars, t, t + 1) + result

    return result
endmethod

method plus_1 takes nothing returns thistype
    local integer t = this
    local integer i
    local integer b1
    local integer b2
    local integer b3
    local integer b4

    set i = t
    set t = i / 0x100
    set b4 = i - t * 0x100

    if b4 < 'Z' then
        if b4 != '9' then
            set i = i + 1
        else
            set i = i + 8
        endif
    else

        set i = t
        set t = i / 0x100
        set b3 = i - t * 0x100
        if b3 < 'Z' then
            if b3 != '9' then
                set i = i * 0x00000100 + 0x00000100 + '0'
            else
                set i = i * 0x00000100 + 0x00000800 + '0'
            endif
        else

            set i = t
            set t = i / 0x100
            set b2 = i - t * 0x100
            if b2 < 'Z' then
                if b2 != '9' then
                    set i = i * 0x00010000 + 0x00010000 + '0' * 0x00000100 + '0'
                else
                    set i = i * 0x00010000 + 0x00080000 + '0' * 0x00000100 + '0'
                endif
            else

                set i = t
                if i != '9' then
                    set i = i * 0x01000000 + 0x01000000 + '0' * 0x00010000 + '0' * 0x00000100 + '0'
                else
                    set i = i * 0x01000000 + 0x08000000 + '0' * 0x00010000 + '0' * 0x00000100 + '0'
                endif
            endif
        endif
    endif

    return i
endmethod

method minus_1 takes nothing returns thistype
    local integer t = this
    local integer i
    local integer b1
    local integer b2
    local integer b3
    local integer b4

    set i = t
    set t = i / 0x100
    set b4 = i - t * 0x100
    if b4 > '0' then
        if b4 != 'A' then
            set i = i - 1
        else
            set i = i - 8
        endif
    else

        set i = t
        set t = i / 0x100
        set b3 = i - t * 0x100
        if b3 > '0' then
            if b3 != 'A' then
                set i = i * 0x00000100 - 0x00000100 + 'Z'
            else
                set i = i * 0x00000100 - 0x00000800 + 'Z'
            endif
        else

            set i = t
            set t = i / 0x100
            set b2 = i - t * 0x100
            if b2 > '0' then
                if b2 != 'A' then
                    set i = i * 0x00010000 - 0x00010000 + 'Z' * 0x00000100 + 'Z'
                else
                    set i = i * 0x00010000 - 0x00080000 + 'Z' * 0x00000100 + 'Z'
                endif
            else

                set i = t
                if i != 'A' then
                    set i = i * 0x01000000 - 0x01000000 + 'Z' * 0x00010000 + 'Z' * 0x00000100 + 'Z'
                else
                    set i = i * 0x01000000 - 0x08000000 + 'Z' * 0x00010000 + 'Z' * 0x00000100 + 'Z'
                endif
            endif
        endif
    endif

    return i
endmethod

method operator< takes thistype other returns boolean
    return integer(this) < integer(other)
endmethod

private static integer array first_unit_oid
private static integer array first_cunit_oid
private static method onInit takes nothing returns nothing
    set first_unit_oid['H'] = 'H000'
    set first_unit_oid['h'] = 'h000'
    set first_unit_oid['O'] = 'O000'
    set first_unit_oid['o'] = 'o000'
    set first_unit_oid['E'] = 'E000'
    set first_unit_oid['e'] = 'e000'
    set first_unit_oid['U'] = 'U000'
    set first_unit_oid['u'] = 'u000'
    set first_unit_oid['N'] = 'N000'
    set first_unit_oid['n'] = 'n000'

    set first_cunit_oid['H'] = 'H600'
    set first_cunit_oid['h'] = 'h600'
    set first_cunit_oid['O'] = 'O600'
    set first_cunit_oid['o'] = 'o600'
    set first_cunit_oid['E'] = 'E600'
    set first_cunit_oid['e'] = 'e600'
    set first_cunit_oid['U'] = 'U600'
    set first_cunit_oid['u'] = 'u600'
    set first_cunit_oid['N'] = 'N600'
    set first_cunit_oid['n'] = 'n600'
endmethod

method to_unit_index takes nothing returns integer
    return this - first_unit_oid[this / 0x01000000] + 1
endmethod
method to_cunit_index takes nothing returns integer
    return this - first_cunit_oid[this / 0x01000000] + 1
endmethod

method to_item_index takes nothing returns integer
    return this - 'I000' + 1
endmethod
method to_citem_index takes nothing returns integer
    return this - 'I600' + 1
endmethod

method to_destructable_index takes nothing returns integer
    return this - 'B000' + 1
endmethod
method to_cdestructable_index takes nothing returns integer
    return this - 'B600' + 1
endmethod

method to_doodad_index takes nothing returns integer
    return this - 'D000' + 1
endmethod
method to_cdoodad_index takes nothing returns integer
    return this - 'D600' + 1
endmethod

method to_ability_index takes nothing returns integer
    return this - 'A000' + 1
endmethod
method to_cability_index takes nothing returns integer
    return this - 'A600' + 1
endmethod

method to_buff_index takes nothing returns integer
    return this - 'B000' + 1
endmethod
method to_cbuff_index takes nothing returns integer
    return this - 'B600' + 1
endmethod

method to_upgrade_index takes nothing returns integer
    return this - 'R000' + 1
endmethod
method to_cupgrade_index takes nothing returns integer
    return this - 'R600' + 1
endmethod

endstruct

endlibrary
 
I'm not sure if requires the _str functions. I don't see a real usecase where user has something like "I001" by default. If he really would need something in a special case, he might take care of it by himself imo.
Or can someone give a useful example maybe where it makes sense to use it over an id?

"BJObjectId", "BJ" means usually BlizzardJass, or? Why is it needed in name? Maybe just "ObjectId" , or + "Tools" ?

JASS:
plus_1()
...
minuts_1()
Are there plans for similar funtions with an other offset, or why the "_1"?
Maybe something like "next()" and "prev()" would be good? or "add()" , "substract()" ? If there shall be other offsets allowed in future, it might come just with a offset parameter maybe.
 
Level 13
Joined
Nov 7, 2014
Messages
571
I'm not sure if requires the _str functions.
The _string function should stay if you find it useful, but I just can not find a use case for myself.
Well... the use case that I've found for the from_str method is making a chat command that has an object-id argument (e.g: create-unit <unit-id> <x> <y>), the to_str method is just for completeness' sake.

"BJObjectId", "BJ" means usually BlizzardJass, or? Why is it needed in name? Maybe just "ObjectId" , or + "Tools" ?
But what's with the name and the other things from the post above?
With newgen one can assign arbitrary object-ids to custom objects but Blizzard used the character set [0 .. 9, A .. Z], so that's the reason for the "BJ" prefix.

Are there plans for similar funtions with an other offset
An offset different than 1? Eh... no? What would be the point?

why the "_1"?
Maybe something like "next()" and "prev()" would be good?

I guess next/inc/succ and prev/dec/pred are okay names but I wanted the names to "mirror" the way one writes a "normal" loop, i.e:
JASS:
set i = 0
loop
    exitwhen i > 9
    // stuff...
    set i = i + 1
endloop

// vs

set i = BJObjectId('A000')
loop
    exitwhen i > BJObjectId('A00Z')
    // stuff...
    set i = i.plus_1()
endloop

// i.e:
// set i = i + 1
// set i = i.plus_1()


// and


set i = 9
loop
    exitwhen i < 0
    // stuff...
    set i = i - 1
endloop

// vs

set i = BJObjectId('A00Z')
loop
    exitwhen i < BJObjectId('A000')
    // stuff...
    set i = i.minus_1()
endloop
 
Thanks for fast response and explaination. I'm happy.


Submission:
BJObjectId v1.0

Date:
16 October 16

Status:
Approved
Note:

Very useful library to work with ObjectIds (see raw code in object editor).
It provides an API to properly loop though a range of ObjectIds, and some other extra functionality like converting ObjectIds into struct-instanceable indices. Example usage: [Snippet] Resource Preloader. Approved.

I added a version number v1.0 just for reference reasons. Hope it's no problem.

An offset different than 1? Eh... no? What would be the point?
I don't really know. Maybe some crazy concept with + 'a' or so instead of "1". ;D I was just curious because of the "1" in the function name. But it's okay if you want to mimic loop structure like showed above.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
How will this cope with an object type specified as '@@@@'?! With exception of control characters which can cause a variety of breaks, it needs a full ASCII map.

In JNGP one can manually specify object types. This includes assigning them other ASCII characters such as '@', ',', '.' etc.
 
Level 13
Joined
Nov 7, 2014
Messages
571
New objects (units, abilities, items, etc.) created from existing objects in the Object Editor use object-ids with the "digits": '0' .. '9', 'A' .. 'Z'.
Only builtin objects like 'AHbz' (Archmage Blizzard ability) use object-ids with the "digits": 'a' .. 'z'.

I think its a good practice not to mess with builtin objects. It easy to make a copy and work with that, so that's why BJObjectId doesn't work for object-ids that use the "digits": 'a' .. 'z'.
 
Top