//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
LibramAbilities | abilcode | Yes | |
LibramDummyAbilities | abilcode | Yes | |
LibramPassives | abilcode | Yes | |
LibramUnit | unit | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AbilityPreload
//===========================================================================
// Information:
//==============
//
// Preloading removes the noticeable delay the first time an ability
// is loaded in a game. If an ability was not already on a pre-placed unit
// or a unit that was created during initialization, preloading is needed
// to prevent a delay.
//
//===========================================================================
// AbilityPreload API:
//=====================
//
// AbilityPreload(abilityid) :
// Call this before any time has elapsed to preload a specific
// ability. If debug mode is enabled, you will see an error message
// if you call this after initialization, or if you try to preload
// an ability that does not exist. Will inline to a UnitAddAbility
// call if debug mode is disabled.
//
// AbilityRangePreload(start, end) :
// Same as AbilityPreload, but preloads a range of abilities.
// It will iterates between the two rawcode values and preload
// every ability along the way. It will not show an error message
// for non-existent abilities.
//
//===========================================================================
// Configuration:
//================
globals
private constant integer PreloadUnitRawcode = 'zsmc'
//This is the rawcode for "Sammy!". It is never used and has no model,
//which makes an ideal preloading unit. Change it if you want to.
endglobals
//===========================================================================
globals
private unit PreloadUnit
endglobals
function AbilityPreload takes integer abilityid returns nothing
static if DEBUG_MODE then
if GetUnitTypeId(PreloadUnit) == 0 then
call BJDebugMsg("AbilityPreload error: Can't preload an ability after initialization")
return
endif
endif
call UnitAddAbility(PreloadUnit, abilityid)
static if DEBUG_MODE then
if GetUnitAbilityLevel(PreloadUnit, abilityid) == 0 then
call BJDebugMsg("AbilityPreload error: Attempted to preload a non-existent ability")
endif
endif
endfunction
function AbilityRangePreload takes integer start, integer end returns nothing
local integer i = 1
static if DEBUG_MODE then
if GetUnitTypeId(PreloadUnit) == 0 then
call BJDebugMsg("AbilityPreload error: Can't preload an ability after initialization")
return
endif
endif
if start > end then
set i = -1
endif
loop
exitwhen start > end
call UnitAddAbility(PreloadUnit, start)
set start = start + i
endloop
endfunction
//===========================================================================
private struct Init extends array
private static method onInit takes nothing returns nothing
set PreloadUnit = CreateUnit(Player(15), PreloadUnitRawcode, 0., 0., 0.)
call UnitApplyTimedLife(PreloadUnit, 0, .001)
call ShowUnit(PreloadUnit, false)
call UnitAddAbility(PreloadUnit, 'Aloc')
endmethod
endstruct
endlibrary
//TESH.scrollpos=53
//TESH.alwaysfold=0
scope Test initializer Init
globals
unit hero
Libram A
LibramPage array Pages
endglobals
private function esc takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,12,"Use the \"Page Up\" and \"Page Down\" abilities/items to shift pages.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,12,"You can also use the arrow keys to shift pages. (up and down arrows)")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,12,"Press |cff00ff00ESC|r to refresh health, mana, cooldowns, and to display this text again.")
call SetWidgetLife(hero,GetUnitState(hero,UNIT_STATE_MAX_LIFE))
call SetUnitState(hero,UNIT_STATE_LIFE,GetUnitState(hero,UNIT_STATE_MAX_LIFE))
call UnitResetCooldown(hero)
endfunction
private function PreloadAbils takes nothing returns nothing
// this is just for preloading the abilities, it is not necessary
// however, I do recommend it in general
call AbilityPreload('AHtb')
call AbilityPreload('AHtc')
call AbilityPreload('AHbh')
call AbilityPreload('AHav')
call AbilityPreload('AHfs')
call AbilityPreload('AHbn')
call AbilityPreload('AHdr')
call AbilityPreload('AHpx')
call AbilityPreload('AHhb')
call AbilityPreload('AHds')
call AbilityPreload('AHad')
call AbilityPreload('AHre')
call AbilityPreload('AHbz')
call AbilityPreload('AHwe')
call AbilityPreload('AHab')
call AbilityPreload('AHmt')
endfunction
private function Init2 takes nothing returns nothing
set A = Libram.create(hero) // creates the libram for the mountain king
set Pages[0] = A.first
set Pages[1] = A.createPage() //creates a page at the end of the list
set Pages[2] = A.createPage()
set Pages[3] = A.createPage()
//first page
call A.addAbility(Pages[0],'AHtb') // thunderbolt
call A.addAbility(Pages[0],'AHtc') // thunderclap
call A.addPassive(Pages[0],'AHbh','Ab02') // passive - bash
call A.addAbility(Pages[0],'AHav') // avatar
//second page
call A.addAbility(Pages[1],'AHfs') // flame strike
call A.addAbility(Pages[1],'AHbn') // banish
call A.addAbility(Pages[1],'AHdr') // siphon mana
call A.addAbility(Pages[1],'AHpx') // phoenix
//third page
call A.addAbility(Pages[2],'AHhb') // holy light
call A.addAbility(Pages[2],'AHds') // divine shield
call A.addPassive(Pages[2],'AHad','Ab03') // passive - devotion aura
call A.addAbility(Pages[2],'AHre') // resurrection
//fourth page
call A.addAbility(Pages[3],'AHbz') // blizzard
call A.addAbility(Pages[3],'AHwe') // water elemental
call A.addPassive(Pages[3],'AHab','Ab01') // passive - brilliance aura
call A.addAbility(Pages[3],'AHmt') // mass teleport
call A.refresh() //refresh, just in case so that all of the abilities will work correctly
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
set hero = gg_unit_Hmkg_0001
set udg_LibramUnit = gg_unit_Eevi_0022
call PreloadAbils()
call SetHeroLevel(hero,10,false)
if GetLocalPlayer()==Player(0) then
call SetCameraTargetController(hero,0,0,false)
endif
call TriggerRegisterPlayerEvent(t,Player(0),EVENT_PLAYER_END_CINEMATIC)
call TriggerRegisterPlayerChatEvent(t,Player(0),"",false)
call TriggerAddAction(t,function esc)
call TriggerExecute(t)
call Init2()
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
This will guide you on how to implement the Libram system for your map. Before your start the guide,
be sure that you have JASS NewGen Pack updated with the latest JassHelper.
Implementation:
1 - First, copy the folder "System", and paste it in your map.
2 - If you are using the AIDS Unit Indexer (the one used in this map), then go to the trigger "AIDS".
Look at the documentation and follow the instructions in the "How to implement?" part.
3 - Go to the trigger "LibramGen" and then enable it. Follow the instructions in the comments.
4 - After that, you are done. Enjoy!
How to add Passive Abilities:
1 - If you understand Lua, or if you think you can, go to the trigger named "Passives" and check it out. There
are some samples that make the process become really easy.
2 - Otherwise, copy one of the dummy spellbooks in the object editor in the map, and then paste it in your map.
Then change the name and "Data - Spell List" to whatever passive you want it to have. Then you are good to go.
3 - To add a passive spell to a libram, you use the following function:
call Libram[unit].addPassive(LibramPage PageToAddTo, integer PassiveID, integer SpellbookID)
PageToAddTo - This is the page you want to add the passive to.
PassiveID - This is the rawcode of the passive you want to add.
SpellbookID - This is the rawcode of the dummy spellbook you made in either step [1] or step [2].
call AddLibramPassive(integer PassiveID, integer SpellbookID)
If you are using the wrapper functions, like the one above, you can use this function to add it
to the last created libram onto the last created page. However, note that if you use the wrapper
functions, you MUST use only the wrapper functions for creations of pages/librams, otherwise it will
not track the necessary information.
//TESH.scrollpos=15
//TESH.alwaysfold=0
//
// _ ___ ___ ___ _______________________________________________
// /_\ |_ _| \/ __| || A D V A N C E D I N D E X I N G ||
// / _ \ | || |) \__ \ || A N D ||
// /_/ \_\___|___/|___/ || D A T A S T O R A G E ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.1.0
// What is AIDS?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// AIDS assigns unique integers between 1 and 8191 to units which enter
// the map. These can be used for arrays and data attaching.
//
// AIDS also allows you to define structs which are created automatically
// when units enter the map, and filtering which units should be indexed
// as well as for which units these structs should be created.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called AIDS, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// Save the map, close it, reopen it, and then delete the "!" from the
// FAR left side of the next lines (so "external" will line up with this line):
//! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
// At the top of the script, there is a 'UnitIndexingFilter' constant
// function. If the function returns true for the unit, then that unit
// will be automatically indexed. Setting this to true will automatically
// index all units. Setting it to false will disable automatic indexing.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function GetUnitId takes unit u returns integer
// - This returns the index of an indexed unit. This will return 0
// if the unit has not been indexed.
// - This function inlines. It does not check if the unit needs an
// index. This function is for the speed freaks.
// - Always use this if 'UnitIndexingFilter' simply returns true.
//
// function GetUnitIndex takes unit u returns integer
// - This will return the index of a unit if it has one, or assign
// an index if the unit doesn't have one (and return the new index).
// - Use this if 'UnitIndexingFilter' doesn't return true.
//
// function GetIndexUnit takes integer index returns unit
// - This returns the unit which has been assigned the 'index'.
//
// AIDS Structs:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Insert: //! runtextmacro AIDS() at the top of a struct to make it
// an AIDS struct.
// - AIDS structs cannot be created or destroyed manually. Instead, they
// are automatically created when an appropriate unit enters the map.
// - You cannot give members default values in their declaration.
// (eg: private integer i=5 is not allowed)
// - You cannot use array members.
// - AIDS structs must "extend array". This will remove some unused
// functions and enforce the above so there will be no mistakes.
// - There are four optional methods you can use in AIDS structs:
// - AIDS_onCreate takes nothing returns nothing
// - This is called when the struct is 'created' for the unit.
// - In here you can assign members their default values, which
// you would usually assign in their declarations.
// (eg: set this.i=5)
// - AIDS_onDestroy takes nothing returns nothing
// - This is called when the struct is 'destroyed' for the unit.
// - This is your substitute to the normal onDestroy method.
// - AIDS_filter takes unit u returns boolean
// - This is similar to the constant filter in the main system.
// - Each unit that enters the map will be tested by each AIDS
// struct filter. If it returns true for that unit, that unit
// will be indexed if it was not already, the AIDS struct will
// have its AIDS_onCreate method called, and later have its
// AIDS_onDestroy method called when the index is recycled.
// - Not declaring this will use the default AIDS filter instead.
// - AIDS_onInit takes nothing returns nothing
// - This is because I stole your onInit function with my textmacro.
// - You can use '.unit' from any AIDS struct to get the unit for which
// the struct is for.
// - The structs id will be the units index, so getting the struct for
// a unit inlines to a single native call, and you can typecast between
// different AIDS structs. This is the premise of AIDS.
// - Never create or destroy AIDS structs directly.
// - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
// decrease the lock level on the struct. If a struct's lock level is
// not 0, it will not be destroyed until it is reduced to 0. Locks just
// put off AIDS struct destruction in case you wish to attach to a timer
// or something which must expire before the struct data disappears.
// Hence, not freeing all locks will leak the struct (and index).
//
// PUI and AutoIndex:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - AIDS includes the PUI textmacros and the AutoIndex module, because
// these systems are not compatible with AIDS but have valid and distinct
// uses.
// - The PUI textmacros are better to use for spells than AIDS structs,
// because they are not created for all units, just those targetted by
// the spell (or whatever else is necessary).
// - The AutoData module is good for very simple array syntax for data
// attachment (although I don't recommend that people actually use it,
// it's here mostly for compatability). Note that unlike the PUI textmacros,
// units must pass the AIDS filter in order for this module to work with
// them. This is exactly as the same as in AutoIndex itself (AutoIndex
// has a filter too).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for writing 90% of this user documentation, challenging my
// interface, doing some testing, suggesting improvements and inspiring
// me to re-do my code to include GetUnitIndex as non-inlining.
// - grim001, for writing the AutoData module, and AutoIndex. I used the
// on-enter-map method that he used. Full credits for the AutoData module.
// - Cohadar, for writing his PUI textmacros. Full credits to him for these,
// except for my slight optimisations for this system.
// Also, I have used an optimised version of his PeriodicRecycler from
// PUI in this system to avoid needing a RemoveUnitEx function.
// - Vexorian, for helping Cohadar on the PUI textmacro.
// - Larcenist, for suggesting the AIDS acronym. Originally he suggested
// 'Alternative Index Detection System', but obviously I came up with
// something better. In fact, I'd say it looks like the acronym was
// an accident. Kinda neat, don't you think? :P
//
// Final Notes:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - With most systems, I wouldn't usually add substitutes for alternative
// systems. However, UnitData systems are an exception, because they
// are incompatible with eachother. Since using this system forbids
// people from using the real PUI or AutoIndex, and a lot of resources
// use either of these, it made sense not to break them all.
//
// - If this documentation confused you as to how to use the system, just
// leave everything as default and use GetUnitId everywhere.
//
// - To use this like PUI (if you don't like spamming indices) simply
// make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
//==============================================================================
// Configurables
//
globals
private constant boolean USE_PERIODIC_RECYCLER = false
private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
// Lower to be able to recycle faster.
// Only used if USE_PERIODIC_RECYCLER
// is set to true.
private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
endglobals
private function UnitIndexingFilter takes unit u returns boolean
return true
endfunction
//==============================================================================
// System code
//
globals
// The unit stored at an index.
private unit array IndexUnit
private integer array LockLevel
endglobals
//==============================================================================
globals
// Recycle stack
private integer array RecycledIndex
private integer MaxRecycledIndex = 0
// Previous highest index
private integer MaxIndex = 0
endglobals
//==============================================================================
globals
private integer array DecayingIndex
private integer MaxDecayingIndex=0
private integer DecayChecker=0
endglobals
globals
private timer UndefendTimer=CreateTimer()
private integer array UndefendIndex
private integer UndefendStackIndex=0
endglobals
globals
private integer array UndefendExpiringIndex
private integer UndefendExpiringIndexLevel=0
endglobals
//==============================================================================
globals
// The Add/Remove stack (or assign/recycle stack).
//
// Indexing can become recusive since units can be created on index
// assignment or deallocation.
// To support this, a stack is used to store the event response results.
private integer ARStackLevel=0
private integer array ARStackIndex
private unit array ARStackUnit
// A later discovery revealed that the Add/Remove stack did not need to be
// used for deallocation. The alternative used works fine...
endglobals
public constant function GetEnteringIndexUnit takes nothing returns unit
return ARStackUnit[ARStackLevel]
endfunction
public function GetIndexOfEnteringUnit takes nothing returns integer
// Called in AIDS structs when units do not pass the initial AIDS filter.
if ARStackIndex[ARStackLevel]==0 then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
endif
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
// Called in AIDS structs when units have passed the initial AIDS filter.
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetDecayingIndex takes nothing returns integer
static if USE_PERIODIC_RECYCLER then
return DecayingIndex[DecayChecker]
else
return UndefendExpiringIndex[UndefendExpiringIndexLevel]
endif
endfunction
//==============================================================================
globals
// For structs and such which need to do things on unit index assignment.
private trigger OnEnter=CreateTrigger()
// The same, but for when units pass the initial filter anyway.
private trigger OnEnterAllocated=CreateTrigger()
// For structs and such which need to do things on unit index deallocation.
private trigger OnDeallocate=CreateTrigger()
endglobals
public function RegisterOnEnter takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnter,b)
endfunction
public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnterAllocated,b)
endfunction
public function RegisterOnDeallocate takes boolexpr b returns triggercondition
return TriggerAddCondition(OnDeallocate,b)
endfunction
//==============================================================================
function GetIndexUnit takes integer index returns unit
debug if index==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
debug elseif IndexUnit[index]==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
debug endif
return IndexUnit[index]
endfunction
function GetUnitId takes unit u returns integer
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
debug elseif GetUnitUserData(u)==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
debug endif
return GetUnitUserData(u)
endfunction
globals//locals
private integer getindex
endglobals
function GetUnitIndex takes unit u returns integer // Cannot be recursive.
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
debug endif
set getindex=GetUnitUserData(u)
if getindex==0 then
// Get new index, from recycler first, else new.
// Store the current index in getindex.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set getindex=MaxIndex
else // Get from recycle stack.
set getindex=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(u,getindex)
set IndexUnit[getindex]=u
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=getindex
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Do not fire things here. No AIDS structs will be made at this point.
endif
return getindex
endfunction
//==============================================================================
public function AddLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]+1
endfunction
public function RemoveLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]-1
static if not USE_PERIODIC_RECYCLER then
if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
// Increment stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Decrement stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=index
// Null the unit.
set IndexUnit[index]=null
endif
endif
endfunction
//==============================================================================
static if USE_PERIODIC_RECYCLER then
private function PeriodicRecycler takes nothing returns nothing
if MaxDecayingIndex>0 then
set DecayChecker=DecayChecker+1
if DecayChecker>MaxDecayingIndex then
set DecayChecker=1
endif
if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
if LockLevel[DecayingIndex[DecayChecker]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
// Null the unit.
set IndexUnit[DecayingIndex[DecayChecker]]=null
// Remove index from decay list.
set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
set MaxDecayingIndex=MaxDecayingIndex-1
endif
endif
endif
endfunction
else
private function UndefendFilter takes nothing returns boolean
return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
endfunction
private function OnUndefendTimer takes nothing returns nothing
loop
exitwhen UndefendStackIndex==0
set UndefendStackIndex=UndefendStackIndex-1
set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
if IndexUnit[UndefendExpiringIndex[0]]!=null then
if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
if LockLevel[UndefendExpiringIndex[0]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
// Null the unit.
set IndexUnit[UndefendExpiringIndex[0]]=null
endif
endif
endif
endloop
endfunction
globals//locals
private integer UndefendFilterIndex
endglobals
private function OnUndefend takes nothing returns boolean
if GetIssuedOrderId()==852056 then // If undefend then...
set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
set UndefendStackIndex=UndefendStackIndex+1
call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
endif
endif
return false
endfunction
endif
//==============================================================================
public function IndexEnum takes nothing returns boolean // Can be recursive...
// Start by adding another level on the AR stack (for recursion's sake).
set ARStackLevel=ARStackLevel+1
// Store the current unit on the (new) top level of the AR stack.
set ARStackUnit[ARStackLevel]=GetFilterUnit()
if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Fire things.
call TriggerEvaluate(OnEnter)
else
// The unit did not pass the filters, so does not need to be auto indexed.
// However, for certain AIDS structs, it may still require indexing.
// These structs may index the unit on their creation.
// We flag that an index must be assigned by setting the current index to 0.
set ARStackIndex[ARStackLevel]=0
// Fire things.
call TriggerEvaluate(OnEnter)
endif
endif
// Decrement the stack.
set ARStackLevel=ARStackLevel-1
return false
endfunction
//==============================================================================
private function InitAIDS takes nothing returns nothing
local region r=CreateRegion()
local group g=CreateGroup()
local integer n=15
static if USE_PERIODIC_RECYCLER then
call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
else
local trigger t=CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
// Capture "undefend" orders.
exitwhen n==0
set n=n-1
endloop
set n=15
call TriggerAddCondition(t,Filter(function OnUndefend))
set t=null
endif
// This must be done first, due to recursion. :)
call RegionAddRect(r,GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
set r=null
loop
call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
exitwhen n==0
set n=n-1
endloop
call DestroyGroup(g)
set g=null
endfunction
//==============================================================================
public struct DEFAULT extends array
method AIDS_onCreate takes nothing returns nothing
endmethod
method AIDS_onDestroy takes nothing returns nothing
endmethod
static method AIDS_filter takes unit u returns boolean
return UnitIndexingFilter(u)
endmethod
static method AIDS_onInit takes nothing returns nothing
endmethod
endstruct
//===========================================================================
// Never create or destroy AIDS structs directly.
// Also, do not initialise members except by using the AIDS_onCreate method.
//===========================================================================
//! textmacro AIDS
// This magic line makes default methods get called which do nothing
// if the methods are otherwise undefined.
private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
//-----------------------------------------------------------------------
// Gotta know whether or not to destroy on deallocation...
private boolean AIDS_instanciated
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator unit takes nothing returns unit
// Allows structVar.unit to return the unit.
return GetIndexUnit(this)
endmethod
//-----------------------------------------------------------------------
method AIDS_addLock takes nothing returns nothing
call AIDS_AddLock(this)
endmethod
method AIDS_removeLock takes nothing returns nothing
call AIDS_RemoveLock(this)
endmethod
//-----------------------------------------------------------------------
private static method AIDS_onEnter takes nothing returns boolean
// At this point, the unit might not have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation.
set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onEnterAllocated takes nothing returns boolean
// At this point, the unit must have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation. Slightly faster!
set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onDeallocate takes nothing returns boolean
if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
// Unflag destruction on deallocation.
set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
endif
return false
endmethod
//-----------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
// Because I robbed you of your struct's onInit method.
call thistype.AIDS_onInit()
endmethod
//! endtextmacro
endlibrary
library PUI uses AIDS
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
// Switched the next two lines for optimisation.
if .pui_unit[pui] != whichUnit then
if .pui_data[pui] != 0 then
// recycled index detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
endlibrary
library AutoIndex uses AIDS
module AutoData
private static thistype array data
// Fixed up the below to use thsitype instead of integer.
static method operator []= takes unit u, thistype i returns nothing
set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
endmethod //using the module's thistype array.
static method operator [] takes unit u returns thistype
return .data[GetUnitId(u)] //Just returning the attached struct.
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
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
//TESH.scrollpos=380
//TESH.alwaysfold=0
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
//
// LibramGen
//
// This trigger generates all the abilities and items necessary for this system to work, using the appropriate
// values. If you wish to change any of the rawcodes or icons or anything when generating, do so here.
//
// GENERATION: To generate the objects, ENABLE this trigger.
//
// After you have ENABLED the trigger, SAVE the map, and then CLOSE it. REOPEN the map, and then DISABLE this
// trigger. To disable a trigger, right click the trigger's name, and then make sure that "Enable Trigger" has
// no checkmark next to it.
//
// If everything was done correctly, the objects should now appear in your map's object editor.
//
// Also, if you wish to change any of the objects manually in the object editor, be sure that this trigger
// is disabled so that it won't overwrite your changes.
//
//~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i local PAGE_UP_CODE = "Apup"
//! i local PAGE_DOWN_CODE = "Apdn"
//! i local PAGE_UP_IABIL_CODE = "Aipu"
//! i local PAGE_DOWN_IABIL_CODE = "Aipd"
//! i local PAGE_UP_ITEM_CODE = "Ipup"
//! i local PAGE_DOWN_ITEM_CODE = "Ipdn"
//! i setobjecttype("abilities")
//! i createobject("ANms", PAGE_UP_CODE)
//! i makechange(current, "alev", "1")
//! i makechange(current, "abpx", "1")
//! i makechange(current, "abpy", "1")
//! i makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedUp.blp")
//! i makechange(current, "arar", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedUp.blp")
//! i makechange(current, "auar", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedUp.blp")
//! i makechange(current, "Nms2", "1", "0")
//! i makechange(current, "Nms1", "1", "0")
//! i makechange(current, "aefs", "None")
//! i makechange(current, "abuf", "1", "")
//! i makechange(current, "aran", "1", "0")
//! i makechange(current, "acdn", "1", "0")
//! i makechange(current, "amcs", "1", "0")
//! i makechange(current, "ahky", "R")
//! i makechange(current, "anam", "Page Up")
//! i makechange(current, "atp1", "1", "|cffffcc00[R]|r Page Up")
//! i makechange(current, "aub1", "1", "Accesses the next page of your abilities.")
//! i makechange(current, "aret", " ")
//! i makechange(current, "arut", " ")
//! i makechange(current, "aut1", "1", " ")
//! i createobject("Absk", PAGE_DOWN_CODE)
//! i makechange(current, "aher", "1")
//! i makechange(current, "alev", "1")
//! i makechange(current, "abpx", "2")
//! i makechange(current, "abpy", "1")
//! i makechange(current, "aart", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedDown.blp")
//! i makechange(current, "bsk2", "1", "0")
//! i makechange(current, "bsk3", "1", "0")
//! i makechange(current, "bsk1", "1", "0")
//! i makechange(current, "abuf", "1", "")
//! i makechange(current, "acdn", "1", "0")
//! i makechange(current, "amcs", "1", "0")
//! i makechange(current, "ahdu", "1", "0")
//! i makechange(current, "adur", "1", "0")
//! i makechange(current, "arac", "Creeps")
//! i makechange(current, "anam", "Page Down")
//! i makechange(current, "ahky", "G")
//! i makechange(current, "atp1", "1", "|cffffcc00[G]|r Page Down")
//! i makechange(current, "aub1", "1", "Accesses the previous page of your abilities.")
//! i createobject(PAGE_UP_CODE, PAGE_UP_IABIL_CODE)
//! i makechange(current, "aite", "1")
//! i makechange(current, "ansf", "[Item]")
//! i createobject(PAGE_DOWN_CODE, PAGE_DOWN_IABIL_CODE)
//! i makechange(current, "aite", "1")
//! i makechange(current, "ansf", "[Item]")
//! i setobjecttype("items")
//! i createobject("desc", PAGE_UP_ITEM_CODE)
//! i makechange(current, "iabi", PAGE_UP_IABIL_CODE)
//! i makechange(current, "ubpy", "2")
//! i makechange(current, "iico", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedUp.blp")
//! i makechange(current, "idro", "0")
//! i makechange(current, "isel", "0")
//! i makechange(current, "ipaw", "0")
//! i makechange(current, "icla", "Miscellaneous")
//! i makechange(current, "icid", PAGE_UP_IABIL_CODE)
//! i makechange(current, "igol", "0")
//! i makechange(current, "iprn", "0")
//! i makechange(current, "ides", "Accesses the next page of your abilities.")
//! i makechange(current, "uhot", "R")
//! i makechange(current, "unam", "Page Up")
//! i makechange(current, "utip", "Purchase |cffffcc00[R]|r Page Up")
//! i makechange(current, "utub", "Accesses the next page of your abilities.")
//! i createobject(PAGE_UP_ITEM_CODE, PAGE_DOWN_ITEM_CODE)
//! i makechange(current, "iabi", PAGE_DOWN_IABIL_CODE)
//! i makechange(current, "ubpx", "1")
//! i makechange(current, "iico", "ReplaceableTextures\\CommandButtons\\BTNReplay-SpeedDown.blp")
//! i makechange(current, "icid", PAGE_DOWN_IABIL_CODE)
//! i makechange(current, "ides", "Accesses the previous page of your abilities.")
//! i makechange(current, "uhot", "G")
//! i makechange(current, "unam", "Page Down")
//! i makechange(current, "utip", "Purchase |cffffcc00[G]|r Page Down")
//! i makechange(current, "utub", "Accesses the previous page of your abilities.")
//! endexternalblock
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Writes map header to map (run once, but multiple times won't hurt)
//------------------------------------------------------------------------
//function initmap()
//comment after initialization
/*
//! externalblock extension=lua FileExporter $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i initmap()
//! endexternalblock
*/
//uncomment after initialization
//! import "luajass.Libram.j"
//------------------------------------------------------------------------
//import and run lua script to current script (all do same thing)
//------------------------
//function dofile(name)
//function require(name)
//function loadfile(name)
//lua scripts are shared across all maps
//jass scripts are local to a map
//returns code inside of file
//------------------------
//function readlua(name)
//function readjass(name)
//writes code to file
//------------------------
//function writelua(name, code)
//function writejass(name, code)
//deletes file
//------------------------
//function deletelua(name)
//function deletejass(name)
//! textmacro LUA_FILE_HEADER
//! i do
//replace "FILE_NAME" with the name of the map
//must be valid directory name
//! i local FILENAME = "Libram"
//! i function getfilename()
//! i return FILENAME
//! i end
//Initialization
///////////////////////////////////////////////////////////////////////
//! i local PATH_LUA_p = "grimext\\luadir"
//! i local PATH_JASS_p = PATH_LUA_p .. "\\" .. FILENAME .. "_dir"
//! i local PATH_LUA = PATH_LUA_p .. "\\"
//! i local PATH_JASS = PATH_JASS_p .. "\\"
//! i local JASS_HUB = "jass\\luajass." .. FILENAME .. ".j"
//! i function initmap()
//! i os.execute("if not exist " .. PATH_LUA .. " (mkdir " .. PATH_LUA .. ")")
//! i os.execute("if not exist " .. PATH_JASS .. " (mkdir " .. PATH_JASS .. ")")
//! i local file = io.open(JASS_HUB, "r")
//! i if (file == nil) then
//! i file = io.open(JASS_HUB, "w")
//! i file:write("")
//! i file:close()
//! i else
//! i file:close()
//! i end
//! i os.execute("if not exist grimext\\luadir\\" .. FILENAME .. "_dir (mkdir grimext\\luadir\\" .. FILENAME .. "_dir)")
//! i end
///////////////////////////////////////////////////////////////////////
//! i local olddofile = dofile
//! i local oldrequire = require
//! i local oldloadfile = loadfile
//! i function dofile(name)
//! i oldrequire("luadir\\" .. name)
//! i end
//! i function require(name)
//! i dofile(name)
//! i end
//! i function loadfile(name)
//! i dofile(name)
//! i end
//! i local function getluapath(name)
//! i return (PATH_LUA .. name .. ".lua")
//! i end
//! i local function getjasspath(name)
//! i return (PATH_JASS .. name .. ".luajass.j")
//! i end
//! i local function getjassimport(name)
//! i return ("\/\/! import \"..\\" .. getjasspath(name) .. "\"")
//! i end
//! i local function del(name)
//! i os.remove(name)
//! i end
//! i local function read(path)
//! i local file = io.open(path, "r")
//! i code = nil
//! i if (file ~= nil) then
//! i code = file:read("*all")
//! i file:close()
//! i end
//! i return code
//! i end
//! i local function write(path, code)
//! i file = io.open(path, "w")
//! i file:write(code)
//! i file:close()
//! i end
//! i local function import(name)
//! i local code = read(JASS_HUB)
//! i local line = getjassimport(name) .. "\n"
//! i local s,k = code:find(line)
//! i if (s == nil) then
//! i write(JASS_HUB, code .. line)
//! i end
//! i end
//! i function readlua(name)
//! i return read(getluapath(name))
//! i end
//! i function writelua(name, code)
//! i write(getluapath(name), code)
//! i end
//! i function readjass(name)
//! i return read(getjasspath(name))
//! i end
//! i function writejass(name, code)
//! i write(getjasspath(name), code)
//! i import(name)
//! i end
//! i function deletelua(name)
//! i del(getluapath(name))
//! i end
//! i function deletejass(name)
//! i del(getjasspath(name))
//! i local line = getjassimport(name) .. "\n"
//! i local code = read(JASS_HUB)
//! i local s,k = code:find(line)
//! i if (s ~= nil) then
//! i write(JASS_HUB, code:sub(1,s-1) .. code:sub(k+1))
//! i end
//! i end
//! i end
//! endtextmacro
//TESH.scrollpos=5
//TESH.alwaysfold=0
// This is the installation script for use of the lua func "createdummybook".
// createdummybook ( name, passiveID )
//
// name - The name of the dummy spell book
// passiveID - The rawcode ID of the passive ability you are making a spell book for.
//
// This script is used to create dummy spell books for passive abilities so they will be
// active even if you are not on the appropriate page.
//
//! externalblock extension=lua ObjectMerger $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writelua("CreateDummyBook", [[
//! i dofile("GetVarObject")
//! i function createdummybook( name, passiveID )
//! i local rawc = getvarobject("Aspb", "abilities", "ABILITIES_BOOK_" .. name, "true")
//! i createobject("Aspb", rawc)
//! i makechange(current, "aite", "0")
//! i makechange(current, "anam", name)
//! i makechange(current, "ansf", " [Dummy]")
//! i makechange(current, "spb3", "1", "1")
//! i makechange(current, "spb4", "1", "11")
//! i makechange(current, "spb1", "1", passiveID)
//! i makechange(current, "spb5", "1", "ambush")
//! i updateobjects()
//! i end
//! i ]])
//! endexternalblock
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! externalblock extension=lua ObjectMerger $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i dofile("CreateDummyBook")
// THIS IS JUST A SAMPLE
//! i createdummybook( "Brilliance Aura" , "AHab" )
//! i createdummybook( "Devotion Aura" , "AHad" )
//! i createdummybook( "Bash" , "AHbh" )
// SEE THE TRIGGER "WriteToScript" FOR DOCUMENTATION
//! endexternalblock
// the example above creates three spellbooks
//TESH.scrollpos=10
//TESH.alwaysfold=0
// This is the stand alone version for create dummy books for passives
// createdummybook( name, rawcode, passiveID )
// name - What to name the dummy spell book.
// rawcode - What the rawcode of the spell book is.
// passiveID - The rawcode of the passive ability to add to the dummy spell book.
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i function createdummybook( name, rawcode, passiveID )
//! i setobjecttype("abilities")
//! i createobject("Aspb", rawcode)
//! i makechange(current, "aite", "0")
//! i makechange(current, "anam", name)
//! i makechange(current, "ansf", " [Dummy]")
//! i makechange(current, "spb3", "1", "1")
//! i makechange(current, "spb4", "1", "11")
//! i makechange(current, "spb1", "1", passiveID)
//! i makechange(current, "spb5", "1", "ambush")
//! i end
// create the dummy books here
// enable the trigger to generate the abilities
// (you'll want to make your own for your map, however. This is just a sample)
// SAMPLE
//! i createdummybook( "Brilliance Aura" , "Ab01" , "AHab" )
//! i createdummybook( "Bash" , "Ab02" , "AHbh" )
//! i createdummybook( "Devotion Aura" , "Ab03" , "AHad" )
//! i createdummybook( "Evasion" , "Ab04" , "AEev" )
//! i createdummybook( "Thorns Aura" , "Ab05" , "AEah" )
//! i createdummybook( "Trueshot Aura" , "Ab06" , "AEar" )
// SAMPLE
//! endexternalblock