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

Libram v3.0.0.0

Libram:

This is a library that creates a "libram", something very similar to a spellbook. It has the same functionality, but was made for convenience for gameplay.

A libram is essentially a bunch of pages with abilities on them. Much like an extended inventory system, you can flip through the pages and access whatever abilities you need. There are only 5 slots, but you can always remove the page up and page down abilities (and opt for the items or the arrow key method) for 2 more slots per page.

Essentially, you can cast whatever spells you want, and if you need to access another ability, just turn the page. This can lead to cool spell organization, such as if you want one page for damage spells, one page for healing spells, and one page for tank spells. All cooldowns are preserved, passives work just fine (if you read the documentation and follow the instructions), and all that good stuff. You can also edit abilities just as you normally would, so it is completely safe to change the level of an ability in the libram without the use of custom functions.

I also have an optional module that can enable the abilities for page up and page down, or you can alternatively use arrow keys. (or both) With 2.0.0.0, you can also use items for swapping. (they are all optional, but I recommend using at least one of them as it is a pain to code it yourself)

Requirements: Any unit indexer, such as AIDS, UnitIndexer, AutoIndex, Bribe's GUI Indexer, etc..

This library also features nice Lua for easy generation. Just read the trigger, "How to Implement" on how to setup the system in your map, it is pretty easy.

