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

[Snippet] IndexCarrier

Level 7
Joined
Apr 5, 2011
Messages
245
[Snippet] Indexer

Reworked:
JASS:
//===============
//=== Indexer ===
//=== v1.050 ====

/*************************************************************************
* Functions
*     function CreateIndexer takes integer n returns integer
*          - Creates an indexer meant for n instances and returns its id
*     function IndexCount takes integer CODE returns integer
*          - Returns count of indexes used by indexer
*           (In very deed this returns the largest index used currently)
*     function Indexed takes integer CODE, integer index returns boolean
*          - Is this index of an indexer currently occupied
*     function Index takes integer CODE returns integer
*          - Occupies free index of an indexer and returns it
*     function Deindex takes integer CODE, integer index returns boolean
*          - Frees an index of an indexer
*
* Array data
*     bool: ... [Indexer N created]        ------------>    [0 indexed]   [1 indexed]   ... [Indexer N+1 created]     ...
*     int:  ... [Indexer N index count] [Freeindex address] [Freeindex 0] [Freeindex 1] ... [Indexer N+1 index count] ...
*************************************************************************/

library Indexer
//=== Settings ===
    globals
        private constant integer SIZE = 256
//================
        private integer count = 0
        private boolean array b
        private integer array i
    endglobals
    
    function CreateIndexer takes integer n returns integer
        local integer temp = count
        set b[count] = true
        set i[count] = 0
        set i[count + 1] = 1
        set count = count + n
        return temp
    endfunction
    
    function IndexCount takes integer CODE returns integer
        return i[CODE]
    endfunction

    function Indexed takes integer CODE, integer index returns boolean
        return b[CODE + 1 + index]
    endfunction
    
    function Index takes integer CODE returns integer
        local integer j = CODE + 1
        local integer index
        if i[j] == 1 then
            set index = i[CODE]
            set i[CODE] = i[CODE] + 1
        else
            set index = i[i[j]]
            set i[j] = i[j] - 1
        endif
        set b[j + index] = true
        return index
    endfunction
    
    function Deindex takes integer CODE, integer index returns boolean
        local integer j = CODE + 1 + index
        if b[j] then
            set b[j] = false
            if i[CODE] == index + 1 then
                set i[CODE] = index
            else
                set CODE = CODE + 1
                set i[CODE] = i[CODE] + 1
                set i[i[CODE]] = index
            endif
            return true
        endif
        return false
    endfunction
endlibrary
Example of usage (old):
JASS:
//========================
//=== SpellPoisonSting ===
//======== v1.000 ========

library SpellPoisonSting requires Cast, Indexed, SpellTypePoison
//=== Settings ===
globals
    private constant integer ABILCODE = 'X204'
    private constant integer BUFFCODE = 'W012'
    private constant integer SLOW_ABILITY = 'X206'
    private constant string SLOW_ORDER = "slow"
//----------------
    private constant integer TICK_COUNT = 15
    private constant real TICK_TIME = 1
endglobals
    struct SpellPoisonSting extends array
        private static real array TICK_DAMAGE
        private static method onInit takes nothing returns nothing
            set TICK_DAMAGE[1] = 4
            set TICK_DAMAGE[2] = 6
            set TICK_DAMAGE[3] = 9
            set TICK_DAMAGE[4] = 13
//================
            set indexer = CreateIndexer()
        endmethod
        private static integer indexer
        private static unit array target
        private static unit array caster
        private static integer array level
        private static integer array tick_count
        private static timer array t
        
        private static method damage takes nothing returns nothing
            set Assist.workTimer = GetExpiredTimer()
            //! runtextmacro ObtainTimerIndex("t")
            //! runtextmacro DealPurePoisonDamage()
            if tick_count[Assist.workInteger] == 0 then
                //! runtextmacro EndPoison("indexer")
            endif
        endmethod

        static method onUnitTakesDamage takes nothing returns nothing
            local integer level
            if GetUnitAbilityLevel(Damage.target, BUFFCODE) != 0 then
                call UnitRemoveAbility(Damage.target, BUFFCODE)
                set level = GetUnitAbilityLevel(Damage.source, ABILCODE)
                call CastUnit(Damage.source, Damage.target, CASTER_DEFAULT_LIFETIME, SLOW_ABILITY, level, SLOW_ORDER)
                if level != 0 then
                    //! runtextmacro BreakPoison("indexer")
                    //! runtextmacro SetPoison("indexer")
                    set tick_count[Assist.workInteger] = TICK_COUNT
                    call TimerStart(t[Assist.workInteger], TICK_TIME, true, function thistype.damage)
                endif
            endif
        endmethod
    endstruct
