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