Here is the code:
JASS:
library Libram requires optional LibramPageShift // ALSO REQUIRES ANY UNIT INDEXER
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
//
//      Libram                   v. 3 . 0 . 0 . 0
//      ¯¯¯¯¯¯                   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//
//          What is it?
//          ¯¯¯¯¯¯¯¯¯¯¯
//                  -  This library adds "pages" for abilities, which extends the limit of the amount of abilities
//                      a hero can have.
//                  -  It simulates a spellbook, but with a bit more convenience in that you still have access to
//                      standard abilities, such as "move" or "attack". 
//                  -  A libram acts as a "book" of one or more pages. Each page has several abilities.
//
//          Pros:
//          ¯¯¯¯
//                  -  It can support a very large amount of pages and abilities.
//                  -  It supports passive abilities.
//                  -  Cooldowns are maintained when switching between pages.
//                  -  You don't have to use special functions when handling with the abilities.
//
//          Cons:
//          ¯¯¯¯
//                  -  This currently supports only 11 PASSIVE abilities per unit, assuming you use the spellbook method.
//                  -  This does have a limit of pages and abilities, but you will most likely never reach it. (unless you have like 80 pages per unit)
//                  -  MUI: THIS IS MUI TO AN EXTENT. It will work perfectly fine with multiple units, but those units
//                  CANNOT share any abilities. For example, two heroes cannot both have the ability "Holy Light", or else
//                  it will bug out, even if only one of them have a libram activated.
//                  -  Autocast abilities won't be automatically casted if the libram is on a different page than the autocast.
//
//          Requirements:
//          ¯¯¯¯¯¯¯¯¯¯¯¯¯
//                - LibramPageShift : OPTIONAL
//                - Any unit indexer, AIDS, UnitIndexer, etc...
//
//          Usage:
//          ¯¯¯¯¯¯
//
/*              call Libram.create(unit) returns Libram                             */
//
//                  - This creates a new libram for a unit. It includes one empty, page. The instance returned is
//                      the user ID of the unit.
//
//                   Libram(instance).caster        - Returns the unit who has that libram.
//                   Libram(instance).first          - Returns the first page of the libram.
//                   Libram(instance).curr          - Returns the page that the player currently has open.
//
/*              call Libram(instance).createPage() returns LibramPage                     */
//  
//                  - This creates a new page for a unit, after the last page. 
//
/*              call Libram(instance).createPageAfter(Page previous) returns LibramPage  */
//
//                  - This creates a new page ordered after the page you specify.
//
/*              call Libram(instance).addAbility(Page, integer ID)                  */
//
//                  - This adds an ability to a page, of the "ID" provided. (rawcode) 
//
/*              call Libram(instance).addPassive(Page, integer ID, integer spellbookID) */
//
//                  - Adds a passive ability to a page. You must use this function to add
//                    it if you want it to work properly. However, you must create a dummy
//                    spellbook for it. You can use my Lua scripts to ease this process.
//                    If you don't want it to be applied when the page is not shown, then
//                    you can just add it as if it were a normal ability.  
//
/*              call Libram(instance).removeAbility(Page, integer ID)                  */
//
//                  - This removes an ability from a page, of the "ID" provided. (rawcode)
//
/*              call Libram(instance).shiftPages(Page toShiftTo)                       */
//          
//                  - This flips the page to the page you specify, unlocking its abilities.
//
/*              call Libram(instance).refresh()                                        */
//
//                  - If you had just setup passive abilities, all the abilities might not show up. Use this function
//                      to refresh the pages, just to make sure that everything displays correctly.
//
/*              call Libram(instance).removePage(Page)                              */
//
//                  - Removes the specified page and all the abilities associated with it.
//
/*              call Libram(instance).destroy()                                     */
//
//                  - Destroys the libram, removes all of its pages, and removes all of the abilities.
//
/*              call Page(instance).findAbility(integer ID) returns AbilityList         */
//
//                  - Finds an ability listed on a page, and returns its AbilityList instance. (return 0 = not found)
//    
//
//          Notes:
//          ¯¯¯¯¯¯
//                  - If you add an ability normally, without using this system, then it will automatically go in a 
//                  free slot. If there are no free slots, it will not be added. It will appear on each page, as long
//                  as there is a free slot.
//                  - If you remove an ability normally that has data on a page (without using this system), then
//                  it will successfully remove the ability, but the data for it will still exist, so be sure
//                  to remove it using this system.
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
    
    private struct AbilityList
        readonly thistype next
        readonly thistype prev
        readonly integer curr
        integer spellbookID 
        
        method destroy takes nothing returns nothing
            set this.next.prev      = this.prev
            set this.prev.next      = this.next
            call this.deallocate()
        endmethod
        
        static method attach takes integer abilID, thistype head returns thistype
            local thistype this  = thistype.allocate()
            set head.next.prev   = this        
            set this.next        = head.next    
            set head.next        = this        
            set this.prev        = head      
            set this.curr        = abilID
            return this
        endmethod
    endstruct 

    struct LibramPage 
        AbilityList list
        thistype next
        thistype prev
        
        method findAbility takes integer ID returns AbilityList
            local AbilityList i = this.list.next
            loop
                exitwhen i.curr == ID or i == 0
                set i = i.next
            endloop
            return i //it will return "0" if it is not found
        endmethod
        
        method destroy takes nothing returns nothing
            local AbilityList clear = this.list.next
            loop
                exitwhen clear == 0
                call clear.destroy()
                set clear = clear.next
            endloop
            call this.deallocate()
        endmethod
        
        method removeAbility takes integer ID returns nothing
            local AbilityList i = this.findAbility(ID)
            if i == 0 then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,15,"PAGE_ERROR["+I2S(this)+"]: Ability not listed on this page.")
                return
            endif
            call i.destroy()
        endmethod
        
        method addAbility takes integer ID returns AbilityList
            return AbilityList.attach(ID, this.list)
        endmethod
        
        static method insertAfter takes thistype node returns thistype
            local thistype this  = thistype.allocate()
            set this.list        = AbilityList.create()
            set node.next.prev   = this
            set this.next        = node.next
            set node.next        = this
            set this.prev        = node
            return this
        endmethod
    endstruct

    struct Libram extends array
        readonly unit caster
        readonly LibramPage first
        readonly LibramPage curr
        private player owner
        
        static method operator [] takes unit u returns thistype
            return GetUnitUserData(u)
        endmethod
        
        method shiftPages takes LibramPage which returns nothing
            local AbilityList i = this.curr.list.next
            local integer lvl 
            loop
                exitwhen i == 0
                if i.spellbookID != 0 then
                    set lvl = GetUnitAbilityLevel(this.caster,i.curr)
                    call UnitRemoveAbility(this.caster,i.curr)
                    call UnitAddAbility(this.caster,i.spellbookID)
                    call SetUnitAbilityLevel(this.caster,i.curr,lvl)
                else
                    call SetPlayerAbilityAvailable(this.owner,i.curr,false)
                endif
                set i = i.next
            endloop
            set this.curr = which
            set i = which.list.next
            loop
                exitwhen i == 0
                if i.spellbookID != 0 then
                    set lvl = GetUnitAbilityLevel(this.caster,i.curr)
                    call UnitRemoveAbility(this.caster,i.spellbookID)
                    call UnitAddAbility(this.caster,i.curr)
                    call SetUnitAbilityLevel(this.caster,i.curr,lvl)
                else
                    call SetPlayerAbilityAvailable(this.owner,i.curr,true)
                endif
                set i = i.next
            endloop
        endmethod
        
        method refresh takes nothing returns nothing
            call this.shiftPages(this.curr.next)
            call this.shiftPages(this.curr.prev)
        endmethod
        
        method createPage takes nothing returns LibramPage
            return LibramPage.insertAfter(this.first.prev)
        endmethod
        
        method createPageAfter takes LibramPage previous returns LibramPage
            return LibramPage.insertAfter(previous)
        endmethod
        
        method removeAbility takes LibramPage source, integer abilityID returns nothing
            local AbilityList i = source.findAbility(abilityID)
            local LibramPage k  = source.next
            local boolean rem   = true
            if i.spellbookID != 0 then
                call UnitRemoveAbility(this.caster,i.spellbookID)
            endif
            loop
                exitwhen k == source
                set rem = k.findAbility(abilityID)==0 //check if any other pages have this ability
                set k   = k.next
            endloop
            call source.removeAbility(abilityID)
            if rem then
                call UnitRemoveAbility(this.caster,abilityID)
                call SetPlayerAbilityAvailable(this.owner,abilityID,true)
            elseif this.curr == source then
                call this.refresh()
            endif
        endmethod
        
        method addPassive takes LibramPage target, integer abilityID, integer spellbookID returns nothing
            set target.addAbility(abilityID).spellbookID = spellbookID
            if this.curr != target then
                call UnitAddAbility(this.caster,spellbookID)
            else
                call UnitAddAbility(this.caster,abilityID)
            endif
            call SetPlayerAbilityAvailable(this.owner,spellbookID,false)
        endmethod
        
        method addAbility takes LibramPage target, integer abilityID returns nothing
            call target.addAbility(abilityID)
            call UnitAddAbility(this.caster,abilityID)
            if this.curr != target then
                call SetPlayerAbilityAvailable(this.owner,abilityID,false)
            endif
        endmethod
        
        method removePage takes LibramPage toRemove returns nothing
            local AbilityList i = toRemove.list.next
            loop
                exitwhen i == 0
                call this.removeAbility(toRemove,i.curr)
                set i = i.next
            endloop
            if this.curr == toRemove then
                call this.shiftPages(this.curr.next)
            endif
            call toRemove.destroy()
        endmethod
        
        method destroy takes nothing returns nothing
            local LibramPage p = this.first.next
            loop
                exitwhen p == this.first
                call this.removePage(p)
                set p = p.next
            endloop
            call this.removePage(p) //remove the head as well
            static if LIBRARY_LibramPageShift then
                static if LibramPageShift_ENABLE_ABILITY_SHIFT then 
                    call UnitRemoveAbility(this.caster,thistype.PAGE_UP)
                    call UnitRemoveAbility(this.caster,thistype.PAGE_DOWN)
                endif
                static if LibramPageShift_ENABLE_ITEM_SHIFT then
                    call RemoveItem(UnitItemInSlot(this.caster,thistype.PAGE_UP_SLOT))
                    call RemoveItem(UnitItemInSlot(this.caster,thistype.PAGE_DOWN_SLOT))
                endif
            endif
            set this.caster = null
        endmethod
        
        static method create takes unit cast returns thistype
            local thistype this    = GetUnitUserData(cast)
            set this.first         = LibramPage.create()
            set this.first.next    = this.first
            set this.first.prev    = this.first
            set this.first.list    = AbilityList.create()
            set this.curr          = this.first
            set this.caster        = cast
            set this.owner         = GetOwningPlayer(cast)
            static if LIBRARY_LibramPageShift then
                static if LibramPageShift_ENABLE_ABILITY_SHIFT then
                    call UnitAddAbility(cast,thistype.PAGE_UP)
                    call UnitAddAbility(cast,thistype.PAGE_DOWN)
                endif
                static if LibramPageShift_ENABLE_ITEM_SHIFT then
                    call UnitAddItemToSlotById(this.caster,thistype.PAGE_UP_ITEM,thistype.PAGE_UP_SLOT)
                    call UnitAddItemToSlotById(this.caster,thistype.PAGE_DOWN_ITEM,thistype.PAGE_DOWN_SLOT)
                endif
            endif
            return this
        endmethod
        
        implement optional LibramInit
        
    endstruct
    
    // Wrapper functions, you can remove this if you don't need it.
    
    // call AddLibram( unit u ) 
        //  Creates a libram for a unit and assigns lastCreatedLibram to it.
    // call AddLibramPage()
        //  Adds a page to the last created libram.
    // call AddLibramAbility( integer abilityID )
        //  Adds an ability to the last created page.
    // call AddLibramPassive( integer abilityID , integer spellbookID )
        //  Adds a passive ability to the last created page. (see documentation for information on passives)
    // The functions above are order dependent. The functions below are for general access:
    
    // call AddPageToUnit( unit u )
        //  Adds a new page to a unit.
    // call AddAbilityToPage( unit u, LibramPage page, integer abilityID )
        //  Adds an ability to a specific page of a unit.
    // call AddPassiveToPage( unit u, LibramPage page, integer abilityID, integer spellbookID )
        //  Adds a passive ability to a specific page of a unit. (see documentation for information on passives)
    // call GetUnitLibram( unit u ) returns Libram
        //  Returns the Libram instance of a unit.
    // call GetLibramPageFromIndex( Libram which, integer index ) returns LibramPage
        //  Returns the nth page of a libram. The first page of a libram has an index
        //  of 1. The next page has an index of 2. Third page has an index of 3... etc.
        
    // lastCreatedLibram - The last created libram from the wrapper functions.
    // lastCreatedPage - The last created page from the wrapper functions.
    
    globals
        Libram lastCreatedLibram   = 0
        LibramPage lastCreatedPage = 0
    endglobals
    
    function AddLibram takes unit owner returns nothing
        set lastCreatedLibram = Libram.create(owner)
        set lastCreatedPage   = lastCreatedLibram.first
    endfunction
    
    function AddLibramPage takes nothing returns nothing
        set lastCreatedPage = lastCreatedLibram.createPage()
    endfunction
    
    function AddLibramAbility takes integer abilityID returns nothing
        call lastCreatedLibram.addAbility(lastCreatedPage, abilityID)
    endfunction
    
    function AddLibramPassive takes integer abilityID, integer spellbookID returns nothing
        call lastCreatedLibram.addPassive(lastCreatedPage, abilityID, spellbookID)
    endfunction
    
    function AddPageToUnit takes unit u returns nothing
        set lastCreatedPage = Libram[u].createPage()
    endfunction
    
    function AddAbilityToPage takes unit u, LibramPage page, integer abilityID returns nothing
        call Libram[u].addAbility(page, abilityID)
    endfunction
    
    function AddPassiveToPage takes unit u, LibramPage page, integer abilityID, integer spellbookID returns nothing
        call Libram[u].addPassive(page, abilityID, spellbookID)
    endfunction
    
    function GetUnitLibram takes unit u returns Libram
        return Libram[u]
    endfunction
    
    function GetLibramPageFromIndex takes Libram which, integer index returns LibramPage
        local LibramPage i = which.first
        local integer c    = 1 
        loop
            if index == c then
                return i
            endif
            set i = i.next
            set c = c + 1
            exitwhen i == which.first
        endloop
        debug call DisplayTextToPlayer(GetLocalPlayer(),0,0,"GetLibramPageFromIndex: Could not retrieve the page of index "+I2S(index)+" of Libram("+I2S(which)+").")
        return 0
    endfunction
    