endlibrary
SpellTypeIndexed:
JASS:
//===============
//=== Indexed ===
//=== v0.950 ====

library Indexed requires Indexer
    //Timers
    //! textmacro ObtainTimerIndex takes t
        set Assist.workInteger = 0
        loop
            exitwhen $t$[Assist.workInteger] == Assist.workTimer
            set Assist.workInteger = Assist.workInteger + 1
        endloop
    //! endtextmacro

    //Units
    //! textmacro ObtainUnitIndex takes u
        set Assist.workInteger = 0
        loop
            exitwhen $u$[Assist.workInteger] == Assist.workUnit
            set Assist.workInteger = Assist.workInteger + 1
        endloop
    //! endtextmacro
    
    //! textmacro SeekUnit takes indexer, u
        set Assist.workBoolean = false
        set Assist.workInteger = 0
        loop
            exitwhen Assist.workInteger == IndexCount($indexer$) or Assist.workBoolean
            if $u$[Assist.workInteger] == Assist.workUnit then
                set Assist.workBoolean = true
            endif
            set Assist.workInteger = Assist.workInteger + 1
        endloop
    //! endtextmacro
endlibrary
SpellTypePoison:
JASS:
//=======================
//=== SpellTypePoison ===
//======= v1.000 ========

library SpellTypePoison requires Assist, Indexer, vGDC
    //-=- General -=-
    //! textmacro BreakPoison takes indexer
        set Assist.workInteger = 0
        loop
            exitwhen Assist.workInteger == IndexCount($indexer$)
            if target[Assist.workInteger] == Damage.target then
                set tick_count[Assist.workInteger] = 1
                set Assist.workInteger = IndexCount($indexer$)
            else
                set Assist.workInteger = Assist.workInteger + 1
            endif
        endloop
    //! endtextmacro
    
    //! textmacro SetPoison takes indexer
        set Assist.workInteger = Index($indexer$)
        set target[Assist.workInteger] = Damage.target
        set caster[Assist.workInteger] = Damage.source
        set thistype.level[Assist.workInteger] = level
        if t[Assist.workInteger] == null then
            set t[Assist.workInteger] = CreateTimer()
        endif
    //! endtextmacro
    
    //! textmacro EndPoison takes indexer
        call Deindex($indexer$, Assist.workInteger)
        set target[Assist.workInteger] = null
        set caster[Assist.workInteger] = null
        call PauseTimer(t[Assist.workInteger])
    //! endtextmacro
    
    //-=- Pure poison -=-
    //! textmacro DealPurePoisonDamage
        set Assist.workReal = GetWidgetLife(target[Assist.workInteger])
        if Assist.workReal != 0 then
            if Assist.workReal < TICK_DAMAGE[level[Assist.workInteger]] then
                call DealDamage(caster[Assist.workInteger], target[Assist.workInteger], ULTIMATE_DAMAGE, PURE, false)
                set tick_count[Assist.workInteger] = 0
            else
                call SetWidgetLife(target[Assist.workInteger], Assist.workReal - TICK_DAMAGE[level[Assist.workInteger]])
                set tick_count[Assist.workInteger] = tick_count[Assist.workInteger] - 1
            endif
        endif
    //! endtextmacro
endlibrary
 
Last edited:
Level 7
Joined
Apr 5, 2011
Messages
245
Np, we can make it larger!
Though I have not tried to do it, I am using only 3 instances for now, and no one with unregister_func =D
(Seems like struct of arrays or struct arrays aren't much different in executive code)
^^ Because ... unregister function is useful only when indexed objects are global, otherwise have evidently excessive methods
(For example, global indexing of units / heroes / structures (because it sometimes seems useful to have separated global indexing) with attached data, though I still do not use this, just a possibility for someone maybe :])
 
Level 7
Joined
Apr 5, 2011
Messages
245
Basically you could use a struct to do this same thing. Scoping indices and abstracting arrays to use "." syntax is more important to me than using a central library just for the sake of unifying indices.
Let's suppose I remade this into 2D private boolean array and struct with static public methods and static readonly fields (to use .[] syntax and others)
This seems much more effective than having a lot of structures (~1 skill per hero = 100 indexes for kind of Dota map) with implemented indexing
Spells are encapsulated in libraries, so what "." syntax do you want to use if ~90% of struct members used inside the library? (And ~9% of the rest is not connected with indexing)
Seems like garbage in this case
(Though I am thinking too much in my own way probably)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Well, unifying allows any resource to have a relation to any resource, like passing a linked list between two resources.

There are a few problems with this in *wc3*

