- Joined
- Jul 17, 2011
- Messages
- 1,863
Let's scrap this, nobody cares. It's all self glorified crap >.>
people should just code w/e they wanna code
noooooo i like the platform one
Let's scrap this, nobody cares. It's all self glorified crap >.>
people should just code w/e they wanna code
library PeriodicEffectsAlpha
globals
private constant integer MAX_INSTANCES = 8191
endglobals
struct PEA [MAX_INSTANCES]
endstruct
endlibrary
library PeriodicEffectsAlpha requires Alloc
globals
constant integer PE_TYPE_DAMAGE = 1
constant integer PE_TYPE_HEALTH = 2
constant integer PE_TYPE_MANA = 3
constant integer PE_TYPE_XP = 4 // only for heroes.
constant real PE_TEXTTAG_TIME_SCALE = 1.000
constant real PE_TEXTTAG_UPDATE_TICK = 0.25
constant real PE_TEXTTAG_FONT_SIZE = 10.000 *(0.023 / 10.000)
constant integer PE_TEXTTAG_RULE_HIDE_ALL = 1
constant integer PE_TEXTTAG_RULE_SHOW_CHOSEN = 2
constant integer PE_TEXTTAG_RULE_SHOW_ALL = 3
constant real PE_INDEXER_CHECK_TICK = 5.000
endglobals
module PE_Settings
private static method onInit takes nothing returns nothing
call thistype.createChannel(1,"|c00FF0000","",true) // damage can't be negative.
call thistype.createChannel(2,"|c0000FF00","|c0000FF00",true) // for health.
call thistype.createChannel(3,"|c000000FF","|c000000FF",true) // for mana.
call thistype.createChannel(4,"|c00FEBA0E","|c00FEBA0E",true) // for experience.
set texttagRule = PE_TEXTTAG_RULE_SHOW_ALL
endmethod
endmodule
function PE_TEXTTAG_RULE_SHOW_CHOSEN_FILTER takes unit u returns boolean
if not IsUnitType(u,UNIT_TYPE_HERO) then
return false
endif
return true
endfunction
module PE_SimpleIndexer
static integer instanceCount = 0
private static thistype recycleStack = 0
private thistype recycleStackNext
private static unit array indexedUnits
private static method fetch takes nothing returns thistype
local thistype this
if (recycleStack == 0) then
set instanceCount = instanceCount + 1
set this = instanceCount
else
set this = recycleStack
set recycleStack = recycleStack.recycleStackNext
endif
return this
endmethod
private method release takes nothing returns nothing
set recycleStackNext = recycleStack
set recycleStack = this
endmethod
static method doIndex takes unit u returns integer
local integer index
if getIndex(u)==0 or getIndex(u)==null then
set index = fetch()
call SaveInteger(thistype.table,GetHandleId(u),0,index)
set indexedUnits[index] = u
if texttagRule==PE_TEXTTAG_RULE_SHOW_CHOSEN then
if PE_TEXTTAG_RULE_SHOW_CHOSEN_FILTER(u) then
set isActive[index] = true
else
set isActive[index] = false
endif
elseif texttagRule==PE_TEXTTAG_RULE_SHOW_ALL then
set isActive[index] = true
else
set isActive[index] = false
endif
set damageChannels[index] = PE_TYPE_DAMAGE
set healthChannels[index] = PE_TYPE_HEALTH
set manaChannels[index] = PE_TYPE_MANA
set xpChannels[index] = PE_TYPE_XP
return index
else
return getIndex(u)
endif
endmethod
static method deIndex takes unit u returns nothing
local integer index = LoadInteger(thistype.table,GetHandleId(u),0)
call thistype(index).release()
call FlushChildHashtable(thistype.table,GetHandleId(u))
set indexedUnits[index] = null
set isActive[index] = false
call DestroyTextTag(texttags[index])
//call BJDebugMsg("deindexing")
endmethod
static method getIndex takes unit u returns integer
return LoadInteger(thistype.table,GetHandleId(u),0)
endmethod
static method getIndexedUnit takes integer i returns unit
return indexedUnits[i]
endmethod
private static method doCheck takes nothing returns nothing
local thistype this
local integer counter = 1
local group temporaryGroup = CreateGroup()
loop
set this = thistype(counter)
exitwhen .whatType==0
if .targets!=null then
call addGroupToGroup(.targets,temporaryGroup,false,false)
endif
set counter = counter + 1
endloop
set counter = 1
loop
exitwhen counter>instanceCount
if IsUnitInGroup(indexedUnits[counter],temporaryGroup)==false then
call deIndex(indexedUnits[counter])
endif
set counter = counter + 1
endloop
set counter = 0
set this = 0
call DestroyGroup(temporaryGroup)
endmethod
private static method onInit takes nothing returns nothing
local timer t = CreateTimer()
call TimerStart(t,PE_INDEXER_CHECK_TICK,true,function thistype.doCheck)
endmethod
endmodule
struct PEA extends array
// -------------------- Main --------------------
private static hashtable table
unit sourceUnit // only needed when using damage effect for defining the damager.
real timeout
real amount
integer totalTicks
private integer currentTicks
integer maxTargets
private integer whatType
private boolean haveModel
private boolean haveTrigger
private boolean isPaused
private boolean destroyOnEnd
private string modelPath
private string modelAttachmentPoint
damagetype damageType
attacktype attackType
private timer effectTimer
private group targets
private trigger sideTrigger
private static code mainCode
// -------------------- Channels --------------------
private static integer array damageChannels
private static integer array healthChannels
private static integer array manaChannels
private static integer array xpChannels
private static string array positiveColors
private static string array negativeColors
private static boolean array showSigns
// -------------------- Texttag --------------------
static integer texttagRule
private static boolean array isActive
private static timer texttagTimer
private static texttag array texttags
private static code texttagCode
// -------------------- Script begins --------------------
implement Alloc
implement PE_SimpleIndexer
static method getDamageStack takes unit target returns real
return LoadReal(table,GetHandleId(target),PE_TYPE_DAMAGE)
endmethod
static method getHealthStack takes unit target returns real
return LoadReal(table,GetHandleId(target),PE_TYPE_HEALTH)
endmethod
static method getManaStack takes unit target returns real
return LoadReal(table,GetHandleId(target),PE_TYPE_MANA)
endmethod
static method getXPStack takes unit target returns real
return LoadReal(table,GetHandleId(target),PE_TYPE_XP)
endmethod
static method changeChannelsForUnit takes unit desiredUnit, integer damageID, integer healthID, integer manaID, integer xpID returns nothing
local integer index = getIndex(desiredUnit)
set damageChannels[index] = damageID
set healthChannels[index] = healthID
set manaChannels[index] = manaID
set xpChannels[index] = xpID
set index = 0
endmethod
static method changeChannelsForGroup takes group desiredGroup, integer damageID, integer healthID, integer manaID, integer xpID returns nothing
local group tempGroup = CreateGroup()
local unit firstUnit
call addGroupToGroup(desiredGroup,tempGroup,false,false)
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null
call changeChannelsForUnit(firstUnit,damageID,healthID,manaID,xpID)
call GroupRemoveUnit(tempGroup,firstUnit)
endloop
endmethod
static method createChannel takes integer desiredID, string positiveColor, string negativeColor, boolean showSign returns nothing
set positiveColors[desiredID] = positiveColor
set negativeColors[desiredID] = negativeColor
set showSigns[desiredID] = showSign
endmethod
method useDefaults takes nothing returns nothing
set .timeout = 0.000
set .amount = 0.000
set .totalTicks = 0
set .currentTicks = 0
set .maxTargets = 100000
set .haveModel = false
set .haveTrigger = false
set .isPaused = true
set .destroyOnEnd = true
set .damageType = DAMAGE_TYPE_NORMAL
set .attackType = ATTACK_TYPE_NORMAL
endmethod
static method createEffect takes integer typeInteger returns thistype
local thistype this
debug if typeInteger!= PE_TYPE_DAMAGE and typeInteger!= PE_TYPE_HEALTH and typeInteger!= PE_TYPE_MANA and typeInteger!= PE_TYPE_XP then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"|cffff0000Periodic Effects: Given effect type does not exist!")
debug endif
set this = thistype.allocate()
set this.targets = CreateGroup()
set this.whatType = typeInteger
call this.useDefaults()
return this
endmethod
method setModel takes string path, string attachmentPoint returns nothing
set .haveModel = true
set .modelPath = path
set .modelAttachmentPoint = attachmentPoint
endmethod
method setTrigger takes trigger userTrigger returns nothing
set .haveTrigger = true
set .sideTrigger = userTrigger
endmethod
private static method addGroupToGroup takes group who, group target, boolean isSubstract, boolean doIndexing returns nothing
local group tempGroup = CreateGroup()
local unit firstUnit
loop
set firstUnit = FirstOfGroup(who)
exitwhen firstUnit==null
call GroupAddUnit(tempGroup, firstUnit)
call GroupRemoveUnit(who, firstUnit)
endloop
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null
if isSubstract then
call GroupRemoveUnit(target,firstUnit)
else
call GroupAddUnit(target, firstUnit)
if doIndexing then
call thistype.doIndex(firstUnit)
endif
endif
call GroupAddUnit(who, firstUnit)
call GroupRemoveUnit(tempGroup, firstUnit)
endloop
call DestroyGroup(tempGroup)
set firstUnit = null
endmethod
method addUnitToTargets takes unit who returns nothing
call GroupAddUnit(.targets, who)
call thistype.doIndex(who)
endmethod
method addGroupToTargets takes group who returns nothing
call addGroupToGroup(who,.targets,false,true)
endmethod
method removeUnitFromTargets takes unit who returns nothing
call GroupRemoveUnit(.targets, who)
endmethod
method removeGroupFromTargets takes group who returns nothing
call addGroupToGroup(who,.targets,true,true)
endmethod
private method processTimer takes nothing returns nothing
set .effectTimer = CreateTimer()
call SaveInteger(table,GetHandleId(.effectTimer),0,this)
call TimerStart(.effectTimer, .timeout, true, mainCode)
endmethod
method startEffect takes nothing returns nothing
set .isPaused = false
call .processTimer()
endmethod
method pauseEffect takes nothing returns nothing
set .isPaused = true
call PauseTimer(.effectTimer)
endmethod
method resumeEffect takes nothing returns nothing
set .isPaused = false
call ResumeTimer(.effectTimer)
endmethod
method restartEffect takes nothing returns nothing
set .isPaused = false
set .currentTicks = 0
call PauseTimer(.effectTimer)
call DestroyTimer(.effectTimer)
call .processTimer()
endmethod
method destroyEffect takes nothing returns nothing
call .pauseEffect()
call FlushChildHashtable(table,GetHandleId(.effectTimer))
call DestroyTimer(.effectTimer)
//call BJDebugMsg("calling deallocate3")
call .useDefaults()
set .sideTrigger = null
call .deallocate()
endmethod
method duplicateEffect takes nothing returns thistype
local thistype newEffect = thistype.createEffect(this.whatType)
set newEffect.amount = this.amount
set newEffect.timeout = this.timeout
set newEffect.totalTicks = this.totalTicks
set newEffect.maxTargets = this.maxTargets
set newEffect.haveModel = this.haveModel
if newEffect.haveModel then
set newEffect.modelPath = this.modelPath
set newEffect.modelAttachmentPoint = this.modelAttachmentPoint
endif
set newEffect.haveTrigger = this.haveTrigger
if newEffect.haveTrigger then
set newEffect.sideTrigger = this.sideTrigger
endif
if newEffect.whatType==1 then
set newEffect.damageType = this.damageType
set newEffect.attackType = this.attackType
endif
return newEffect
endmethod
method applyToUnit takes unit who returns thistype
local thistype freshEffect = .duplicateEffect()
call freshEffect.addUnitToTargets(who)
call freshEffect.startEffect()
return freshEffect
endmethod
method applyToGroup takes group who returns thistype
local thistype freshEffect = .duplicateEffect()
call freshEffect.addGroupToTargets(who)
call freshEffect.startEffect()
return freshEffect
endmethod
private static method texttagEngine takes nothing returns nothing
local thistype this
local integer counter = 1
local integer index
local group holderGroup = CreateGroup()
local group finalGroup = CreateGroup()
local unit firstUnit
local string tempString = ""
local real array damageStack
local real array healthStack
local real array manaStack
local real array xpStack
if texttagRule!=PE_TEXTTAG_RULE_HIDE_ALL then
loop
set this = thistype(counter)
exitwhen this.whatType==0
call GroupClear(holderGroup)
call addGroupToGroup(this.targets,holderGroup,false,false)
loop
set firstUnit = FirstOfGroup(holderGroup)
exitwhen firstUnit==null
set index = getIndex(firstUnit)
if isActive[index] then
if not IsUnitInGroup(firstUnit,finalGroup) then
call GroupAddUnit(finalGroup,firstUnit)
endif
if not .isPaused then
if .whatType==PE_TYPE_DAMAGE then
if .amount>0 then
set damageStack[index] = damageStack[index] + .amount*(PE_TEXTTAG_TIME_SCALE/.timeout)
endif
elseif .whatType==PE_TYPE_HEALTH then
set healthStack[index] = healthStack[index] + .amount*(PE_TEXTTAG_TIME_SCALE/.timeout)
elseif .whatType==PE_TYPE_MANA then
set manaStack[index] = manaStack[index] + .amount*(PE_TEXTTAG_TIME_SCALE/.timeout)
elseif .whatType==PE_TYPE_XP then
set xpStack[index] = xpStack[index] + R2I(.amount*(PE_TEXTTAG_TIME_SCALE/.timeout))
endif
elseif .isPaused and .currentTicks==.totalTicks then
call DestroyGroup(.targets)
endif
call GroupRemoveUnit(holderGroup,firstUnit)
endif
endloop
set counter = counter + 1
endloop
endif
set index = 0
loop
set firstUnit = FirstOfGroup(finalGroup)
exitwhen firstUnit==null
set index = getIndex(firstUnit)
set tempString = ""
//call BJDebugMsg("doing texttag for "+ GetUnitName(firstUnit) + " ("+I2S(index)+")")
if damageStack[index]!=0 then
set tempString = positiveColors[damageChannels[index]]
if showSigns[damageChannels[index]] then
set tempString = tempString + "!"
endif
set tempString = tempString + R2SW(damageStack[index],5,1) + "|n|r"
//call BJDebugMsg("tempString is: " + tempString)
else
//call BJDebugMsg("damage stack was 0!")
endif
if healthStack[index]!=0 then
if healthStack[index]>0 then
set tempString = tempString + positiveColors[healthChannels[index]]
if showSigns[healthChannels[index]] then
set tempString = tempString + "+ "
endif
else
set tempString = tempString + negativeColors[healthChannels[index]]
if showSigns[healthChannels[index]] then
set tempString = tempString + "- "
endif
endif
set tempString = tempString + R2SW(healthStack[index],7,1) + "|n|r"
endif
if manaStack[index]!=0 then
if manaStack[index]>0 then
set tempString = tempString + positiveColors[manaChannels[index]]
if showSigns[manaChannels[index]] then
set tempString = tempString + "+ "
endif
else
set tempString = tempString + negativeColors[manaChannels[index]]
if showSigns[manaChannels[index]] then
set tempString = tempString + "- "
endif
endif
set tempString = tempString + R2SW(manaStack[index],7,1) + "|n|r"
endif
if xpStack[index]!=0 then
if xpStack[index]>0 then
set tempString = tempString + positiveColors[xpChannels[index]]
if showSigns[xpChannels[index]] then
set tempString = tempString + "+ "
endif
else
set tempString = tempString + negativeColors[manaChannels[index]]
if showSigns[xpChannels[index]] then
set tempString = tempString + "- "
endif
endif
set tempString = tempString + R2SW(xpStack[index],7,1) + "|n|r"
endif
//call BJDebugMsg(I2S(StringLength(tempString)))
if StringLength(tempString)<1 then
call DestroyTextTag(texttags[index])
set isActive[index] = false
//call BJDebugMsg("Destroying texttag")
endif
if texttags[index]==null and isActive[index]==true then
set texttags[index] = CreateTextTag()
//call BJDebugMsg("Creating Texttag for index: "+I2S(index))
endif
if isActive[index]==true then
call SetTextTagPos(texttags[index],GetUnitX(firstUnit),GetUnitY(firstUnit),100)
call SetTextTagText(texttags[index],tempString,PE_TEXTTAG_FONT_SIZE)
call SetTextTagColor(texttags[index],255,255,255,255)
endif
call SaveReal(table,GetHandleId(firstUnit),PE_TYPE_DAMAGE,damageStack[index])
call SaveReal(table,GetHandleId(firstUnit),PE_TYPE_HEALTH,healthStack[index])
call SaveReal(table,GetHandleId(firstUnit),PE_TYPE_MANA,manaStack[index])
call SaveReal(table,GetHandleId(firstUnit),PE_TYPE_XP,xpStack[index])
call GroupRemoveUnit(finalGroup,firstUnit)
endloop
set counter = 0
set index = 0
set this = 0
set firstUnit = null
set tempString = null
call DestroyGroup(holderGroup)
call DestroyGroup(finalGroup)
//call BJDebugMsg("Ending texttag engine")
loop
exitwhen counter>thistype.instanceCount
set damageStack[counter] = 0.000
set healthStack[counter] = 0.000
set manaStack[counter] = 0.000
set xpStack[counter] = 0.000
set counter = counter + 1
endloop
set counter = 0
endmethod
private static method mainEngine takes nothing returns nothing
local thistype this = LoadInteger(table,GetHandleId(GetExpiredTimer()),0)
local integer counter = 0
local group tempGroup = CreateGroup()
local unit firstUnit
if .isPaused then
set this = 0
call DestroyGroup(tempGroup)
return
endif
set .currentTicks = .currentTicks + 1
call addGroupToGroup(.targets,tempGroup,false,false)
if .whatType==PE_TYPE_DAMAGE then
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null or counter>.maxTargets
if GetWidgetLife(firstUnit)>0.405 then
call UnitDamageTarget(.sourceUnit,firstUnit,.amount,true,false,.attackType,.damageType,WEAPON_TYPE_WHOKNOWS)
if .haveModel then
call DestroyEffect(AddSpecialEffectTarget(.modelPath,firstUnit,.modelAttachmentPoint))
endif
set counter = counter + 1
endif
call GroupRemoveUnit(tempGroup,firstUnit)
endloop
elseif .whatType==PE_TYPE_HEALTH then
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null or counter>.maxTargets
if GetWidgetLife(firstUnit)>0.405 then
call SetWidgetLife(firstUnit,GetWidgetLife(firstUnit)+.amount)
if .haveModel then
call DestroyEffect(AddSpecialEffectTarget(.modelPath,firstUnit,.modelAttachmentPoint))
endif
set counter = counter + 1
endif
call GroupRemoveUnit(tempGroup,firstUnit)
endloop
elseif .whatType==PE_TYPE_MANA then
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null or counter>.maxTargets
if GetWidgetLife(firstUnit)>0.405 then
call SetUnitState(firstUnit,UNIT_STATE_MANA,GetUnitState(firstUnit,UNIT_STATE_MANA)+.amount)
if .haveModel then
call DestroyEffect(AddSpecialEffectTarget(.modelPath,firstUnit,.modelAttachmentPoint))
endif
set counter = counter + 1
endif
call GroupRemoveUnit(tempGroup,firstUnit)
endloop
elseif .whatType==PE_TYPE_XP then
loop
set firstUnit = FirstOfGroup(tempGroup)
exitwhen firstUnit==null or counter>.maxTargets
if IsUnitType(firstUnit,UNIT_TYPE_HERO) and GetWidgetLife(firstUnit)>0.405 then
call AddHeroXP(firstUnit,R2I(.amount),false)
if .haveModel then
call DestroyEffect(AddSpecialEffectTarget(.modelPath,firstUnit,.modelAttachmentPoint))
endif
set counter = counter + 1
endif
call GroupRemoveUnit(tempGroup,firstUnit)
endloop
endif
if .haveTrigger then
call TriggerExecute(.sideTrigger)
endif
//end
if .currentTicks==.totalTicks then
if .destroyOnEnd then
call .destroyEffect()
else
call .pauseEffect()
endif
endif
endmethod
private static method onInit takes nothing returns nothing
set table = InitHashtable()
set mainCode = (function thistype.mainEngine)
set thistype.texttagCode = (function thistype.texttagEngine)
set thistype.texttagTimer = CreateTimer()
call TimerStart(thistype.texttagTimer,PE_TEXTTAG_UPDATE_TICK,true,thistype.texttagCode)
endmethod
implement PE_Settings
endstruct
endlibrary
function WidgetDropItem takes widget inWidget, integer inItemID returns item
local real x
local real y
local real radius = 32
local real widgetX
local real widgetY
if (inItemID == -1) then
return null
endif
set widgetX = GetWidgetX(inWidget)
set widgetY = GetWidgetY(inWidget)
set x = GetRandomReal(widgetX - radius, widgetX + radius)
set y = GetRandomReal(widgetY - radius, widgetY + radius)
return CreateItem(inItemID, x, y)
endfunction
This site has great railway and train models, yet no one ever coded a Train Transportation System!?![]()
LoL.
We list down all of the necessary systems that which most of the maps will use.![]()
Uhh Snakes?What map don't need a train-system? You clearly have no clue what you are talking about!
But on a serious note, because I saw suggestions of Command Systems, I though this suggestion was on topic.
Uhh Snakes?
Command systems? Like those in Dota? We already have those
I know we have those, but in Dota? Anyway it was on the list.
I've made a request of this though nobody took it upon themselves to do it. Fire-spread system on buildings, even trees and units.
Here are some:
http://www.hiveworkshop.com/forums/jass-resources-412/system-mode-manager-197956/
https://github.com/nestharus/JASS/blob/master/jass/Systems/Cmd/script.j
Both of which allows you to create commands. The Nestharus version is more of a prompt
Can you link your buff library?
Does it handle stacked buffs and overwriting buffs that already exist if the level is higher? Is there an option for the lower buff to return when the new buff fades, like with auras?
Also, how are the buff effects run? Periodic vs Instant. On buff, on debuff, etc.
Resources that have yet to be coded
Item Indexer
______________________________
No periodic loop
globals
item array udg_ItemIndex_Items
integer udg_ItemIndex_NextIndex = 0
endglobals
function CreateItemIndex takes item whichItem returns nothing
loop
set udg_ItemIndex_NextIndex = udg_ItemIndex_NextIndex +1
exitwhen udg_ItemIndex_Items[udg_ItemIndex_NextIndex] == null
if udg_ItemIndex_NextIndex >= 8191 then
set udg_ItemIndex_NextIndex = 0
endif
endloop
set udg_ItemIndex_Items[udg_ItemIndex_NextIndex] = whichItem
call SetItemUserData(whichItem, udg_ItemIndex_NextIndex)
endfunction
function RemoveItemIndex takes item whichItem returns nothing
set udg_II_AntiLoop = true
set udg_ItemIndex_Items[GetItemUserData(whichItem)] = null
call SetItemUserData(whichItem, 0)
set udg_II_AntiLoop = false
endfunction
function SecureItemIndex takes item whichItem returns integer
if udg_II_AntiLoop then
return 0
endif
set udg_II_AntiLoop = true
if GetItemUserData(whichItem) == 0 then
call ItemIndex_CreateIndex(whichItem)
return udg_ItemIndex_NextIndex
endif
set udg_II_AntiLoop = false
return 0
endfunction
hook GetItemUserData SecureItemIndex
globals
item array udg_ItemIndex_Items
integer udg_ItemIndex_NextIndex = 0
endglobals
function CreateItemIndex takes item whichItem returns nothing
loop
set udg_ItemIndex_NextIndex = udg_ItemIndex_NextIndex +1
exitwhen udg_ItemIndex_Items[udg_ItemIndex_NextIndex] == null
if udg_ItemIndex_NextIndex >= 8191 then
set udg_ItemIndex_NextIndex = 0
endif
endloop
set udg_ItemIndex_Items[udg_ItemIndex_NextIndex] = whichItem
call SetItemUserData(whichItem, udg_ItemIndex_NextIndex)
endfunction
function RemoveItemIndex takes item whichItem returns nothing
set udg_ItemIndex_Items[GetItemUserData(whichItem)] = null
call SetItemUserData(whichItem, 0)
endfunction
function GetItemIndex takes item whichItem returns integer
local integer id = GetItemUserData(whichItem)
if id == 0 then
call ItemIndex_CreateIndex(whichItem)
return udg_ItemIndex_NextIndex
endif
return id
endfunction
Wow, dat arrogance... But just to get the facts straight: Yes, BuffHandler allows stacking. It's literally the default setting for any buff created.I was looking for Xonok's, not Muzzel's ; ).
I can't even read Muzzel's API at a glance, so I haven't/won't look at his : P. I did read some of the code though and it doesn't seem to do the stacking etc, which is the most complex part of buffs =).
BuffHandler can do all this just fine.The system itself handles only a few things. Each unit has a doubly linked list containing its buffs. Each buff has a similar list containing its effects. Both lists also have a counter for easier iteration.
The base system also handles unique ID generation for buffs and effects.
Each buff effect can have a variety of methods:
Apply - When the parent buff is applied to a unit.
Clear - When parent buff is removed from unit
Construct - When effect is added to buff.
Destruct - When effect is removed from buff.
The type off a buff is set during its creation and has no other interaction with the core system. It is in no way related to what effects the buff has, except what the user implements.
Buffs can be created and effects added to them without the buff applying to any unit. They can also be removed from units without destroying them(think of spellsteal to understand why).
Effects have a type and all their methods are attached to the type, not the instance.
I don't think all this should actually be part of the buff system itself. The buff system should be the framework, not providing any gameplay effects directly. For this, you should write an extension that builds on your buff system.What exactly a buff does is defined by what methods it's added upon creation. Multiple effects of the same type can be added to the same buff.
Current methods:
EffectSfx - Creates a special effect on target unit.
EffectSpeed - Modifies the speed of target unit.
EffectTimeout - Destroys the buff a certain time after it has been applied to a unit. Transferring the buff resets the timer.
EffectUnique - Removes any buffs of the same type when this effect is applied to a unit.
EffectHoT - Heals target unit over time.
EffectDoT - Damages target unit over time.
EffectDoR - Deals damage to parent unit when the buff is removed from it for any reason.
EffectRoD - Destroys the buff when the target takes damage.
EffectStun - Stuns the target of the buff until the buff wears off. Handles stacking properly as defined by how they work in melee.
EffectMoT - Restores mana on the unit over time.
Creating new effects is fairly easy as the complexities of applying effects to buffs and units are abstracted away. This keeps the core system safe from feature creep and lets users easily define how exactly they want to use the system.
1. Muzzel's buff can do all this without any hardcoded functionality like in your solution; just add a custom struct member for the casting unit and pass it to the struct on buff creation. Done. BuffHandler allows complete freedom about what parameters you want on each buff and what not, as it makes use ofCurrently the system is in heavy development, far from being finished(although usable) and the code may be messy. It's still superior(for my use case) to any other buff system that I've encountered including muzzel's, because:
1. It allows defining the exact effects of a buff separate from its type. This makes it possible to have buffs that have effects based on external conditions, such as the attributes of the casting unit or other abilities present.
2. It can be made to include the functionality of nearly all object editor spell buffs without taking excessive time for any single one.
3. It's easy to implement and extend.
extends
to create a buff struct from the base struct.struct BuffXYZ extends Buff
Wow, dat arrogance...
Somehow I could tell how it works with just the blink of an eye.Bad documentation is bad documentation, lol. If you can't tell the API at a glance, then the documentation sucks ; P.
1. Yeah but let's be honest here... why would you ever need that? It makes sense that buff effects are defined by the buff type. If you want different buff groups, you can always create a middleman-buff and extend your sub-buffs from that. So basiclyYou didn't seem to realize that the listed effects are separate from the core system.
1. It can technically not do what my system can, because the effects of a buff are defined by type as opposed to being defined on runtime like I've done.
2. It requires AIDS and TimerUtils, which I don't use or need.
3. Some other stuff that I don't have time to explain atm.
BuffVenom extends BuffDoT extends Buff
.... I prefer readability over miniscule performance gains any day. But I see how people can have a different oppinion on this. Fair enough.Also, I started development before muzzel's system became public and prefer to write my systems myself whenever possible. I've been designing my system for ages. In any case, I will not use a system that uses structs. It's a huge no-no for me to use a system that obfuscates code without any performance gain.
1. Yeah but let's be honest here... why would you ever need that? It makes sense that buff effects are defined by the buff type. If you want different buff groups, you can always create a middleman-buff and extend your sub-buffs from that. So basiclyBuffVenom extends BuffDoT extends Buff
.
2. This is legit. Then again, changing or removing dependencies is a matter of minutes
... I prefer readability over miniscule performance gains any day. But I see how people can have a different oppinion on this. Fair enough.
But then, this is all your personal oppinion. It's not like the system has any obvious flaws that requires a re-code or re-submit of a buff system.
But never mind; we'll see when your system is done. At least your system has the advantage of the maker still being around here.![]()
I guess it really depends on how you actually use buffs in your map.1. I didn't consider this at first either. Eventually I realized that it's necessary at least for my purposes though. Having this choice doesn't really hurt either, as you can still have a wrapper that provides exactly that - buff type definitions. I might even provide it as one of the useful libraries. That way the best of both worlds can be achieved.
About the example you provided, my system uses effects for this. The thing is, extending things like that doesn't allow freely allocating buff effects. I find it better to be able to create buffs by listing their effects(which I effectively do, despite not having effects defined by buff types).
2. I prefer not to have to touch system code at all. If I take the effort of getting to know someone else's code, I might as well code the thing myself, removing the reason of using other people's code in the first place.
SongOfX extends Song extends Buff
SongOfY extends Song extends Buff
Depends. If you come from an OOP background, structs are intuitive in design and improve readability by adding a clear inheritance structure.Well, if it actually gave any readability... The useful thing about structs is having the ability to define types, but the way structs are typically used, they outweigh that benefit with having to learn additional syntax to do what is effectively the same. This obfuscates code and makes the learning curve steeper for no apparent gain. I consider it better to use obfuscating as little as possible, especially when they give no performance or understanding benefit.
car.controls.breaks.push()
milkyway.solarsystem.earth.america.town.car.controls.breaks.push()
I haven't seen him active for months, tbh.I thought muzzel is still here. Is he not?
I guess it really depends on how you actually use buffs in your map.
I personally use it in a way that I create 1 new Buffstruct extending buff for any buff (= different icon and tooltip) I want to have in my map.
If the buff is basicly a mutual exclusive type, I create a middleman buff; so, for example:
SongOfX extends Song extends Buff
I've found this to be a very intuitive solution. Every new child layer basicly adds exclusive parameters specific to the type of buff. Songs add a duration parameter. SongofX adds a strength bonus parameter.
If I had another song called SongOfY that adds intelligence instead of strength, I'd just inherit from the middleman:
SongOfY extends Song extends Buff
It still inherits the duration parameter from Song and all the members of the general Buff struct, but it doesn't have the strength parameter of SongOfX.
I never felt the need to extend any custom buffs from "functionality types", like DoT or HoT or anything like that - because when I took a look at all the abilities I have, I've found them to be too different in design for that to actually be worth the effort. Basicly, as soon as you switch over to a registry of effects, rather than having custom code callbacks for every buff, you also artifically limit your freedom in buff design. What if I want a DoT that instead of damaging heals a very specific type of unit (and only that)? What if I want a DoT to have a chance of randomly "exploding"? I'd need to define a custom effect for anything that goes out of the ordinary.
An effects approach like you are suggesting is great if you have lots, and I mean really lots of buff spells in your game that are very similar to each other. It's basicly the Personification of the "Don't repeat yourself"-principle.
But when we really think about that in terms of game design, then you will ultimately have to ask one question: "If I have dozens of DoT type spells, how are they actually different from each other?"
This is actually a matter of good game design. There is no reason to have redundant abilities, other than for eye candy effects. It just convolutes your game with too much extra information to manage.
I always followed the philosophy that a fireball is a fireball. I could create dozens of other abilities that effectively do the same (dealing damage in an area), having cute names like iceball or doomsday bowl of ultimate doom, but in the end, even with the changed visuals and name, they'd still be a fireball at heart.
And at this point, why even bother adding them? What's the value in having "more of the same"?
When I internalized this principle, I realized that the "don't repeat yourself"-principle doesn't really apply to buffs. Because if you come into a situation in which you would repeat yourself, then you already did something wrong from a game design perspective.
I hope you could follow my reasoning here.
Depends. If you come from an OOP background, structs are intuitive in design and improve readability by adding a clear inheritance structure.
You know that any car has a steering wheel. You can access that steering wheel when you have access to the car. If you have a steering wheel, on the other hand, it doesn't neccesarily mean that you sit inside a car. For example, you could also be sitting in a boat. This logic is very useful in programming, as you can freely add layers of abstraction to your code, depending on what you want to do.
The struct syntax is designed to have a visual representation of this principle.
With every "Dot" applied to a struct, you basicly go one step further into detail, starting from the largest layer of abstraction:
car.controls.breaks.push()
With good naming conventions, this can go a long way to improve readability.
Go a step further and you will see something like this:
milkyway.solarsystem.earth.america.town.car.controls.breaks.push()
I think you get the idea. It's great for managing huge amounts of data with a high level of abstraction.
I haven't seen him active for months, tbh.
struct BuffPoison extends Buff
unit caster
real dmg
method getName takes nothing returns string
return "Poison"
endmethod
method getRaw takes nothing returns integer
return 'A000' //basicly the tornado ability used for the buff icon
endmethod
method periodic takes nothing returns nothing
call UnitDamageTarget(this.caster, this.getUnit(), this.dmg)
endmethod
method onApply takes nothing returns nothing
call this.startPeriodic(3)
call this.setDuration(15)
endmethod
endstruct
function Spell takes nothing returns nothing
//this function gets called by a spellcast trigger, obviously
local BuffPoison b = BuffPoison.create()
call b.apply(GetSpellTargetUnit())
set b.caster = GetTriggerUnit()
set b.dmg = GetHeroInt(GetTriggerUnit())
endfunction
method onDamage takes unit source returns nothing
//whatever it should do
endmethod
Buff.onDamage.exists
and if so, run it.Just take the code above, adjust the tornado aura ability to slow by 30% (or use SetUnitMoveSpeed, but I don't recommend that because it's a crappy native). Change the duration value to 6 instead of 15 and set dmg to 100.What if I want a buff that slows 30% for 6 seconds and deals 100 damage per second for the duration?
public function Apply takes unit target,unit caster returns nothing
local integer buffid = Buff_GrantBuff(target,'BPoi')
local integer effectid = Buff_CreateEffect(EffectSfx)
call EffectSfx_SetSfxPath(effectid,"insert\model\here.mdl")
call EffectSfx_SetAttachPoint(effectid,"chest")
call Buff_BuffAddEffect(buffid,effectid)
set effectid = Buff_CreateEffect(EffectTimeout)
call EffectTimeout_Set(effectid,10)
call Buff_BuffAddEffect(buffid,effectid)
set effectid = Buff_CreateEffect(EffectDoT)
call EffectDoT_SetTimeout(effectid,0.1)
call EffectDoT_SetDPS(effectid,100)
call EffectDoT_SetSource(effectid,caster)
call EffectDoT_SetAbility(effectid,Lethal_Poison)
call EffectDoT_SetAttackType(effectid,ATTACK_TYPE_NORMAL)
call EffectDoT_SetDamageType(effectid,DAMAGE_TYPE_MAGIC)
call Buff_BuffAddEffect(buffid,effectid)
set effectid = Buff_CreateEffect(EffectSpeed)
call EffectSpeed_SetSpeeds(effectid,0,-500)
call Buff_BuffAddEffect(buffid,effectid)
//set effectid = Buff_CreateEffect(EffectUnique)
//call Buff_BuffAddEffect(buffid,effectid)
endfunction
Just take the code above, adjust the tornado aura ability to slow by 30% (or use SetUnitMoveSpeed, but I don't recommend that because it's a crappy native). Change the duration value to 6 instead of 15 and set dmg to 100.
What? Of course it provides spell steal:That approach doesn't provide full functionality though. Consider spell steal.
Why have a buff system if it can't be used to reasonably provide everything that the inferior(default) system does?
b.apply(unit)
All this should be done via a Bonus mod, not via a buff system. You then just apply these bonuses onApply and remove them onRemove. It's as simple as that. What numbers you use there is completely up to you.I hope that you understand that that is not really what I meant.
What I mean is that I can add several different effects on it.
For example, I want the buff to also increase armor, health and damage by 10%.
Also, I want that the unit gain a heal and mana regain over time.
If that is not enough, I want the duration be equal to 6 + 10% INT.
What I mean is that you have to CODE the effects.
Lets for example take a custom stat system and I use triggers to change movement speed.
In that case, my tornado can do what he wants but he never slows any units.
Updated my previous post with an example.
I replicated the exact same behaviour in BuffHandler just for the lulz:EDIT: Example of the buff that Wietlol mentioned. Note that the verbosity is due to how I prefer it and it doesn't have to be like that. Every line here that starts with EffectSfx_ handles effects and thus, is not part of the core system.
struct BuffPoison extends Buff
effect sfx
unit caster
method getName takes nothing returns string
return "Poison"
endmethod
method getRaw takes nothing returns integer
return 'BPoi'
endmethod
method getPolicy takes nothing returns integer
return BUFF_POLICY_LAST
endmethod
method periodic takes nothing returns nothing
call UnitDamageTarget(this.caster, this.getUnit(), this.dmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
endmethod
method onApply takes nothing returns nothing
set this.sfx = AddSpecialEffectTarget("insert\\model\\here.mdl", this.getUnit(), "chest")
call SetUnitMoveSpeed(this.getUnit(), GetUnitDefaultMoveSpeed(this.getUnit())-500) //better use a bonus mod here to apply the slow effect
endmethod
method onRemove takes nothing returns nothing
call DestroyEffect(this.sfx)
set this.sfx = null
set this.caster = null
call SetUnitMoveSpeed(this.getUnit(), GetUnitDefaultMoveSpeed(this.getUnit()))
endmethod
static method add takes unit caster returns Buff
local thistype b = thistype.create()
call b.setDuration(10)
call b.startPeriodic(0.1)
set b.dmg = 10
set b.caster = caster
endmethod
endstruct
onApply
method.// API:
// ------------------------------------------------------------------------------------------------
// Basics
// ------------------------------------------------------------------------------------------------
// has(int x) --> Check if value is in stack
// index(int value) --> Get the index of value
// add(int x) --> Add a new value to the stack
// clear() --> Remove all values from the stack
// count --> Return how many elements are in stack
// delete(int x) --> Delete a value from the stack
// full --> Boolean whether Stack is full or not
// empty --> Boolean whether Stack is empty or not
//
// ------------------------------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------------------------------
// each(code x) --> Execute this function for each value in stack
// max[= x] --> Get/Set maximum amount of elements in stack
// merge(Stack x) --> Merge other stack into current stack
// split(Stack x, int y) --> Split this stack with elements after y into the new stack
// switch(int x, int y) --> Switch indexes for value x and y
// sort(bool asc) --> Sort Stack ascend or descending
//
// ------------------------------------------------------------------------------------------------
// Iteration
// ------------------------------------------------------------------------------------------------
// cursor --> Get current cursor index location
// direction --> Get boolean, whether iterating is forward (true) or backward (false)
// hasNext() --> Boolean, whether cur is at end or not
// hasPrev() --> Boolean, whether cur is at start or not
// getNext() --> Increases cur by 1 and returns the current element
// getPrev() --> Decreases cur by 1 and returns the current element
// reset() --> Sets cur to -1 (start)
// end() --> Sets cur to count
//
// ------------------------------------------------------------------------------------------------
// Getters
// ------------------------------------------------------------------------------------------------
// get(int x) --> Get value on index x
// getFirst() --> Returns first element of stack
// getLast() --> Returns the last element of stack
// random() --> Return any random value of the stack
//
// ------------------------------------------------------------------------------------------------
// Each Static Members
// ------------------------------------------------------------------------------------------------
// thistype.eachStack --> Current instance of stack
// thistype.eachValue --> Current value
// thistype.eachIndex --> Current index
//*******************************************
//* .___.
//* / \
//* | O _ O |
//* / \_/ \
//* .' / \ `.
//* / _| |_ \
//* (_/ | | \_)
//* \ /
//* __\_>-<_/__
//* ~;/ \;~
//*
//* muzzels Spellbox presents:
//*
//* BuffHandler
//* Version 1.1
//*
//*******************************************
//* Documentation
//*
//*
//* 1. Basics
//* =========
//* 1.1 Defining bufftypes
//* Each bufftype basically exists of one struct that holds all the variable
//* members and methods your buff needs. The struct has to extend "Buff" which
//* allows you to access most of the functionality of this system and registers
//* your buff for the automated bufflists of your units.
//* For a cleaner code it is recommended that you use a new library for every
//* bufftype you define.
//*
//* struct MyBuff extends Buff
//* // ...
//* endstruct
//*
//* 1.2 Applying buffs to units
//* To bring a buff on a unit you first have to create a new instance by calling:
//*
//* local MyBuff b = MyBuff.create()
//*
//* Then you can apply the newly created buff to a unit:
//* call b.apply(someUnit)
//*
//* To destroy a buff simply call the destructor method:
//*
//* call b.destroy()
//*
//* 1.3 Buff events
//* A very important thing to remember is that you may not define the normal
//* constructor and destructor methods for your bufftype, namely the "create"
//* and "onDestroy" methods. They are used by the system and writing your own
//* will throw an error.
//* Instead you may optionally implement the following methods:
//*
//* method onCreate takes nothing returns nothing
//* endmethod
//* // Will be run immediately after the buff is created.
//* // Use this as a replacement for the "create" method.
//*
//* method onApply takes nothing returns nothing
//* endmethod
//* // Will be run immediately after the buff is applied to a unit.
//*
//* method onRemove takes nothing returns nothing
//* endmethod
//* // Will be run right before the buff is removed from the unit.
//* // (e.g. applied to another unit or destroyed)
//*
//* method preDestroy takes nothing returns nothing
//* endmethod
//* // Will be run right before the buff is destroyed.
//* // Use this as a replacement for the "onDestroy" method.
//*
//* To access the unit the buff is currently applied to you may use the
//* method ".getUnit()".
//* If the buff is created but not yet applied it will return "null".
//*
//* 1.4 Eyecandy
//* Defining a bufficon involves some object data creation.
//* First you need to create a new ability based off "Slow-Aura (Tornad)"
//* ('Aasl') with a buff based on "Tornado (Slow-Aura)" ('Basl'). Change the
//* name, tooltip and icon of the buff to whatever you want.
//* The name of the buff is by default displayed in red. To change it to green
//* add the following colortags: "|cff00ff00BuffName|r".
//*
//* To apply that bufficon to your buff just add the following method to your
//* struct which returns the raw id of your newly created ability:
//*
//* method getRaw takes nothing returns integer
//* return 'A000'
//* endmethod
//*
//* In addition to "getRaw" to remove the delay between the removal of the
//* buff and its icon you can optionally define the function:
//*
//* method getBuffRaw takes nothing returns integer
//* return 'B000'
//* endmethod
//*
//* You may also define an optional name for your buff. Note that this name
//* is not required and also not displayed anywhere, but some of the debugging
//* functions make use of it and its a good convention to define names for all
//* your buffs. To define a name for your buff just add the following method
//* to your struct which returns the desired name:
//*
//* method getName takes nothing returns string
//* return "MyBuff"
//* endmethod
//*
//* 2. Advanced Features
//* ====================
//* 2.1 Unapplying and Reapplying
//* Once applied buffs are not bound to a specific unit but can be Reapplied
//* (moved) to another unit which may even be "null" (the buff is then in a
//* not applied state, but does not get deleted). Do do this simply call the
//* apply method again:
//*
//* call b.apply(someOtherUnit) // reapplying the buff to someOtherUnit
//* call b.apply(null) // unapplying the buff
//*
//* When reapplying a buff to another unit the methods "onRemove" and
//* "onApply" will, if implemented, be called.
//* Note that in "onRemove" the method "getUnit()" will refer to the old unit
//* while in "onApply" it will already return the new unit.
//*
//* 2.2 Buffs with Duration
//* To easily create a buff with limited duration you can make use of the
//* following method that starts a timer and destroys the buff after the
//* specified timeout:
//*
//* method setDuration takes real duration returns nothing
//*
//* You can either call this function inside of "onCreate" or inside of
//* "onApply". This will have two different effects:
//* When called in "onCreate" the timer will continue when the buff is being
//* reapplied to another unit. When called in "onApply" the timer will
//* restart when reapplied.
//*
//* 2.3 Periodic Buffs
//* To easily create buffs that perform a certain action periodically you
//* can define the following method:
//*
//* method periodic takes nothing returns nothing
//* // Do something
//* endmethod
//*
//* You can start and stop the periodical loop with the following methods:
//* method startPeriodic takes real timeout returns nothing
//* method stopPeriodic takes nothing returns nothing
//*
//* 3. BuffLists
//* ============
//* 3.1 Accessing BuffLists
//* When applied to a unit buffs get stored in the units bufflist. Any units
//* bufflist can be accessed using the following function:
//*
//* function getUnitBuffList takes unit u returns BuffList
//*
//* To access the bufflist a buff is currently applied you can use the
//* following method of the buffs struct:
//*
//* method getParentBufflist takes nothing returns BuffList
//*
//* Note that this is faster than using "getUnitBuffList(this.getUnit())"
//* since it does not require the unit indexing system.
//*
//* 3.2 BuffType-Ids
//* Something that might be useful when working with BuffLists are the
//* BuffType-Ids. BuffType-Ids are automatically assigned to each bufftype
//* and provide a simple way to compare the type of different buffs.
//*
//* To get a buffs BuffType-Id call the function:
//*
//* method getBuffTypeId takes nothing returns integer
//*
//* For technical reasons there is currently no static method that returns
//* the BuffType-Id, but this might be added in future versions.
//*
//* 3.3 BuffList API
//* To get the unit a bufflist is applied to you can use the following method:
//*
//* method getUnit takes nothing returns unit
//*
//* To iterate through a bufflist object you can use its iterator:
//*
//* method iterator takes nothing returns BuffListIterator
//*
//* The BuffListIterator then offers the following methods:
//*
//* method next takes nothing returns Buff
//* // Returns the next Buff in the BuffList.
//* // You should only use this function if ".hasNext()" returned "true".
//*
//* method hasNext takes nothing returns boolean
//* // Returns "true" if the next ".next()" call will return a valid Buff.
//* // Returns "false" if there are no more buffs in the BuffList.
//*
//* method hasNoNext takes nothing returns boolean
//* // Negated version of "hasNext" for easier use with "endloop".
//*
//* Note that the iterators are exclusively designed for use immediately after
//* creation. Iterators will not work correctly after the BuffList is altered.
//* After using the iterator dont forget to destroy it by calling its
//* ".destroy()" method.
//*
//* Lets make a short example of a buff that, when applied to a unit, destroys
//* all other buffs from that unit except itself and other buffs of this type:
//*
//* struct MyNewBuff
//* method onApply takes nothing returns nothing
//* local BuffListIterator iter = this.getParentBufflist().iterator()
//* local Buff b
//* loop
//* exitwhen iter.hasNoNext()
//* set b = iter.next()
//* if (b.getBuffTypeId() != this.getBuffTypeId()) then
//* call b.destroy()
//* endif
//* endloop
//* call iter.destroy()
//* endmethod
//* endstruct
//*
//* To count the amount of buffs of a specified type you can use the following
//* To count the amount of buffs of a certain type you can use the following
//* of a BuffList object:
//*
//* method countBuffsOfType takes Buff buff returns integer
//*
//* method countBuffsOfTypeId takes integer buffTypeId returns integer
//* // Use this if you only have the BuffType-Id to specify the buff type.
//*
//* 4. Stacking Policies
//* ====================
//* There are different possibilities to define how multiple buffs of the same
//* behave when applied to the same unit.
//* 4.1 Premade policies
//* For most situations selecting the right one of the premade stacking
//* policies will suffice. Currently there are:
//*
//* BUFF_POLICY_STACKING (default)
//* // An unlimited amout of buffs of this type can coexist on all units.
//*
//* BUFF_POLICY_LAST
//* // Buffs of this type are limited to one instance per target unit.
//* // The last applied buf will destroy existing ones.
//*
//* To define the policy for a bufftype you can just add the following method
//* to your buff struct which returns the the policy type:
//*
//* method getPolicy takes nothing returns integer
//* return BUFF_POLICY_LAST
//* endmethod
//*
//* Default is BUFF_POLICY_STACKING.
//*
//* There might be more premade policies in future versions.
//*
//* 4.2 Customize Stacking Behaviour
//* If you require a stacking behaviour that the premade policies cannot provide
//* you can customize it by defining the following function:
//*
//* method checkApply takes nothing returns boolean
//* return true // if you simply return true this is equal to BUFF_POLICY_STACKING
//* endmethod
//*
//* If defined this function runs before the buff is applied to unit and before
//* "onApply" fires.
//* In the method body you can check the bufflist for other instances of this
//* type, remove them, modify them etc.
//* If this function returns true the buff will get applied.
//* If this function returns false the buff will not get applied but als not
//* deleted. If you want it deleted you may call ".delete()" right before
//* the return statement.
//*
//* 5. Custom Members
//* =================
//* A quick way to add custom members to all buff types is the CustomMembers
//* module. Its defined in the top of this system and is automatically implemented
//* by all buff structs you define. Its method "initCustomMembers" can be used to
//* initialize default values to the custom members.
//*
//* Changelog
//* =========
//* Version 1.1:
//* - Making use of "UnitMakeAbilityPermanent"
//* - Added the optional possibility to remove the buff directly, avoiding the small
//* delay between the removal of the buff and the removal of the buff icon
//* Version 1.0:
//* - Initial Release
//*
//* Credits
//* =======
//* - Menag
//*
//*******************************************
//*
//* Add custom members here:
module CustomMembers
// Initialize default values to the custom members here:
method initCustomMembers takes nothing returns nothing
endmethod
endmodule
//*
//*
//*******************************************
/*
*****************************************************************************
*
* FUNCTIONS
*
* CreateTrack( string modelPath, real x, real y, real z, real facing ) returns Track
* - Creates a trackable of modelPath at coordinates ( x, y, z ) with
* - "facing" in degrees. Returns the trackable instance.
*
* CreateTrackForPlayer( string modelPath, real x, real y, real z, real facing, player who ) returns Track
* - Same as function above, but creates it for one player.
*
* RegisterAnyClickEvent( code c ) returns nothing
* RegisterAnyHoverEvent( code c ) returns nothing
* - Fires "c" when any trackable is clicked or hovered.
*
* RegisterClickEvent( Track obj, code c ) returns nothing
* RegisterHoverEvent( Track obj, code c ) returns nothing
* - Fires "c" when the trackable of "obj" is clicked or hovered respectively.
* RegisterInteractEvent( Track obj, code c ) returns nothing
* - Fires "c" when the trackable of "obj" is clicked or hovered.
* - Use GetTriggerEventId() == EVENT_GAME_TRACKABLE_TRACK
* to differentiate between the two event occurrences. This
* method is more efficient on handles than the two above.
*
* EnableTrackInstance( Track obj, boolean flag ) returns nothing
* - A disabled Track instance will not fire its events.
* - Track instances are enabled by default.
* IsTrackInstanceEnabled( Track obj ) returns boolean
* - Returns whether an instance is enabled.
*
* EVENT RESPONSES
*
* GetTriggerTrackInstance() returns Track
* - Returns the Track instance that had a player interaction.
* GetTriggerTrackable() returns trackable
* - Returns the trackable object that had a player interaction.
* GetTriggerTrackablePlayer() returns player
* - Returns the player that interacted with the trackable object.
*
*****************************************************************************
*
* struct Track
*
* static Track instance
* - The triggering instance of the event.
* static trackable object
* - The triggering trackable object of the event.
* static player tracker
* - The player who interacted with the trackable object.
*
* readonly real x
* readonly real y
* readonly real z
* readonly real facing
* readonly string model
* - Instance properties.
* - Warning: the invisible platform has a default z-value
* of 2.94794. So if you input 0 into the system, it'll
* end up ~3 units above the ground. For an alternative model,
* see: [url]http://www.hiveworkshop.com/forums/2661555-post54.html[/url]
*
* method operator enabled= takes boolean flag returns nothing
* method operator enabled takes nothing returns boolean
*
* static method create takes string modelPath, real x, real y, real z, real facing returns Track
* static method createForPlayer takes string modelPath, real x, real y, real z, real facing, player p returns Track
*
* static method registerAnyClick takes code c returns nothing
* static method registerAnyHover takes code c returns nothing
*
* method registerClick takes code c returns nothing
* method registerHover takes code c returns nothing
* method registerInteract takes code c returns nothing
*
* - All equivalent to their function counterparts.
*
*****************************************************************************
*
* Credits
* - Azlier: Trackable2 (inspiration)
* - Arhowk: bugfix
* - Dalvengyr: bugfix from a typo.
* - Uberplayer: bugfix; info on the invisible platform Z issue.
*
****************************************************************************/
We still can't check deindex event. No timer hmmmm how can we deindex Item @@ Use your func instead, hmmm I dont think that's a good wayWhat is wrong with this one?
EDIT:
JASS:globals item array udg_ItemIndex_Items integer udg_ItemIndex_NextIndex = 0 endglobals function CreateItemIndex takes item whichItem returns nothing loop set udg_ItemIndex_NextIndex = udg_ItemIndex_NextIndex +1 exitwhen udg_ItemIndex_Items[udg_ItemIndex_NextIndex] == null if udg_ItemIndex_NextIndex >= 8191 then set udg_ItemIndex_NextIndex = 0 endif endloop set udg_ItemIndex_Items[udg_ItemIndex_NextIndex] = whichItem call SetItemUserData(whichItem, udg_ItemIndex_NextIndex) endfunction function RemoveItemIndex takes item whichItem returns nothing set udg_II_AntiLoop = true set udg_ItemIndex_Items[GetItemUserData(whichItem)] = null call SetItemUserData(whichItem, 0) set udg_II_AntiLoop = false endfunction function SecureItemIndex takes item whichItem returns integer if udg_II_AntiLoop then return 0 endif set udg_II_AntiLoop = true if GetItemUserData(whichItem) == 0 then call ItemIndex_CreateIndex(whichItem) return udg_ItemIndex_NextIndex endif set udg_II_AntiLoop = false return 0 endfunction hook GetItemUserData SecureItemIndex
JASS:globals item array udg_ItemIndex_Items integer udg_ItemIndex_NextIndex = 0 endglobals function CreateItemIndex takes item whichItem returns nothing loop set udg_ItemIndex_NextIndex = udg_ItemIndex_NextIndex +1 exitwhen udg_ItemIndex_Items[udg_ItemIndex_NextIndex] == null if udg_ItemIndex_NextIndex >= 8191 then set udg_ItemIndex_NextIndex = 0 endif endloop set udg_ItemIndex_Items[udg_ItemIndex_NextIndex] = whichItem call SetItemUserData(whichItem, udg_ItemIndex_NextIndex) endfunction function RemoveItemIndex takes item whichItem returns nothing set udg_ItemIndex_Items[GetItemUserData(whichItem)] = null call SetItemUserData(whichItem, 0) endfunction function GetItemIndex takes item whichItem returns integer local integer id = GetItemUserData(whichItem) if id == 0 then call ItemIndex_CreateIndex(whichItem) return udg_ItemIndex_NextIndex endif return id endfunction
Still gotta do de-indexing.
Yeah, I'm all for "unified" documentation and demo rules...Personally I would campaign for providing a small demo code, along with a Jass submission.
Just basic API, so users get a quick guidance about how-to-use a resource.
For libraries where demo code doesn't make any sense ( Table, ... etc ) a small
demo like documention below the actual code ( like in Table ) turns out useful.
Exceptions may be snippets, which are understandable at a glance. ( AutoFly, LocZ, ...)
I think it's useful, because ultimately you want resources with short learning curve.
@Buff: Documentation is a text wall, penguin is cool and API is good.
Actually I like the system very much. For myself, I never code with stub methods though.
struct BonusType
GetRandomInt(min, max)
GetRandomReal(min, max)
struct ItemType
global ItemType SWORD = CreateItemType()
struct CustomItem
static method identify takes item whichItem returns CustomItem
CustomItem i
having a linked list which covers all rolled bonuses.method translate takes nothing returns nothing
has to run only once! Outcome is stored into a string member./n
CustomItem(i).descriptiom
CustomItem(i).list.first