endlibrary

JASS:
library LibramPageShift
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
//
//      LibramPageShift                 
//      ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯                      
//
//          What is it?
//          ¯¯¯¯¯¯¯¯¯¯¯
//                  -  This library enables shifting with abilities, items, and arrow keys. (up and down)
//                  -  All of them can be disabled/enabled.
//
//          Usage:
//          ¯¯¯¯¯
//                  ENABLE_KEY_SHIFT     - Enables shifting pages with arrow keys.
//                  ENABLE_ABILITY_SHIFT - Enables shifting pages with abilities.
//                  ENABLE_ITEM_SHIFT    - Enables shifting pages with items.
//
//                  PAGE_UP              - The rawcode of the "Page Up" ability.
//                  PAGE_DOWN            - The rawcode of the "Page Down" ability.
//
//                  PAGE_UP_ITEM         - The rawcode of the "Page Up" item.
//                  PAGE_DOWN_ITEM       - The rawcode of the "Page Down" item.    
//
//                  PAGE_UP_BUFF         - In order for the abilities to not interrupt the current order,
//                                       they must be based off specific spells. Currently, it is using
//                                       Mana Shield and Berserk. If you change it to something else, then
//                                       you have to change the corresponding buff, so that the buff won't appear.
//                  PAGE_DOWN_BUFF       - Same as above.    
//
//          NOTE:
//          ¯¯¯¯
//              For this to work properly, your unit cannot have any abilities based off of Berserk or Mana Shield.
//          If you do, then you either need to change the ability or change the abilities in this system. Simply base 
//          the spell off of a different spell that does not interrupt order:
//              Berserk, Defend, Magical Defense, Mana Shield, Immolation, Divine Shield
//          And then change the PAGE_UP_BUFF and/or PAGE_DOWN_BUFF accordingly.                         
//
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

    globals
        public  constant boolean ENABLE_KEY_SHIFT     = true
        public  constant boolean ENABLE_ABILITY_SHIFT = true
        public  constant boolean ENABLE_ITEM_SHIFT    = true
    endglobals 

    module LibramInit
        static if ENABLE_KEY_SHIFT then
            private static group G = CreateGroup()
            static eventid ev
        endif
        
        static constant integer PAGE_UP_BUFF   = 'BNms' // Mana Shield
        static constant integer PAGE_DOWN_BUFF = 'Bbsk' // Berserk
        
        static if ENABLE_ABILITY_SHIFT then
            static constant integer PAGE_UP   = 'Apup'
            static constant integer PAGE_DOWN = 'Apdn'
        endif
        static if ENABLE_ITEM_SHIFT then
            static constant integer PAGE_UP_ITEM           = 'Ipup'
            static constant integer PAGE_DOWN_ITEM         = 'Ipdn'
            static constant integer PAGE_UP_SLOT           = 4
            static constant integer PAGE_DOWN_SLOT         = 5
        endif
        
        static if ENABLE_KEY_SHIFT then
            private static method filter takes nothing returns boolean
                local thistype this = Libram(GetUnitUserData(GetFilterUnit()))
                if this.caster != null then
                    if thistype.ev == EVENT_PLAYER_ARROW_UP_DOWN then
                        call this.shiftPages(this.curr.next)
                    elseif thistype.ev == EVENT_PLAYER_ARROW_DOWN_DOWN then
                        call this.shiftPages(this.curr.prev)
                    endif
                endif
                return false
            endmethod
        endif
        
        static timer        BuffRemoval = CreateTimer()
        static integer      RemoveBuffMax = 0
        static unit array   RemoveBuff
        
        static method RemoveBuffs takes nothing returns nothing
            loop
                exitwhen RemoveBuffMax == 0
                call UnitRemoveAbility(RemoveBuff[RemoveBuffMax], PAGE_UP_BUFF)
                call UnitRemoveAbility(RemoveBuff[RemoveBuffMax], PAGE_DOWN_BUFF)
                set RemoveBuffMax = RemoveBuffMax - 1
            endloop
        endmethod

        static method onShift takes nothing returns boolean
            local unit     cast = GetTriggerUnit()
            local thistype this = GetUnitUserData(cast)
            local integer  id   = GetSpellAbilityId()
            
            if cast == null then
                static if ENABLE_KEY_SHIFT then
                    set thistype.ev = GetTriggerEventId()
                    call GroupEnumUnitsSelected(thistype.G,GetTriggerPlayer(),Condition(function thistype.filter))
                    return false
                endif
            endif
            static if ENABLE_ABILITY_SHIFT then
                if id == thistype.PAGE_UP or id == thistype.PAGE_DOWN then
                    set RemoveBuffMax = RemoveBuffMax + 1
                    set RemoveBuff[RemoveBuffMax] = cast
                    call TimerStart(thistype.BuffRemoval,0,false,function thistype.RemoveBuffs)
                    if id == thistype.PAGE_UP then
                        if UnitRemoveAbility(cast,id) then
                            call UnitAddAbility(cast,id)
                        endif
                        call this.shiftPages(this.curr.next)
                    elseif id == thistype.PAGE_DOWN then
                        call this.shiftPages(this.curr.prev)
                    endif
                    set cast = null
                    return false
                endif
            endif
            static if ENABLE_ITEM_SHIFT then
                set id = GetItemTypeId(GetManipulatedItem())
                if id != 0 then
                    set RemoveBuffMax = RemoveBuffMax + 1
                    set RemoveBuff[RemoveBuffMax] = cast
                    call TimerStart(thistype.BuffRemoval,0,false,function thistype.RemoveBuffs)
                    if id == thistype.PAGE_UP_ITEM then
                        call this.shiftPages(this.curr.next)
                    elseif id == thistype.PAGE_DOWN_ITEM then
                        call this.shiftPages(this.curr.prev)
                    endif
                endif
            endif
            return false
        endmethod
    
        static method onInit takes nothing returns nothing
            local trigger t  = CreateTrigger()
            local integer i  = 11
            loop
                if GetPlayerSlotState(Player(i))==PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i))==MAP_CONTROL_USER then
                    static if ENABLE_ABILITY_SHIFT then
                        call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
                    endif
                    static if ENABLE_ITEM_SHIFT then
                        call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_USE_ITEM,null)
                    endif
                    static if ENABLE_KEY_SHIFT then
                        call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_UP_DOWN)
                        call TriggerRegisterPlayerEvent(t,Player(i),EVENT_PLAYER_ARROW_DOWN_DOWN)
                    endif
                endif
                exitwhen i == 0
                set i = i - 1
            endloop
            call TriggerAddCondition(t,Condition(function thistype.onShift))
        endmethod
    endmodule