1: memory is limited (will run into performance issues trying to make a bigger array, a hashtable will actually be faster)
2: arrays of different types can't be easily related (once again, hashtable)
3. performance (will have to result in a hashtable, best performing implementation)

Now, while relating any resource to any other resource may be good, it's not very necessary

Also

when any two resources are related to each other, they typically use each other's structures specifically, not a generic structure. In cases where a generic structure is used, a specific implementation for the resource can usually be utilized (very rare when a generic global structure is better, sorting is one example)


The 1 pro does not outweigh the 3 cons. In SC2, you can actually do a global memory space, and it's recommended that you do a global memory space too. Why? You can make gigantic arrays, you can use functionrefs to perform generic operations on an unknown set, and you can link sets of different types up with each other.

If you want something like this, it's only really feasible in SC2.
 
Level 7
Joined
Apr 5, 2011
Messages
245
So, ~32 indexers (with 256 instances for each indexer)
The thing is I'm making my Dota map now, so ... There is no both general and optimal solution.
Normal Dota has 10 heroes (hero types) at once, average hero uses ~2 indexers. Considering items this is ~32 indexers used, there should not be "the issues" in such case, if I got it right.
Then, for optimization, there can be "many instances" and "less instances" indexers, first for things like Apply Poison, Initiate Stun, bla bla, second one - exact spells: "Impale", "Storm Bolt", etc. Most spells do not go over 8 instances.
Let's say, WTF mode can be set before first hero is picked => before skill are initiated. (I am gonna move init indexers from Init to HeroPicked) If WTF mode is not activated, optimization is used, otherwise all indexers have a lot of instances which can probably cause some little lags on my PC. :O
 
Level 7
Joined
Apr 5, 2011
Messages
245
Uh, it's easier to just use arrays than this...
And use more memory / write a lot of similar functions
This:
1. You do not need large array for 1 spell
(Correct me if I am wrong about this, I heard different information about wc3 arrays allocation from different people)
2. You do not need to write own recycler
3. You do not need to write own overlap
4. Extremely easy to use
99.9% spells can be written w/o any library with ease, but this is a lot of ugly code
 
Level 7
Joined
Apr 5, 2011
Messages
245
Lol, this is philosophy
I understood, this is not as efficient as @SCII, but ... just this does not mean that hashtable is more efficient, and your sentence "every good indexer -> hashtable" is not right definetely :/
(Just heard, hashtable is slow but arrays fast (not @SCII forums))
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Tell me how your thing is easier than this

http://www.hiveworkshop.com/forums/jass-resources-412/snippet-alloc-alternative-221493/

edit
If you wanna allocate arrays, then increase instanceCount by n, where n is the size of the array. If you wanna do different sized arrays, do multiple allocators.

It's a very simple and effective tool, and it follows the philosophy of spamming allocators since wc3 will allow you to allocate more than enough memory.
 
Level 7
Joined
Apr 5, 2011
Messages
245
^ That's good and simple, no doubt
(My thing is not easier definetely)

(Prepare to deal with some "inexperienced facts")
1. You create personal method for each instance while I use single ones
(I guess, this can even work faster, but again, I am a newbie in programming)
2. You create other structs dynamically and implement the code even if you don't need to use more than 1 instance of struct (no sense to create a lot of), while my indexers are absolutely identical in this case
3. Your code is struct-dependant while mine can be used anywhere
 
Spamming arrays is no problem.
Using one array has the potential to use up more memory because they are allocated logarithmically, so you could be using 260 indexes and have 512 allocated ones. With multiple arrays, you're less likely to allocate space for indexes you don't need because your indexes would be pretty low. No map is going to have a spell that gets cast more than 20 times at once. Seriously.
 
Level 7
Joined
Apr 5, 2011
Messages
245
Using one array has the potential to use up more memory because they are allocated logarithmically, so you could be using 260 indexes and have 512 allocated ones.
Thanks, this is useful information.
Using one array has the potential to use up more memory because they are allocated logarithmically, so you could be using 260 indexes and have 512 allocated ones. With multiple arrays, you're less likely to allocate space for indexes you don't need because your indexes would be pretty low. No map is going to have a spell that gets cast more than 20 times at once. Seriously.
Indexer can be reworked from generating linear CODE to CODE_TABLE.
For example, unit indexer uses 512 indexes, periodic effect indexer uses 256 indexes, Dota Nerubian Assassin Impale uses 4 indexes (2 for Nerubian Assassin and 2 for Rubick, both with refreshers), what can be easily configurable by setting game mode (WTF mode => another CODE_TABLE)

Edit:
In fact, my Impale does not use own indexing
 
Top