endlibrary

abilsys04.png


It is decently light, as it is just a bunch of linked lists. The lists themselves will normally be short (unless you are insane/doing something weird), so it shouldn't be a problem in terms of speed.

Please report any bugs you find (aside from the fact that Illidan doesn't properly metamorphosis, lol). Sorry if I didn't explain it properly. The test map will explain it all, hopefully. Enjoy!

Also, thanks to Weep for confirming something for me in a thread.


3.0.0.0
  • It is now MUI, to an extent. See the documentation for details.
  • The code has been improved, and it now requires an indexer.
  • Lua has been added to instantly generate the needed objects for the Libram Page Shift add-on.
  • The spells and items no longer interrupt the unit's order.
  • Simple wrapper functions have been added, to make it easier for those who wish to use GUI or simpler JASS. (you still end up using mostly JASS)
2.0.0.0
  • Added support for ordering pages. (at least upon creation)
  • Added support for using items to shift pages.
  • No longer requires the "Alloc" library.
  • Improved the code and made more of the variables readonly.
  • Fixed a bug concerning one ability on multiple pages. If it were removed, it would affect the other pages as well, and they would not have it. But now I fixed that so that the data is still removed from the page, but the ability itself is not removed unless it is not on any other pages.
1.0.0.0
  • Initial release.


Keywords:
spell, ability, system, book, abilities, libram, vjass
Contents

Testing Map (Map)

Reviews
19th Oct 2011 Bribe: Approved - a highly recommended alternative to spellbooks. I recommend making "findAbility" hashtable-based instead of search-based, and also possibly implementing RegisterPlayerUnitEvent to the PageShiftModule.

Moderator

M

Moderator

19th Oct 2011
Bribe: Approved - a highly recommended alternative to spellbooks.

I recommend making "findAbility" hashtable-based instead of search-based, and also possibly implementing RegisterPlayerUnitEvent to the PageShiftModule.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
  • Hashing using the Table library would eliminate these O(1) searches that you do.
  • I like the idea!
  • Since you're using static-if's, this block should be broken up with static if's:

    JASS:
                if GetSpellAbilityId()== thistype.PAGE_UP or GetTriggerEventId()==EVENT_PLAYER_ARROW_UP_DOWN then
                    call this.shiftPages(true)
                elseif GetSpellAbilityId() == thistype.PAGE_DOWN or GetTriggerEventId()==EVENT_PLAYER_ARROW_DOWN_DOWN then
                    call this.shiftPages(false)
                endif
  • Static if's can be used to avoid creating the two constant integers in the event the user does not want that option enabled.
  • "Page" is quite generic for the struct name, "LibramPage" would be more exclusive.
  • I have mixed feelings about the "Alloc" resource, I still have to do more thinking but it unfortunately seems lacking, I just have to think of what it is that it's lacking.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
With the first of the unnofficial JassHelper updates I'm planning, onDestroy will be a declarable method in array structs (don't need the silly capitalization) and normal structs will only have the safety checks when in debug mode. If this JassHelper update gets adopted, Alloc would no longer provide any speed advantage.

I plan to do regular unnoffical JassHelper updates as Vexorian seems to have retired from the WarCraft III business. Probably look for the first revision from me next month.
 
  • Hashing using the Table library would eliminate these O(1) searches that you do.

Yeah, I thought the same thing. I'll update it soon.

EDIT: I might not be able to eliminate the searches because I can't link back abil ID's to pages (due to the fact that one ability may have more than one page associated with it)

  • Since you're using static-if's, this block should be broken up with static if's:

    JASS:
                if GetSpellAbilityId()== thistype.PAGE_UP or GetTriggerEventId()==EVENT_PLAYER_ARROW_UP_DOWN then
                    call this.shiftPages(true)
                elseif GetSpellAbilityId() == thistype.PAGE_DOWN or GetTriggerEventId()==EVENT_PLAYER_ARROW_DOWN_DOWN then
                    call this.shiftPages(false)
                endif
  • Static if's can be used to avoid creating the two constant integers in the event the user does not want that option enabled.

Will do.

  • "Page" is quite generic for the struct name, "LibramPage" would be more exclusive.

Yeah, LibramPage sounds better. I will probably update it for that.

  • I have mixed feelings about the "Alloc" resource, I still have to do more thinking but it unfortunately seems lacking, I just have to think of what it is that it's lacking.

Okay, I will update it for the standard alloc method. This doesn't need polymorphism, but I'll inline it anyway.

even though idk any vJass this is awesome!! 5/5

Thank you, hopefully I can make it GUI friendly soon! :D

-------------

Thank you guys for the comments & feedback! It is all greatly appreciated. :D

EDIT: Updated to 2.0.0.0.
 
Last edited:
I guess it's time to give the world the advanced List library because it would still allow you to do O(1) with any integer association.

It will improve the technique, but the lists generally don't get longer than 6-12 abilities, so the searches aren't that bad. ;D However, I never benchmarked it or anything.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
It's more to do with the philosophy of not having to do a search (able to index everything together).

List basically fixes this problem:
  • Struct A is full of instances. Each instance has a pointer to struct B.
  • A lot of struct A's instances have multiple-appearances of the same instance in struct B, so if struct B wants to reference struct A is has to be able to store more than one instance of struct A for each instance of struct B

List allows struct B to store any number of instances of struct A. List is based on Table which also means it can allocate an essentially unlimited number of times.
 
Yeah, it is possible, and I should make it that way. I've just been a little busy lately and haven't been able to update my system. If I don't have the time to make the updates by next Wednesday, you can feel free to GY it. (for the time being) Until then, you can expect an update. :)

Note: To those looking to use this system, it doesn't bug at the moment, but it's instance-ability can be improved a bit.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
This is a really nice system! ^_^

The only thing I would want is for Page Up and Page Down abilities to not interrupt channeling spells, like if you used the arrow keys.
Like if they were based off Berserk or Wind Walk. Unfortunately, that would limit the hero from using abilities based off them... :\
 
I'd like to make a GUI version of this, which will be MUI, someone requested it and I
think it's a good idea. Would you be OK with it? You would get due credits obviously.

Of course! Feel free to. :)

can you add an option to disable passive if it isn't on the page? i'm not saying it's good idea or not, but might be desired.

I think if you want to do that, you can just add the ability normally (as if it is a active ability, instead of doing all the passive ability setup stuff). But I can always make a function for that or something, I'll look into it.

So this remembers the cooldowns by using player ability availability? That's a neat trick.

Yep. ;D Because if you remove it and re-add it, then it resets the cooldown, so I think this is the only way.

Thanks for the comments guys, I am sorry I haven't been able to update this so soon, I just have been crammed with work.
 
Updated.

3.0.0.0

  • It is now MUI, to an extent. See the documentation for details.
  • The code has been improved, and it now requires an indexer.
  • Lua has been added to instantly generate the needed objects for the Libram Page Shift add-on.
  • The spells and items no longer interrupt the unit's order.
  • Simple wrapper functions have been added, to make it easier for those who wish to use GUI or simpler JASS. (you still end up using mostly JASS)
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
i tryed add libram things but failed, please somebody can help to implentate? just make workable (i added the triggers but cant enable them coz allways have error, idk why, i am damn noob with jass things :D) then i can add the skills to system coz that part i understand, i added already the abilities/triggers what needed for system (exclude passives coz i think that need only for test)

http://shadowvzs.uw.hu/13new.w3x
 
Make sure you have downloaded Jass NewGen Pack v5d; if you have it shouldn't bring up errors. Take a look at the example trigger for some help.

I can try to help as well, just send me a PM with the libram you want in your map, how many pages, and which abilities on each page. (or just send me enough information on how you want your libram to be setup) I'll try to help if I get the time.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Make sure you have downloaded Jass NewGen Pack v5d; if you have it shouldn't bring up errors. Take a look at the example trigger for some help.

i tryed add and when i try save jass helper gived error or warn, main problem after few minute my editor going mad (even if i run normal we) i can open my map and if i click to any trigger category then i get error (with something registry number or idk what) and editor is closed automatically, somebody got time & can help implentate the libram ability system? (i send the map if somebody can, i stucked here, coz until i cant resolve the ability things i am stucked on my map)
 
Level 1
Joined
May 22, 2012
Messages
2
Hey guys. Sorry, I'm not literate in JASS at all.

I can open the provided test map and test it. However, once I save it, I get an error message regarding the trigger Libram (Missing requirement: optional (libraries cannot require scopes)) and I cannot play it any more.

I'm using jassnewgenpack5d and the latest wc3 client. I tried running the thing on 1.21 version of the game, the same thing happens. It's probably something silly, but still - any help appreciated.
 
Level 25
Joined
Feb 2, 2006
Messages
1,667
Does this work in Reforged with more than level 1 hero abilities? The example heroes have all abilities only level 1.
So this does not work with skilling them? How can I specify the ability levels? I might store the slot 1-5 levels and apply them to every page.
 
Top