//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
EquipmentEvent | real | No |
//TESH.scrollpos=9
//TESH.alwaysfold=0
library ErrorMessage /* v1.0.1.4
*************************************************************************************
*
* Issue THW Compliant Error Messages
*
************************************************************************************
*
* debug function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
* - In the event of an error the game will be permanently paused
*
* debug function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
*
************************************************************************************/
static if DEBUG_MODE then
private struct Fields extends array
static constant string COLOR_RED = "|cffff0000"
static constant string COLOR_YELLOW = "|cffffff00"
static string lastError = null
endstruct
private function Pause takes nothing returns nothing
call PauseGame(true)
endfunction
private function ThrowMessage takes string libraryName, string functionName, string objectName, integer objectInstance, string description, string errorType, string color returns nothing
local string str
local string color_braces = "|cff66FF99"
local string orange = "|cffff6600"
set str = "->\n-> " + color_braces + "{|r " + "Library" + color_braces + "(" + orange + libraryName + color_braces + ")"
if (objectName != null) then
if (objectInstance > 0) then
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + " (|rinstance = " + orange + I2S(objectInstance) + color_braces + ") )" + "|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
else
set str = str + "|r.Object" + color_braces + "(" + orange + objectName + color_braces + ")|r." + "Method" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
else
set str = str + "|r." + "Function" + color_braces + "(" + orange + functionName + color_braces + ")"
endif
set str = str + color_braces + " }|r " + "has thrown an exception of type " + color_braces + "(" + color + errorType + color_braces + ")|r."
set Fields.lastError = str + "\n->\n" + "-> " + color + description + "|r\n->"
endfunction
function ThrowError takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Error", Fields.COLOR_RED)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
call TimerStart(CreateTimer(), 0, true, function Pause)
set objectInstance = 1/0
endif
endfunction
function ThrowWarning takes boolean expression, string libraryName, string functionName, string objectName, integer objectInstance, string description returns nothing
if (Fields.lastError != null) then
set objectInstance = 1/0
endif
if (expression) then
call ThrowMessage(libraryName, functionName, objectName, objectInstance, description, "Warning", Fields.COLOR_YELLOW)
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,Fields.lastError)
set Fields.lastError = null
endif
endfunction
endif
endlibrary
//TESH.scrollpos=105
//TESH.alwaysfold=0
/*****************************************************
Equipment Manual:
Credits to The Witcher
for writting parts of this manual.
******************************************************
Table of content:
1. How to Set Up
2. How to register items
3. How to deal with special effects.
4. Hot to initialize Equipment
5. vJass information
--------- 1) How to Set Up (1 ---------
a) Copy all the abilities used by this system (here they are all under "human") or create your own ones
they are one ability for opening the inventory, one for closing, one which makes items "clickable", and for each empty slot a ability
b) Create all your items which can be equipped and give them as ability ONLY the [clickable item] ability
c) For each equipable item create a icon ability (best if you use "channel" as base ability)
2 things to be careful with:
Make sure that the item icon's Spell ID for each type of item is distinct
i.e. swords => acidbomb
shields => bloodlust
(be aware that the Cancel button has it's own Spell ID => absorb)
Make sure that every item ability icon has the X and Y button coordinates matching with the type of item it is.
i.e. Necklaces = 0,0 ; Helmets = 1,0 ; Gloves = 2,0
Necklace of Power = 0,0
Glory Necklace = 0,0
Bloodlust Helmet = 1,0
Green Cap = 1,0
Gloves of the IronDwarf = 2,0
d) Create abilities for each equipable item. DONT use the same ability twice on items of different equipment classes!!
Dont give those abilities to the items! They will be given to the equipping unit ingame by this system.
I advise you to take abilities which have no icon because it would show up on the owner
(of course this can be useful if an item should grant a active ability, for example!)
if you need an icon ability which should be invisible (aura for example), create a spellbook, add that ability to it,
disable the spellbook at map start and register the spellbook as an ability given by your item ;)
e) Initialize your items and classes
f). Make sure that all the adjustable variables in the setup part fit your map.
--------- 2) How to register items (2 ---------
This system isn't that perfect that it can do everything on its own.
At first you have to initialize your classes.
Do this via function:
function RegisterEquipmentClass takes integer class, integer emptyIcon returns nothing
- "class" is the item class your are registering.
- "icon" is the icon ability that indicates an empty slot.
You also have to register all items you want to be equip-able to this system.
With the following function you can do that:
call RegisterItem(itemId, abilities, icon, class, twohanded, animation)
"itemId" is of course the rawcode of the item.
"icon" is the ability shown inside the Equipment when the item is equipped.
"class" is the slot the item jumps in (be sure to adjust the x/y coordinates inside the object editor according
to the coordinates of the "empty slot" icon ability of the same class)
"twohanded" is a boolean (i think self explaining) !!works only for mainhand items!!
"animation" is a string which indicates the animation tag added when this item is equipped.
ATTENTION: offhand slot items animations always overwrite mainhand slot items animations.
User "null" or "" if you don't want the item to use an animationtag.
until now your items have no affixes when equipped...
you can add countless affixes to one item type!
to add one use:
call AddItemAbility( integer itemId, integer abilityId, integer level )
"itemId" is the rawcode of the item.
"abilityId" is the rawcode of the ability the unit gets when it equips this item.
"level" is the level the ability is set to, when equipped.
If you use library BonusMod, you can also add Bonuses to your item ids via:
function ItemAddBonus takes integer itemId, integer bonus, integer amount returns nothing
Example:
globals
integer AMULET_CLASS = 2
endglobals
...
call RegisterEquipmentClass('A00A', AMULET_CLASS)
...
call RegisterEquipmentItemId('I004', 'A008', AMULET_CLASS, false, "Alternate")
call AddItemAbility('I004', 'A002', 1)
--------- 3) How to deal with speciale effects (3 ---------
If you wish to add a special effect to an item, for example a sword model to your registered sword item
or an crown to a registered helm item, there are two ways to do that.
a) For special effects using only one attachPointName i.e. "Hand Left" or "Chest" you can
register the effect directly to the system.
Pro : You don't have to create an ability for each class and string file you want to add.
Does not allocate an etxra node within the ability stack.
Contra: Effects added via ability vanish instantly on unequip, while those added via function display a death animation.
i.e. the effect stays for 2 seconds after unequipping the item.
call AddItemSpecialEffect takes integer itemid, string file, string attachPointName returns nothing
"itemid" is the rawcode of the item the effect is associated with.
"file" is the string path of the model.
"attachPointName" is the attach point of the effect
ATTENTION: attachPointName has a mandatory part i.e. Hand, Chest, Foot, Head, ...
and an optional modifier i.e. left, right, first, second, ....
a full list can be found here: http://world-editor-tutorials.thehelper.net/attachments.php
Example:
call AddItemSpecialEffect('I00C', "war3mapImported\\SilverRazor.mdx", "Hand Left")
b) For complex special effects using multiple attach points create a new ability and
add the special effect to the ability within in object editor. Here you are able to define up to 6 target attachment points.
A good example is the effect for spiked carapace which requires 4 target attachment points.
--------- 4) How to initialize Equipment (4 ---------
call CreateEquipment ( unit )
Easy isn't it!
--------- 5) vJass Information (5 ---------
All wrapper function can be avoided by calling the struct methods directly.
The speed bonus however is negligible, so I recommend to stick to the wrapper functions,
as they are saver to use.
*/
//TESH.scrollpos=11
//TESH.alwaysfold=0
library Equipment /* v2.5.2
*************************************************************************************
*
* Library Equipment extends the Warcraft III inventory surface
* by using a dummy unit and ability buttons as user interface.
* Equipped items can have own ability- and/or abstract bonus lists
* as well as special effect models and animatags.
*
**************************************************************************************
*
* */ uses /*
*
* */ ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
* */ optional Bonus /* github.com/nestharus/JASS/tree/master/jass/Systems/Bonus
* */ optional ItemSet /* hiveworkshop.com/forums/spells-569/itempower-v1-1-0-0-a-243917/
* */ optional BonusMod /* wc3c.net/showthread.php?t=107940
* */ optional ItemPower /* hiveworkshop.com/forums/spells-569/itempower-v1-1-0-0-a-243917/
*
************************************************************************************
*
* 1. Import Instruction:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* • Copy library Equipment, ErrorMessage into your map.
* • Copy the Equipment dummy unit into your map.
* • For startes it's recommended to import the demo abilties aswell.
* • Read over the user settings. ( Below the API )
* • Within the EquipmentManual you'll find all information, about setting up Equipment.
*
* 2. Equipment with: ItemPower, Bonus and ItemSet ( any combination of these three )
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Everything is installed fully automatic, you don't have do anything.
* Just copy and paste the desired libraries into your map.
*
* 3. API
* ¯¯¯¯¯¯
*
* Creator/Destructor:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* function CreateEquipment takes unit whichUnit returns Equipment
* function DestroyEquipment takes unit whichUnit returns nothing
*
* Pre-game Functions:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* function RegisterEquipmentClass takes integer class, integer emptyIcon returns nothing
* function RegisterEquipmentItemId takes integer itemId, integer icon, integer class, boolean twohanded, string animation returns EquipmentItem
*
* function ItemIdAddAbility takes integer itemId, integer abilityId, integer level returns nothing
* function ItemIdAddBonus takes integer itemId, integer bonus, integer amount returns nothing
* • Each item id ( example: 'I00A') can have individual affixes.
* Affixes are added/removed when items are equipped/unequipped.
* Those affixes can be Warcraft III abilities or abstract bonuses from library BonusMod.
*
* function ItemIdAddSpecialEffect takes integer itemId, string file, string attachPointName returns nothing
*
* function EnableItemIdForUnitId takes integer itemId, integer unitId, boolean flag returns nothing
* • You can disable item ids for certain unit ids.
* Example: call EnableItemIdForUnitId('I00A', 'hfoo', false).
*
* Equipment events:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* function RegisterItemEquipEvent takes code func returns nothing
* function RegisterItemUnequipEvent takes code func returns nothing
*
* constant function GetTriggerItemId takes nothing returns integer
* constant function GetEquippingUnit takes nothing returns unit
*
* Ingame Functions:
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* function UnitHasEquipment takes unit whichUnit returns boolean
* function UnitItemIdInSlot takes unit whichUnit, integer class returns integer
* function IsEquipmentClassEmpty takes unit whichUnit, integer class returns boolean
* function UnequipItemClass takes unit whichUnit, integer class returns nothing
* function EquipItem takes unit whichUnit, item whichItem returns boolean
* function EquipItemIdById takes unit whichUnit, integer itemId returns boolean
* function UnitHasItemIdEquipped takes unit whichUnit, integer itemId returns boolean
*
* Special:
* ¯¯¯¯¯¯¯¯
* function EquipmentEnableAnimationTags takes unit whichUnit, boolean flag returns nothing
* • Some units shouldn't change animation tags, because it messes with their model.
* A good is example is the demon hunter with an active metamorphosis,
* because metamorphosis also uses animationtags.
*
**************************************************************************************/
// User settings:
// ==============
globals
//======================================================
// Item Class Configuration
//
// Do not forget to initialize your classes.
// For that use function RegisterEquipmentClass(class, icon)
// For example: call RegisterEquipmentClass(ITEM_CLASS_MAINHAND, 'A00D')
// Set the total amount of item classes in your map.
//
private constant integer ITEM_CLASSES = 9
// Mainhand and offhand are default classes which do not require any changes.
//
constant integer ITEM_CLASS_MAINHAND = 0
constant integer ITEM_CLASS_OFFHAND = 1
// Add as many further classes as desired. These below are just examples.
// For that start of with two and increase 1 per class.
//
constant integer ITEM_CLASS_AMULET = 2
constant integer ITEM_CLASS_ARMOR = 3
constant integer ITEM_CLASS_BOOTS = 4
constant integer ITEM_CLASS_GLOVES = 5
constant integer ITEM_CLASS_RING = 6
constant integer ITEM_CLASS_HELMET = 7
constant integer ITEM_CLASS_SPECIAL = 8
//======================================================
// Object Data Configuration
//
// Set the data value of the inventory dummy unit.
//
private constant integer INVENTORY_DUMMY_ID = 'h007'
// Set the data value of the ability to close the inventory.
//
private constant integer EXIT_ABILITY = 'A00G'
// Set the data value of the ability to open the inventory
//
private constant integer OPEN_ABILITY = 'A003'
// Set the data value of the ability, which gives the dummy the same inventory as the hero has.
private constant integer INVENTORY_ABILITY = 'AInv'
// Set the data value of the ability shown in the offhand slot, when a twohanded weapon is equipped.
//
private constant integer TWOHAND_ABILITY = 'A00Z'
private constant boolean SHOW_TWOHAND_ABILITY = true
//======================================================
// Misc Configuration
// This text is shown, when a unit wants to equip an item, which it isn't allowed to.
// For example: " can't equip " becomes ingame: Rifleman can't equip Giant Sword!
//
private constant string UNABLE_TO_EQUIP_STRING = " can't equip "
endglobals
//==============================================================================
// Functions, constants and variables used by Equipment. Make changes carefully.
//==============================================================================
globals
//======================================================
// Constants
//
// Constants concerning the interface error msg.
private constant string ERROR_MSG_PREFIX = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"
private constant real ERROR_MSG_POS_X = 0.52
private constant real ERROR_MSG_POS_Y = 0.96
private constant real ERROR_MSG_DURATION = 5.00
// Misc constants.
private constant integer ORDER_ID_STOP = 0xD0004
// Data storage constants.
private constant hashtable TABLE = InitHashtable()
// Event related constans.
private constant trigger EQUIP = CreateTrigger()
private constant trigger UNEQUIP = CreateTrigger()
//======================================================
// Variables
//
// Event related variables.
private unit eventUnit = null
private integer eventItem = 0
// Misc variables.
private sound error = null
endglobals
//***************************************************************************
//*
//* Unit Indexer Utility
//*
//***************************************************************************
// Allows you do decide whether or not the dummy unit should be indexed.
private function ToogleUnitIndexer takes boolean enable returns boolean
local boolean prev = true
static if LIBRARY_UnitIndexer then
set prev = UnitIndexer.enabled
set UnitIndexer.enabled = enable
endif
return prev
endfunction
//***************************************************************************
//*
//* Equipment Event
//*
//***************************************************************************
constant function GetEquippingUnit takes nothing returns unit
return eventUnit
endfunction
constant function GetTriggerItemId takes nothing returns integer
return eventItem
endfunction
function RegisterItemEquipEvent takes code func returns nothing
call TriggerAddCondition(EQUIP, Condition(func))
endfunction
function RegisterItemUnequipEvent takes code func returns nothing
call TriggerAddCondition(UNEQUIP, Condition(func))
endfunction
private function Fire takes trigger eventTrigger, unit source, integer itemId returns nothing
local integer prevItem = eventItem
local unit prevUnit = eventUnit
set eventItem = itemId
set eventUnit = source
call TriggerEvaluate(eventTrigger)
set eventItem = prevItem
set eventUnit = prevUnit
set prevUnit = null
endfunction
//***************************************************************************
//*
//* Custom Interface Error
//*
//***************************************************************************
private function InterfaceError takes unit source, item eventItem returns nothing
local string msg = ERROR_MSG_PREFIX + GetUnitName(source) + UNABLE_TO_EQUIP_STRING + GetItemName(eventItem) + "|r"
call DisplayTimedTextToPlayer(GetOwningPlayer(source), ERROR_MSG_POS_X, ERROR_MSG_POS_Y, ERROR_MSG_DURATION, msg)
if GetLocalPlayer() == GetOwningPlayer(source) then
call StartSound(error)
endif
endfunction
function UnitHasEquipment takes unit whichUnit returns boolean
return HaveSavedInteger(TABLE, GetHandleId(whichUnit), 0)
endfunction
//***************************************************************************
//*
//* Initialization Utility
//*
//***************************************************************************
private module Init
private static method onInit takes nothing returns nothing
call thistype.init()
endmethod
endmodule
//***************************************************************************
//*
//* Equipment Source Code
//*
//***************************************************************************
//===========================================================================
// Item id based bonuses:
// Data structure is a stack without destructors.
// The maximum allocation limit is 8190 instances.
// An ItemBonus can be of type ability or an abstract bonus from BonusMod.
private struct ItemBonus extends array
private static thistype collectionCount = 0
private static thistype nodeCount = 0
//===========================================================================
// Struct members
readonly thistype first
readonly thistype next
readonly integer bonus
readonly integer quantity
readonly boolean isAbility
method add takes integer bonus, integer quantity, boolean flag returns nothing
local thistype node = thistype(0).next
if node == 0 then
debug call ThrowError(nodeCount == 8191, "Equipment", "ItemBonus(this).add", "thistype", this, "Overflow!")
set node = nodeCount + 1
set nodeCount = node
else
set thistype(0).next = next
endif
set node.next = first
set first = node
set node.bonus = bonus
set node.quantity = quantity
set node.isAbility = flag
endmethod
static method create takes nothing returns thistype
local thistype this = thistype(0).first
if this == 0 then
debug call ThrowError(collectionCount == 8191, "Equipment", "ItemBonus.create", "thistype", this, "Overflow!")
set this = collectionCount + 1
set collectionCount = this
else
set thistype(0).first = first
endif
set first = 0
return this
endmethod
endstruct
//===========================================================================
// EquipmentItem struct to extends item data fields
private struct EquipmentItem extends array
private static integer alloc = ITEM_CLASSES + 1
//===========================================================================
// Struct members
readonly integer icon
readonly integer class
readonly integer itemId
readonly string tag
readonly string file
readonly string position
readonly boolean twohanded
readonly ItemBonus bonuses
static method operator [] takes integer itemId returns EquipmentItem
return LoadInteger(TABLE, itemId, 0)
endmethod
method destroyFx takes unit source returns nothing
local integer id = GetHandleId(source)
if HaveSavedHandle(TABLE, id, itemId) then
call DestroyEffect(LoadEffectHandle(TABLE, id, itemId))
call RemoveSavedHandle(TABLE, id, itemId)
endif
endmethod
method addFx takes unit source returns nothing
local integer id = GetHandleId(source)
if StringLength(file) > 0 and not HaveSavedHandle(TABLE, id, itemId) then
call SaveEffectHandle(TABLE, id, itemId, AddSpecialEffectTarget(file, source, position))
endif
endmethod
static method createClass takes integer class, integer icon returns thistype
debug call ThrowError(class < 0, "EquipmentItem", "createClass", "class", class, "The class argument must be greater than zero!")
debug call ThrowError(class > ITEM_CLASSES, "EquipmentItem", "createClass", "class", class, "Class out of bound! Increase the amount of ITEM_CLASSES in the Equipment setup!")
debug call ThrowError(HaveSavedInteger(TABLE, class, 0), "EquipmentItem", "createClass", "class", class, "Attempt to override class!")
call SaveInteger(TABLE, class, 0, 0)
set thistype(class).class = class
set thistype(class).icon = icon
return class
endmethod
method addBonus takes integer bonus, integer quantity, boolean isAbility returns nothing
if bonuses == 0 then
set bonuses = ItemBonus.create()
endif
call bonuses.add(bonus, quantity, isAbility)
endmethod
method removeBonuses takes unit source returns nothing
local ItemBonus node
if bonuses == 0 then
return
endif
set node = bonuses.first
loop
exitwhen node == 0
if node.isAbility then
call UnitRemoveAbility(source, node.bonus)
else
static if LIBRARY_Bonus then
call AddUnitBonus(source, node.bonus, -node.quantity)
elseif LIBRARY_BonusMod then
call AddUnitBonus(source, node.bonus, -node.quantity)
endif
endif
set node = node.next
endloop
endmethod
method addBonuses takes unit source returns nothing
local ItemBonus node
if bonuses == 0 then
return
endif
set node = bonuses.first
loop
exitwhen node == 0
if node.isAbility then
if UnitAddAbility(source, node.bonus) then
call UnitMakeAbilityPermanent(source, true, node.bonus)
call SetUnitAbilityLevel(source, node.bonus, node.quantity)
endif
else
static if LIBRARY_Bonus then
call AddUnitBonus(source, node.bonus, node.quantity)
elseif LIBRARY_BonusMod then
call AddUnitBonus(source, node.bonus, node.quantity)
endif
endif
set node = node.next
endloop
endmethod
method addSpecialEffect takes string str, string attachPointName returns nothing
debug call ThrowWarning(StringLength(file) > 0, "Equipment", "addSpecialEffect", "file", this, "Attempt to override" + file + " for [" + GetObjectName(itemId) + "] to " + str + "!")
set file = str
set position = attachPointName
endmethod
static method create takes integer itemTypeId, integer abilityIcon, integer itemClass, boolean isTwohanded, string animation returns thistype
local thistype this = thistype.alloc + 1
set thistype.alloc = integer(this)
debug call ThrowError(itemTypeId == 0, "Equipment", "EquipmentItem.create", "itemTypeId", 0, "Invalid item id ( 0 )")
debug call ThrowError(HaveSavedInteger(TABLE, itemTypeId, 0), "Equipment", "EquipmentItem.create", "itemTypeId", itemTypeId, "Attempt to double register [" + GetObjectName(itemTypeId) + "]")
debug call ThrowError(not HaveSavedInteger(TABLE, class, 0), "Equipment", "EquipmentItem.create", "class", class, "Unknown class for object [" + GetObjectName(itemTypeId) + "]")
call SaveInteger(TABLE, itemTypeId, 0, integer(this))
set itemId = itemTypeId
set class = itemClass
set icon = abilityIcon
set bonuses = 0
set twohanded = isTwohanded and itemClass == ITEM_CLASS_MAINHAND
if itemClass == ITEM_CLASS_MAINHAND or itemClass == ITEM_CLASS_OFFHAND then
set tag = animation
else
set tag = null
endif
return this
endmethod
endstruct
//===========================================================================
// Equipment struct
struct Equipment
private static EquipmentItem twohandIcon = 0
//===========================================================================
// Struct members
readonly unit source
readonly string animtag
readonly EquipmentItem array itemSlot [ITEM_CLASSES]
private unit dummy
private trigger trig
//===========================================================================
// Typecast unit to Equipment instance
static method operator [] takes unit whichUnit returns thistype
return LoadInteger(TABLE, GetHandleId(whichUnit), 0)
endmethod
method operator exists takes nothing returns boolean
return trig != null
endmethod
method hasItemId takes integer itemId returns boolean
local EquipmentItem node = EquipmentItem[itemId]
return itemSlot[node.class] == node
endmethod
//===========================================================================
// Syncing source and dummy inventories
private method sync takes nothing returns nothing
local integer index = UnitInventorySize(source)
loop
exitwhen index <= 0
set index = index - 1
call RemoveItem(UnitItemInSlot(dummy, index))
if UnitAddItemToSlotById(dummy, GetItemTypeId(UnitItemInSlot(source, index)), index) then
call SetItemDroppable(UnitItemInSlot(dummy, index), false)
endif
endloop
endmethod
private method refreshAnimation takes nothing returns nothing
local string s
if itemSlot[ITEM_CLASS_OFFHAND] != 0 and itemSlot[ITEM_CLASS_OFFHAND] != twohandIcon then
set s = itemSlot[ITEM_CLASS_OFFHAND].tag
else
set s = itemSlot[ITEM_CLASS_MAINHAND].tag
endif
call AddUnitAnimationProperties(source, animtag, false)
call AddUnitAnimationProperties(source, s, true)
set animtag = s
endmethod
private method equipEmptyClass takes integer class returns nothing
call UnitAddAbility(dummy, EquipmentItem(class).icon)
call UnitMakeAbilityPermanent(dummy, true, EquipmentItem(class).icon)
set itemSlot[class] = 0
endmethod
private method equipTwohandIcon takes nothing returns nothing
call UnitAddAbility(dummy, twohandIcon.icon)
call UnitMakeAbilityPermanent(dummy, true, twohandIcon.icon)
set itemSlot[ITEM_CLASS_OFFHAND] = twohandIcon
endmethod
//===========================================================================
// Equip and unequip
method unequip takes integer class returns boolean
local EquipmentItem eventItem = itemSlot[class]
debug call ThrowWarning(not exists, "Equipment", "unequip", "thistype", this, "Running unequip for a null instance!")
if exists then
if eventItem != 0 then
if eventItem.twohanded then
call UnitRemoveAbility(dummy, TWOHAND_ABILITY)
call equipEmptyClass(ITEM_CLASS_OFFHAND)
endif
call eventItem.removeBonuses(source)
call eventItem.destroyFx(source)
call UnitRemoveAbility(dummy, eventItem.icon)
call UnitAddItemById(source, eventItem.itemId)
static if LIBRARY_ItemPower then
call ItemPower_UnitRemoveItemId(source, eventItem.itemId)
endif
static if LIBRARY_ItemSet then
call ItemSet.onItemUnequip(source, eventItem.itemId)
endif
call Fire(UNEQUIP, source, eventItem.itemId)
set itemSlot[class] = 0
if LoadBoolean(TABLE, GetHandleId(source), 0) then
call refreshAnimation()
endif
return true
else
return UnitRemoveAbility(dummy, EquipmentItem(class).icon)
endif
endif
return false
endmethod
method equip takes integer itemId returns boolean
local EquipmentItem toEquip
local EquipmentItem mainEquip
debug call ThrowWarning(not exists, "Equipment", "equip", "thistype", this, "Running equip for a null instance!")
// Evaluate if the desired item id is a valid equipment item.
if not HaveSavedInteger(TABLE, itemId, 0) or not exists then
return false
endif
set toEquip = EquipmentItem[itemId]
set mainEquip = itemSlot[ITEM_CLASS_MAINHAND]
if mainEquip != 0 then
if toEquip.twohanded or mainEquip.twohanded and (toEquip.class == ITEM_CLASS_OFFHAND or toEquip.class == ITEM_CLASS_MAINHAND) then
call unequip(ITEM_CLASS_OFFHAND)
call unequip(ITEM_CLASS_MAINHAND)
if not toEquip.twohanded and toEquip.class == ITEM_CLASS_MAINHAND then
call equipEmptyClass(ITEM_CLASS_OFFHAND)
elseif toEquip.class == ITEM_CLASS_OFFHAND then
call equipEmptyClass(ITEM_CLASS_MAINHAND)
endif
endif
endif
call unequip(toEquip.class)
if toEquip.twohanded then
call unequip(ITEM_CLASS_OFFHAND)
static if SHOW_TWOHAND_ABILITY then
call equipTwohandIcon()
else
set itemSlot[ITEM_CLASS_OFFHAND] = twohandIcon
endif
endif
call toEquip.addFx(source)
call toEquip.addBonuses(source)
call UnitAddAbility(dummy, toEquip.icon)
call UnitMakeAbilityPermanent(dummy, true, toEquip.icon)
static if LIBRARY_ItemPower then
call ItemPower_UnitAddItemId(source, toEquip.itemId)
endif
static if LIBRARY_ItemSet then
call ItemSet.onItemEquip(source, toEquip.itemId)
endif
set itemSlot[toEquip.class] = toEquip
// Check if the unit is allowed to use custom animation tags.
if LoadBoolean(TABLE, GetHandleId(source), 0) then
call refreshAnimation()
endif
// Fire the event trigger.
call Fire(EQUIP, source, itemId)
return true
endmethod
//===========================================================================
// Native trigger event response
// EVENT_UNIT_SPELL_CAST
private static method unitSpellCast takes unit eventUnit, integer abilityId returns nothing
local thistype this = LoadInteger(TABLE, GetHandleId(eventUnit), 0)
local integer class
if abilityId == EXIT_ABILITY then
if GetLocalPlayer() == GetOwningPlayer(eventUnit) then
call ClearSelection()
call SelectUnit(source, true)
endif
elseif abilityId == OPEN_ABILITY then
call SetUnitX(dummy, GetUnitX(eventUnit))
call SetUnitY(dummy, GetUnitY(eventUnit))
if GetLocalPlayer() == GetOwningPlayer(eventUnit) then
call ClearSelection()
call SelectUnit(dummy, true)
endif
call sync()
// Check if the event ability has been casted by the dummy.
elseif GetUnitTypeId(eventUnit) == INVENTORY_DUMMY_ID then
set class = 0
loop
exitwhen itemSlot[class].icon == abilityId or class >= ITEM_CLASSES
set class = class + 1
endloop
if class < ITEM_CLASSES then
call unequip(class)
call equipEmptyClass(class)
call sync()
endif
endif
endmethod
// EVENT_UNIT_USE_ITEM
private static method unitUseItem takes unit eventUnit, item eventItem returns nothing
local integer itemId = GetItemTypeId(eventItem)
local integer slot
local thistype this
// Evaluate if the item is registered to Equipment.
if HaveSavedInteger(TABLE, itemId, 0) then
set this = LoadInteger(TABLE, GetHandleId(eventUnit), 0)
// Check if the item id is banned for the source unit type id.
if LoadBoolean(TABLE, itemId, GetUnitTypeId(source)) then
call InterfaceError(source, eventItem)
elseif eventUnit == source then
if not hasItemId(itemId) and equip(itemId) then
call RemoveItem(eventItem)
call sync()
endif
elseif eventUnit == dummy then
set eventUnit = source
set slot = UnitInventorySize(eventUnit)
loop
exitwhen slot <= 0
set slot = slot - 1
if itemId == GetItemTypeId(UnitItemInSlot(eventUnit, slot)) then
call UnitUseItem(eventUnit, UnitItemInSlot(eventUnit, slot))
call RemoveItem(eventItem)
exitwhen true
endif
endloop
set eventUnit = null
endif
endif
endmethod
//===========================================================================
// Bug fix for EVENT_UNIT_ISSUED_TARGET_ORDER:
// The dummy must not pick up items.
// Otherwise those items would be purged from the map.
private static method rescueItem takes unit eventUnit returns nothing
call PauseUnit(eventUnit, true)
call IssueImmediateOrderById(eventUnit, ORDER_ID_STOP)
call PauseUnit(eventUnit, false)
endmethod
private static method onEvent takes nothing returns boolean
if GetTriggerEventId() == EVENT_UNIT_USE_ITEM then
call unitUseItem(GetTriggerUnit(), GetManipulatedItem())
elseif GetTriggerEventId() == EVENT_UNIT_SPELL_CAST then
call unitSpellCast(GetTriggerUnit(), GetSpellAbilityId())
elseif GetOrderTargetItem() != null then
call rescueItem(GetTriggerUnit())
endif
return false
endmethod
//===========================================================================
// Creator & Destructor
method destroy takes nothing returns nothing
local integer index = 0
debug call ThrowError(not exists, "Equipment", "destroy", "not exists", this, "Attempt to destroy null instance!")
loop
exitwhen index >= ITEM_CLASSES
if itemSlot[index] != 0 then
call unequip(itemSlot[index].class)
endif
set index = index + 1
endloop
call AddUnitAnimationProperties(source, animtag, false)
call UnitRemoveAbility(source, OPEN_ABILITY)
call RemoveSavedInteger(TABLE, GetHandleId(source), 0)
call RemoveSavedInteger(TABLE, GetHandleId(dummy), 0)
call RemoveSavedBoolean(TABLE, GetHandleId(source), 0)
call RemoveUnit(dummy)
call TriggerClearConditions(trig)
call DestroyTrigger(trig)
set trig = null
set source = null
set dummy = null
call deallocate()
endmethod
static method create takes unit whichUnit returns Equipment
local thistype this = allocate()
local integer index = 0
local boolean reset = ToogleUnitIndexer(false)
debug call ThrowError(UnitHasEquipment(whichUnit), "Equipment", "create", "exists", 0, "An equipment already exists for " + GetUnitName(whichUnit))
debug call ThrowError(GetUnitTypeId(whichUnit) == 0, "Equipment", "create", "whichUnit", 0, "Invalid unit argument ( null )!")
set trig = CreateTrigger()
set dummy = CreateUnit(GetOwningPlayer(whichUnit), INVENTORY_DUMMY_ID, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.00)
set source = whichUnit
call ToogleUnitIndexer(reset)
call UnitAddAbility(dummy, INVENTORY_ABILITY)
call UnitAddAbility(dummy, EXIT_ABILITY)
call UnitMakeAbilityPermanent(dummy, true, INVENTORY_ABILITY)
call UnitMakeAbilityPermanent(dummy, true, EXIT_ABILITY)
call TriggerRegisterUnitEvent(trig, whichUnit, EVENT_UNIT_USE_ITEM)
call TriggerRegisterUnitEvent(trig, whichUnit, EVENT_UNIT_SPELL_CAST)
call TriggerRegisterUnitEvent(trig, dummy, EVENT_UNIT_USE_ITEM)
call TriggerRegisterUnitEvent(trig, dummy, EVENT_UNIT_SPELL_CAST)
call TriggerRegisterUnitEvent(trig, dummy, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(trig, Condition(function thistype.onEvent))
call UnitAddAbility(source, OPEN_ABILITY)
call UnitMakeAbilityPermanent(source, true, OPEN_ABILITY)
// Create references:
call SaveInteger(TABLE, GetHandleId(whichUnit), 0, integer(this))
call SaveInteger(TABLE, GetHandleId(dummy), 0, integer(this))
call SaveBoolean(TABLE, GetHandleId(whichUnit), 0, true)
loop
exitwhen index >= ITEM_CLASSES
call equipEmptyClass(index)
set index = index + 1
endloop
return this
endmethod
//===========================================================================
// Initialization
private static method init takes nothing returns nothing
set twohandIcon = EquipmentItem.createClass(ITEM_CLASSES, TWOHAND_ABILITY)
set error = CreateSoundFromLabel("InterfaceError", false, false, false, 10, 10)
endmethod
implement Init
endstruct
//===========================================================================
// Procedural Equipment API
function RegisterEquipmentClass takes integer class, integer emptyIcon returns nothing
call EquipmentItem.createClass(class, emptyIcon)
endfunction
function RegisterEquipmentItemId takes integer itemId, integer icon, integer class, boolean twohanded, string animation returns nothing
call EquipmentItem.create(itemId, icon, class, twohanded, animation)
endfunction
function CreateEquipment takes unit whichUnit returns Equipment
return Equipment.create(whichUnit)
endfunction
function GetUnitEquipment takes unit source returns Equipment
return Equipment[source]
endfunction
function DestroyEquipment takes unit source returns nothing
if UnitHasEquipment(source) then
call Equipment[source].destroy()
endif
endfunction
function UnitHasItemIdEquipped takes unit source, integer itemId returns boolean
return Equipment[source].hasItemId(itemId)
endfunction
function UnitItemIdInSlot takes unit source, integer class returns integer
return Equipment[source].itemSlot[class].itemId
endfunction
function ItemIdAddAbility takes integer itemId, integer abilityId, integer level returns nothing
call EquipmentItem[itemId].addBonus(abilityId, level, true)
endfunction
function ItemIdAddBonus takes integer itemId, integer bonus, integer amount returns nothing
call EquipmentItem[itemId].addBonus(bonus, amount, false)
endfunction
function ItemIdAddSpecialEffect takes integer itemId, string file, string attachPointName returns nothing
call EquipmentItem[itemId].addSpecialEffect(file, attachPointName)
endfunction
function EnableItemIdForUnitId takes integer itemId, integer unitId, boolean flag returns nothing
call SaveBoolean(TABLE, itemId, unitId, not flag)
endfunction
function IsEquipmentClassEmpty takes unit source, integer class returns boolean
return Equipment[source].itemSlot[class] == 0
endfunction
function UnitEquipItem takes unit source, item whichItem returns boolean
if Equipment[source].equip(GetItemTypeId(whichItem)) then
call RemoveItem(whichItem)
return true
endif
return false
endfunction
function UnitEquipItemById takes unit source, integer itemId returns boolean
return Equipment[source].equip(itemId)
endfunction
function UnitUnequipItemClass takes unit source, integer class returns boolean
return Equipment[source].unequip(class)
endfunction
function EquipmentEnableAnimationTags takes unit source, boolean flag returns nothing
call SaveBoolean(TABLE, GetHandleId(source), 0, flag)
endfunction
endlibrary
//TESH.scrollpos=84
//TESH.alwaysfold=0
scope SetupEquipment initializer Initialize
private struct InitFirst extends array
private static method init takes nothing returns nothing
/**
* Init all classes and items in a module initializer to avoid ingame malfunction.
*
* A class has an unique integer key and an empty slot icon ability.
*
* For easy access, define each class in the globals of the Equipment library as:
*
* --> constant integer ITEM_CLASS_EXAMPLE = x
*
*/
call RegisterEquipmentClass(ITEM_CLASS_MAINHAND, 'A00D')
call RegisterEquipmentClass(ITEM_CLASS_OFFHAND, 'A00E')
call RegisterEquipmentClass(ITEM_CLASS_AMULET, 'A00A')
call RegisterEquipmentClass(ITEM_CLASS_ARMOR, 'A007')
call RegisterEquipmentClass(ITEM_CLASS_BOOTS, 'A00F')
call RegisterEquipmentClass(ITEM_CLASS_GLOVES, 'A004')
call RegisterEquipmentClass(ITEM_CLASS_RING, 'A00C')
call RegisterEquipmentClass(ITEM_CLASS_HELMET, 'A00L')
call RegisterEquipmentClass(ITEM_CLASS_SPECIAL, 'A00M')
/**
* Item ids can be registered with RegisterEquipmentItemId
*
* itemid | icon | class | twohanded? | animationtag
*/
// Silversword
call RegisterEquipmentItemId('I004', 'A008', ITEM_CLASS_MAINHAND, false, "Alternate")
call ItemIdAddAbility('I004', 'A002', 1) // This is how you add an ability to an itemid.
call EnableItemIdForUnitId('I004','H001', false) // Forbid this item for 'H001'
/**
* In case you use library BonusMod, you can also do:
* call ItemIdAddBonus('I004', BONUS_LIFE, 1000.)
* call ItemIdAddBonus('I004', BONUS_DAMAGE, 200.)
*
* etc...
*/
//* Sacred stone
call RegisterEquipmentItemId('I000', 'A00K', ITEM_CLASS_SPECIAL, false, "")
call ItemIdAddAbility('I000', 'A000', 1)// + 10 Agility.
call ItemIdAddAbility('I000', 'A00Y', 1)
//* Slizer
call RegisterEquipmentItemId('I005' , 'A00H', ITEM_CLASS_MAINHAND, false, "Alternate")
call ItemIdAddAbility('I005', 'A005', 1)
//* Razor
call RegisterEquipmentItemId('I006', 'A00J', ITEM_CLASS_MAINHAND, true, "Channel")
call ItemIdAddAbility('I006', 'A005', 1)// 'A005' adds the SilverRazor modelfile to 'I006'!
//* As an alternative you could also use:
//-->> call AddItemIdSpecialEffect('I006', "war3mapImported\\SilverRazor.mdx", "hand left")
//* Pros and Cons of using the function call can be found in the Equipment Manual.
//* Shield
call RegisterEquipmentItemId('I001', 'A009', ITEM_CLASS_OFFHAND, false, "defend")
call ItemIdAddAbility('I001', 'A006', 1)
//* Amulet of darkness
call RegisterEquipmentItemId('I00A', 'A00B', ITEM_CLASS_AMULET, false, "")
//* Golems Skin
call RegisterEquipmentItemId('I003', 'A00N', ITEM_CLASS_ARMOR, false, "")
call ItemIdAddAbility('I003', 'A00U', 1)
//* Windwalkers
call RegisterEquipmentItemId('I002', 'A00O', ITEM_CLASS_BOOTS, false, "")
call ItemIdAddAbility('I002', 'A00X', 1)
//* Stonefists
call RegisterEquipmentItemId('I007', 'A00P', ITEM_CLASS_GLOVES, false, "")
call ItemIdAddAbility('I007', 'A00W', 1)
//* Arthuriel
call RegisterEquipmentItemId('I008', 'A00Q', ITEM_CLASS_RING, false, "")
call ItemIdAddAbility('I008', 'A00T', 1)
//* Mask of Horror
call RegisterEquipmentItemId('I009', 'A00R', ITEM_CLASS_HELMET, false, "")
call ItemIdAddAbility('I009', 'A00V', 1)
//* Harold's Cleaver Twohand Weapon
call RegisterEquipmentItemId('I00B' , 'A013', ITEM_CLASS_MAINHAND, true, "Alternate")
/**
* This way you attach special effects to weapons without using an ability.
*/
call ItemIdAddSpecialEffect('I00B', "war3mapImported\\SilverRazor.mdx", "hand left")
//** Harold's Ring
call RegisterEquipmentItemId('I00D', 'A014', ITEM_CLASS_RING, false, "")
call ItemIdAddAbility('I00D', 'A016', 1)//+ 10 Strenght
//* Harold's Horn
call RegisterEquipmentItemId('I00C', 'A015', ITEM_CLASS_SPECIAL, false, "")
call ItemIdAddAbility('I00C', 'A00Y', 1)//+ 10% AttackSpeed
endmethod
private static method onInit takes nothing returns nothing
call init()
endmethod
endstruct
private function Initialize takes nothing returns nothing
local unit demon = CreateUnit(Player(0), 'Edem', -14450, 13612, 0)
local unit test = CreateUnit(Player(0), 'H001', -14450, 13612, 0)
call CreateEquipment(test)
call CreateEquipment(demon)
//* Except for the village 255 animations model,
//* Animationtags can mess with your models.
//* You can disable this feature for a unit via:
call EquipmentEnableAnimationTags(demon, false)
//* The demo map includes ItemPower and ItemSet,
//* to show what can Equipment do.
call CreateUnitItemPower(demon)
call CreateUnitItemPower(test)
call SetHeroLevel(demon, 10, false)
endfunction
endscope
//TESH.scrollpos=311
//TESH.alwaysfold=0
library ItemSet /* v2.5
*************************************************************************************
*
* ItemSet allows to create "item sets" as known from various other games.
* Bonuses are applied to an unit, depending on the number of equipped items
* of that item set.
*
* One item set can have as many bonuses as you want.
* Upper bound for bonus instancing is 8190.
*
* Available bonuses are:
* 1. Abilities
*
* Optional bonuses, if libraries exist:
* 2. ItemPower ( BPower ) for custom item affixes.
* 3a. BonusMod ( Earthfury ) for excellent bonus handling ( Life, Mana, Agility, ... ).
* 3b. Bonus ( Nestharus ) equals BonusMod in API and functionality. I recommend "BonusMod" over "Bonus".
*
*************************************************************************************
*
* */ uses /*
*
* */ optional Bonus /* https://github.com/nestharus/JASS/tree/master/jass/Systems/Bonus
* */ optional BonusMod /* http://www.wc3c.net/showthread.php?t=107940
* */ optional ItemPower /* http://www.hiveworkshop.com/forums/spells-569/itempower-v1-1-0-0-a-243917/
* */ optional ErrorMessage /* https://raw.githubusercontent.com/nestharus/JASS/master/jass/Systems/ErrorMessage/script.j
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the ItemSet script into your map.
* Libraries listed as optional are not mandatory to have.
* ¯¯¯¯¯¯¯¯
* 2. API
* ¯¯¯¯¯¯
*
* readonly static constant integer COMPLETE = 0
* readonly static constant integer INCOMPLETE = 1
* function RegisterItemSetEvent takes integer whichEvent, code func returns nothing
* --> GetTriggerItemSet() returns the event ItemSet
* --> GetTriggerItemSetUnit() returns the event unit
* --> GetTriggerItemSetEventId() returns the event integer ( COMPLETE, INCOMPLETE )
*
*
* struct ItemSet extends array
*
* Creator:
* static method create takes string name, string model, string attachPointName returns thistype
* --> create a new ItemSet instance. Each item set has a name, an effect model and an attach point.
* - Can take null as effect model, if a specialeffect is not wanted.
*
* Adding items:
* method addItem takes integer itemId returns nothing
* --> Add an item to an ItemSet instance. One item id can only be linked to one ItemSet instance.
*
* Adding ItemSet Bonuses:
* method addAbility takes integer itemCount, integer abilityId returns nothing
* --> Adds an ability to the instace as ItemSet bonus.
* --> Arguments:
* - itemCount : How many items are required to apply the ability.
* - abilityId : Which ability
*
* Only available if library ItemPower exists:
* ¯¯¯¯¯¯¯¯¯
* method addItemPower takes integer itemCount, integer power, real value returns nothing
* --> Adds an ItemPower to the instace as ItemSet bonus.
* --> Arguments:
* - itemCount: How many items are required to apply the ItemPower.
* - power : Which ItemPower
* - value : The amount added to that units ItemPower.
*
* Only available if library Bonus exists:
* ¯¯¯¯¯
* method addBonus takes integer itemCount, integer bonus, integer value returns nothing
* --> Adds an Bonus to the instance as ItemSet bonus.
* --> Arguments:
* - itemCount: How many items are required to apply the Bonus.
* - bonus : Which Bonus
* - value : The amount added to that units Bonus.
*
* method addBuffIndicator takes integer itemCount, integer abilityId, integer buffId returns nothing
* --> Adds a buff placer to the instance.
* --> Arguments:
* - itemCount : How many items are required to apply the buff.
* - abilityId : Ability id of the buff placer.
* - buffId : Ability id of the buff.
*
* static method onItemUnequip takes unit whichUnit, integer itemId returns nothing
* static method onItemEquip takes unit whichUnit, integer itemId returns nothing
* --> Run these two using your inventory system.
* --> Can use the normal warcraft inventory aswell.
*
* static method operator [] takes integer itemId returns thistype
* --> Returns the corresponding ItemSet instance for an item id.
*
* readonly string name
* --> ItemSet name
*
* Unit Indexer implementation: ( Optional )
*
* static method onUnitDeindex takes unit whichUnit returns nothing
* --> Run this function on unit deindex event for best hashtable cleanup.
*
* 3. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
/**
* Identifies the type of bonus added to the ItemSet instance.
*/
key ITEM_SET_BONUS_TYPE_ABILITY
key ITEM_SET_BONUS_TYPE_ITEM_POWER
key ITEM_SET_BONUS_TYPE_BONUS
/**
* Should completed ItemSets add a special effect to the unit?
*/
private constant boolean ADD_SPECIAL_EFFECT_TARGET = true
endglobals
//ItemSet code. Edit on your own risk.
private struct ItemSetBonus extends array
// ItemSetBonus instances are constant data, so there is no need for a recycler.
private static integer alloc = 0
integer requiredItems
integer bonus // bonus can be of type ability, ItemPower or Bonus.
integer bonusType
//static if LIBRARY_ItemPower then
real itemPowerAmount
//endif
//static if LIBRARY_Bonus then
integer bonusAmount
//endif
// ItemSets are constant. Contact me, if you want to design them dynamically.
static method create takes integer counter, integer bonusTypeId, integer bonusId, real itemPowerValue, integer bonusValue returns ItemSetBonus
local ItemSetBonus this = ItemSetBonus(ItemSetBonus.alloc + 1)
set ItemSetBonus.alloc = integer(this)
static if LIBRARY_ErrorMessage then
debug call ThrowError((this == 8191), "ItemSetBonus", "create", "thistype", 8191, "Overflow!")
else
debug if (this == 8191) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 10000. ,SCOPE_PREFIX + "FATAL ERROR: Overflow! Stopping Thread!")
debug set this = 1/0
debug endif
endif
set requiredItems = counter
set bonus = bonusId
set bonusType = bonusTypeId
set itemPowerAmount = itemPowerValue
set bonusAmount = bonusValue
return this
endmethod
endstruct
struct ItemSet extends array
// Event id's.
readonly static constant integer COMPLETE = 0
readonly static constant integer INCOMPLETE = 1
// Event response variables.
// Wrapper functions are located below struct ItemSet.
readonly static unit triggerUnit = null
readonly static integer triggerEventId = -1
readonly static ItemSet triggerItemSet = 0
// Allocation:
private static integer alloc = 0
// Members:
readonly string name // Name of the ItemSet
private integer bonuses // Keep track of how many Bonuses an ItemSet has.
private integer maxItemCount // Keep track of how many items an ItemSet has.
private string sfx
private string point
private static hashtable hash = InitHashtable()
private static trigger incompleted = CreateTrigger()
private static trigger completed = CreateTrigger()
private static constant integer BUFF_BUFF_ID_OFFSET = 20000
private static constant integer BUFF_ABILITY_ID_OFFSET = 10000
static method registerEvent takes integer whichEvent, boolexpr expression returns nothing
if (whichEvent == ItemSet.COMPLETE) then
call TriggerAddCondition(ItemSet.completed, expression)
elseif (whichEvent == ItemSet.INCOMPLETE) then
call TriggerAddCondition(ItemSet.incompleted, expression)
debug else
debug call BJDebugMsg(SCOPE_PREFIX + "Error: Invalid ItemSet event index [" + I2S(whichEvent) + "]!")
endif
endmethod
static method operator [] takes integer itemId returns ItemSet
return ItemSet(LoadInteger(ItemSet.hash, 0, itemId))
endmethod
private method fire takes trigger whichTrigger, unit whichUnit, integer id returns nothing
local ItemSet prevItemSet = ItemSet.triggerItemSet
local unit prevUnit = ItemSet.triggerUnit
local integer prevEv = ItemSet.triggerEventId
set ItemSet.triggerItemSet = this
set ItemSet.triggerUnit = whichUnit
set ItemSet.triggerEventId = id
call TriggerEvaluate(whichTrigger)
set ItemSet.triggerItemSet = prevItemSet
set ItemSet.triggerUnit = prevUnit
set ItemSet.triggerEventId = prevEv
set prevUnit = null
endmethod
private method itemSetComplete takes unit whichUnit returns nothing
if (sfx != null) then
static if ADD_SPECIAL_EFFECT_TARGET then
call SaveEffectHandle(hash, GetHandleId(whichUnit), this, AddSpecialEffectTarget(sfx, whichUnit, point))
else
call DestroyEffect(AddSpecialEffectTarget(sfx, whichUnit, point))
endif
endif
call fire(ItemSet.completed, whichUnit, ItemSet.COMPLETE)
endmethod
private method itemSetIncomplete takes unit whichUnit returns nothing
static if ADD_SPECIAL_EFFECT_TARGET then
local integer id = GetHandleId(whichUnit)
if HaveSavedHandle(hash, id, this) then
call DestroyEffect(LoadEffectHandle(hash, id, this))
call RemoveSavedHandle(hash, id, this)
endif
endif
call fire(ItemSet.incompleted, whichUnit, ItemSet.INCOMPLETE)
endmethod
private method remove takes unit whichUnit, integer itemCounter returns nothing
local integer index = 0
local ItemSetBonus toRemove
local integer child
loop
exitwhen index == bonuses
set toRemove = ItemSetBonus(LoadInteger(hash, this, index))
if (toRemove.requiredItems == itemCounter) then
if (toRemove.bonusType == ITEM_SET_BONUS_TYPE_ABILITY) then
call UnitRemoveAbility(whichUnit, toRemove.bonus)
// Impossible to exclude via optional textmacro/module, ....
elseif (toRemove.bonusType == ITEM_SET_BONUS_TYPE_ITEM_POWER) then
static if LIBRARY_ItemPower then
call AddUnitItemPower(whichUnit, toRemove.bonus, -toRemove.itemPowerAmount)
endif
elseif (toRemove.bonusType == ITEM_SET_BONUS_TYPE_BONUS) then
static if LIBRARY_Bonus then
call AddUnitBonus(whichUnit, toRemove.bonus, -toRemove.bonusAmount)
elseif LIBRARY_BonusMod then
call AddUnitBonus(whichUnit, toRemove.bonus, -toRemove.bonusAmount)
endif
endif
endif
set index = index + 1
endloop
set child = itemCounter + BUFF_ABILITY_ID_OFFSET
if HaveSavedInteger(hash, this, child) then
call UnitRemoveAbility(whichUnit, LoadInteger(hash, this, child))
call UnitRemoveAbility(whichUnit, LoadInteger(hash, this, itemCounter + BUFF_BUFF_ID_OFFSET))
endif
set child = itemCounter + BUFF_ABILITY_ID_OFFSET - 1
if HaveSavedInteger(hash, this, child) then
call UnitAddAbility(whichUnit, LoadInteger(hash, this, child))
call UnitMakeAbilityPermanent(whichUnit, true, LoadInteger(hash, this, itemCounter + BUFF_ABILITY_ID_OFFSET - 1))
endif
if (itemCounter == maxItemCount) then
call itemSetIncomplete(whichUnit)
endif
endmethod
static method onItemUnequip takes unit whichUnit, integer itemId returns nothing
local integer id = GetHandleId(whichUnit)
local ItemSet this
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((id == 0), "ItemSet", "onItemUnequip", "id", id, "Invalid Unit Handle (null)!")
endif
if (HaveSavedInteger(hash, 0, itemId)) and (id != 0) then
call SaveInteger(hash, id, itemId, LoadInteger(hash, id, itemId) - 1)
if (LoadInteger(hash, id, itemId) == 0) then
call RemoveSavedInteger(hash, id, itemId)
set this = LoadInteger(ItemSet.hash, 0, itemId)
call remove(whichUnit, LoadInteger(hash, id, this))
call SaveInteger(hash, id, this, LoadInteger(hash, id, this) - 1)
endif
endif
endmethod
private method add takes unit whichUnit, integer itemCounter returns nothing
local integer index = 0
local ItemSetBonus toAdd
local integer child
loop
// Loop through all ItemSetBonuses created for this ItemSet.
exitwhen index == bonuses
//Load the current Bonus.
set toAdd = ItemSetBonus(LoadInteger(hash, this, index))
//A Bonus is only applied if the numer of carried items equals its counter.
if (toAdd.requiredItems == itemCounter) then
if (toAdd.bonusType == ITEM_SET_BONUS_TYPE_ABILITY) then
call UnitAddAbility(whichUnit, toAdd.bonus)
call UnitMakeAbilityPermanent(whichUnit, true, toAdd.bonus)
elseif (toAdd.bonusType == ITEM_SET_BONUS_TYPE_ITEM_POWER) then
static if LIBRARY_ItemPower then
call AddUnitItemPower(whichUnit, toAdd.bonus, toAdd.itemPowerAmount)
endif
elseif (toAdd.bonusType == ITEM_SET_BONUS_TYPE_BONUS) then
static if LIBRARY_Bonus then
call AddUnitBonus(whichUnit, toAdd.bonus, toAdd.bonusAmount)
elseif LIBRARY_BonusMod then
call AddUnitBonus(whichUnit, toAdd.bonus, toAdd.bonusAmount)
endif
endif
endif
//Move on to the next loop index.
set index = index + 1
endloop
set child = itemCounter + BUFF_ABILITY_ID_OFFSET
if HaveSavedInteger(hash, this, child) then
call UnitAddAbility(whichUnit, LoadInteger(hash, this, child))
call UnitMakeAbilityPermanent(whichUnit, true, LoadInteger(hash, this, child))
endif
// Check if prev item amount holds a buff.
set child = itemCounter + BUFF_ABILITY_ID_OFFSET - 1
if HaveSavedInteger(hash, this, child) then
call UnitRemoveAbility(whichUnit, LoadInteger(hash, this, child))
call UnitRemoveAbility(whichUnit, LoadInteger(hash, this, itemCounter + BUFF_BUFF_ID_OFFSET - 1))
endif
//Check if the ItemSet is completed.
if (itemCounter == maxItemCount) then
call itemSetComplete(whichUnit)
endif
endmethod
static method onItemEquip takes unit whichUnit, integer itemId returns nothing
local integer id = GetHandleId(whichUnit)
local ItemSet this
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((id == 0), "ItemSet", "onItemEquip", "id", id, "Invalid Unit Handle ( null )!")
endif
// Is item in database and the unit exists.
if (HaveSavedInteger(hash, 0, itemId)) and (id != 0) then
// Keep track how often that item was equipped.
call SaveInteger(hash, id, itemId, LoadInteger(hash, id, itemId) + 1)
// Bonuses are only applied on the first item of that type.
if (LoadInteger(hash, id, itemId) == 1) then
set this = LoadInteger(hash, 0, itemId)
call SaveInteger(hash, id, this, LoadInteger(hash, id, this) + 1)
//Apply bonuses associated with that ItemSet counter.
call add(whichUnit, LoadInteger(hash, id, this))
endif
endif
endmethod
method addBuffIndicator takes integer itemCount, integer abilityId, integer buffId returns nothing
call SaveInteger(hash, this, itemCount + BUFF_ABILITY_ID_OFFSET, abilityId)
call SaveInteger(hash, this, itemCount + BUFF_BUFF_ID_OFFSET, buffId)
endmethod
method addBonus takes integer itemCount, integer bonusType, integer amount returns nothing
call SaveInteger(hash, this, bonuses, ItemSetBonus.create(itemCount, ITEM_SET_BONUS_TYPE_BONUS, bonusType, 0., amount))
set bonuses = bonuses + 1
endmethod
method addItemPower takes integer itemCount, integer power, real value returns nothing
call SaveInteger(hash, this, bonuses, ItemSetBonus.create(itemCount, ITEM_SET_BONUS_TYPE_ITEM_POWER, power, value, 0))
set bonuses = bonuses + 1
endmethod
method addAbility takes integer itemCount, integer abilityId returns nothing
call SaveInteger(hash, this, bonuses, ItemSetBonus.create(itemCount, ITEM_SET_BONUS_TYPE_ABILITY, abilityId, 0., 0))
set bonuses = bonuses + 1
endmethod
method addItem takes integer itemId returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(HaveSavedInteger(hash, 0, itemId), "ItemSet", "addItem", "itemId", this, "Adding Item Ids To Two Different ItemSets Is Not Allowed: " + GetObjectName(itemId))
debug call ThrowError(itemId == 0 , "ItemSet", "addItem", "itemId", this, "Invalid Item Id (0)!")
endif
call SaveInteger(hash, 0, itemId, this)
set maxItemCount = maxItemCount + 1
endmethod
static method create takes string itemSetName, string modelName, string attachPointName returns thistype
local ItemSet this = ItemSet(ItemSet.alloc + 1)
set ItemSet.alloc = integer(this)
static if LIBRARY_ErrorMessage then
debug call ThrowError((this == 8191), "ItemSet", "create", "thistype", 8191, "Overflow!")
else
debug if (this == 8191) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 10000. ,SCOPE_PREFIX + "FATAL ERROR: Overflow! Stopping Thread!")
debug set this = 1/0
debug endif
endif
set name = itemSetName
set sfx = modelName
set point = attachPointName
return this
endmethod
/**
* Call this method for best hashtable cleanup.
*/
static method onUnitDeindex takes unit whichUnit returns nothing
local integer id = GetHandleId(whichUnit)
if (0 != GetUnitTypeId(whichUnit)) and (0 != id) then
call FlushChildHashtable(thistype.hash, id)
endif
endmethod
endstruct
/**
* Wrapper functions.
*/
function GetTriggerItemSetEventId takes nothing returns integer
return ItemSet.triggerEventId
endfunction
function GetTriggerItemSetUnit takes nothing returns unit
return ItemSet.triggerUnit
endfunction
function GetTriggerItemSet takes nothing returns ItemSet
return ItemSet.triggerItemSet
endfunction
function RegisterItemSetEvent takes integer whichEvent, code func returns nothing
call ItemSet.registerEvent(whichEvent, Condition(func))
endfunction
endlibrary
//TESH.scrollpos=187
//TESH.alwaysfold=0
library ItemPower/* v4.4
*************************************************************************************
*
* ItemPower allows to assigns reals to items.
* The attaches there reals to units wearing such items.
* This way items can have a huge range of bonus attributes,
* normally not provided by warcraft 3.
*
* Examples:
* ¯¯¯¯¯¯¯¯¯
* Block amount, Block chance, Magic find, Bonus exp, Resistance, ...
*
* When an unit equips or unequips an item, these values will get summarized for that unit.
* By default the operation is either + or -
*
* One item can have as many "powers" as you want.
*
* Can be used independant from items by using the AddUnitItemPower and SetUnitItemPower functions.
*
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
*
************************************************************************************
*
* 1. Import instruction
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the ItemPower script into your map.
* ErrorMessage is optional, but useful to have.
* ¯¯¯¯¯¯¯¯
* 2. API
* ¯¯¯¯¯¯
*
* function CreateItemPower takes integer power, real base returns integer
* - Create an unique ItemPower index.
* - Start at 1 and use the next even number per new power. Ex:(1, 2, 3, ...)
* - Negative or 0 as index is forbidden!
*
* function AddItemIdItemPower takes integer itemId, integer power, real amount returns nothing
* - Assigns an unique power value to an item id for an ItemPower.
* --> call AddItemIdItemPower('I000', 1, 20.)
*
* function CreateUnitItemPower takes unit whichUnit returns nothing
* - Register an unit to ItemPower.
* - Properly sets all base values for that unit.
* - Required because powers can have base values beyond 0.
*
* function DestroyUnitItemPower takes unit whichUnit returns nothing
* - Flushes the child key. Memory management.
* - Use this if you wish to keep the hashtable cleaner, once an unit is no longer used.
*
* function GetUnitItemPower takes unit whichUnit, integer power returns real
* - Returns total ItemPower amount for an unit's power.
* --> set damage = GetUnitItemPower(unit, 1)
*
* public function UnitAddItemId takes unit whichUnit, integer itemId returns nothing
* public function UnitRemoveItemId takes unit whichUnit, integer itemId returns nothing
* - Removes/Adds all assigned power values of an item to the unit.
* - Use this function on item equip/unequip events.
* --> ItemPower_UnitAddItemId(unit, 'I00I')
*
* function SetUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
* function AddUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
* - Change ItemPower values independant from item ids. (per spell, abiilty, ...)
* - extends ItemPower usage to be very flexible and useful.
* --> AddUnitItemPower(unit, 1, 10)
*
* function UnitUsesItemPower takes unit whichUnit returns boolean
* function ItemIdUsesItemPower takes integer itemId returns boolean
*
*/
//ItemPower code. Edit on your own risk.
globals
private constant hashtable TABLE = InitHashtable()
private integer powers = -1
endglobals
function ItemIdUsesItemPower takes integer itemId returns boolean
return HaveSavedBoolean(TABLE, itemId, 0)
endfunction
function UnitUsesItemPower takes unit whichUnit returns boolean
return HaveSavedReal(TABLE, GetHandleId(whichUnit), powers) and (GetUnitTypeId(whichUnit) != 0)
endfunction
function GetUnitItemPower takes unit whichUnit, integer power returns real
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "GetUnitItemPower", "whichUnit", 0, GetUnitName(whichUnit) + " Is not using ItemPower!")
debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "GetUnitItemPower", "power", power, "Attempt To get an invalid ItemPower!")
endif
return LoadReal(TABLE, GetHandleId(whichUnit), power)
endfunction
function AddUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
local integer id = GetHandleId(whichUnit)
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "AddUnitItemPower", "whichUnit", id, GetUnitName(whichUnit) + " Is not using ItemPower!")
debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "AddUnitItemPower", "power", power, "Attempt to manipulate an invalid ItemPower!")
endif
if (UnitUsesItemPower(whichUnit)) then
call SaveReal(TABLE, id, power, LoadReal(TABLE, id, power) + amount)
endif
endfunction
function SetUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
local integer id = GetHandleId(whichUnit)
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "SetUnitItemPower", "whichUnit", id, GetUnitName(whichUnit) + " Is Not Using ItemPower!")
debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "SetUnitItemPower", "power", power, "Attempt To Manipulate An Invalid ItemPower!")
endif
if (UnitUsesItemPower(whichUnit)) then
call SaveReal(TABLE, id, power, amount)
endif
endfunction
public function UnitRemoveItemId takes unit whichUnit, integer itemId returns nothing
local integer next = LoadInteger(TABLE, itemId, 0)
local integer id = GetHandleId(whichUnit)
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "ItemPower_RemoveUnitItemId", "whichUnit", 0, GetUnitName(whichUnit) + " is not using ItemPower!")
endif
//* Minimal overhead for important safety.
if UnitUsesItemPower(whichUnit) and ItemIdUsesItemPower(itemId) then
loop
exitwhen (0 == next)
call SaveReal(TABLE, id, next, LoadReal(TABLE, id, next) - LoadReal(TABLE, itemId, next))
set next = LoadInteger(TABLE, itemId, next)
endloop
endif
endfunction
public function UnitAddItemId takes unit whichUnit, integer itemId returns nothing
local integer next = LoadInteger(TABLE, itemId, 0)
local integer id = GetHandleId(whichUnit)
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "ItemPower_AddUnitItemId", "whichUnit", id, GetUnitName(whichUnit) + " is not using ItemPower!")
endif
if UnitUsesItemPower(whichUnit) and ItemIdUsesItemPower(itemId) then
loop
exitwhen (0 == next)
call SaveReal(TABLE, id, next, LoadReal(TABLE, id, next) + LoadReal(TABLE, itemId, next))
set next = LoadInteger(TABLE, itemId, next)
endloop
endif
endfunction
//* Flushes the child key.
function DestroyUnitItemPower takes unit whichUnit returns nothing
if UnitUsesItemPower(whichUnit) then
call FlushChildHashtable(TABLE, GetHandleId(whichUnit))
debug else
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((true), "ItemPower", "DestroyUnitItemPower", "whichUnit", GetHandleId(whichUnit), GetUnitName(whichUnit) + " is not using ItemPower!")
endif
endif
endfunction
function CreateUnitItemPower takes unit whichUnit returns nothing
local integer id = GetHandleId(whichUnit)
local integer next = 0
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((GetUnitTypeId(whichUnit) == 0), "ItemPower", "CreateUnitItemPower", "GetUnitTypeId", 0, "Invalid unit handle ( null )!")
endif
if (GetUnitTypeId(whichUnit) == 0) then
return
endif
loop
exitwhen next > powers
call SaveReal(TABLE, id, next, LoadReal(TABLE, 0, next))
set next = next + 1
endloop
endfunction
function AddItemIdItemPower takes integer itemId, integer power, real amount returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError((itemId == 0), "ItemPower", "AddItemIdItemPower", "itemId", 0, "Invalid ItemId ( 0 )!")
else
if (itemId == 0) then
return
endif
endif
if not HaveSavedReal(TABLE, itemId, power) then
call SaveInteger(TABLE, itemId, power, LoadInteger(TABLE, itemId, 0))
call SaveInteger(TABLE, itemId, 0, power)
endif
call SaveReal(TABLE, itemId, power, amount)
call SaveBoolean(TABLE, itemId, 0, true)
endfunction
function CreateItemPower takes integer power, real base returns integer
static if LIBRARY_ErrorMessage then
debug call ThrowWarning((HaveSavedReal(TABLE, 0, power)), "ItemPower", "CreateItemPower", "power", power, "Attempt to override an existing ItemPower!")
debug call ThrowError((0 >= power), "ItemPower", "CreateItemPower", "power", power, "ItemPower indexes must be greater than zero!")
else
if (0 >= power) then
debug call BJDebugMsg(SCOPE_PREFIX + "ERROR: CreateItemPower, Invalid ItemPower [" + I2S(power) + "]!!!")
return power
endif
endif
if (powers < power) then
set powers = power
endif
call SaveReal(TABLE, 0, power, base)
return power
endfunction
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
//* This example is not MUI!!!
//* You can use ItemPower values for many purposes.
//* Examples:
//* =========
//* 1. Design your own custom damage system.
//* 2. Check for them within spell and modify.
//* 3. ......
scope Board initializer Init
globals
private multiboard board = null
endglobals
private function SetText takes integer col, integer row, string str returns nothing
local multiboarditem i = MultiboardGetItem(board, row, col)
call MultiboardSetItemValue(i, str)
call MultiboardReleaseItem(i)
set i = null
endfunction
private function OnEvent takes nothing returns boolean
local unit u = GetEquippingUnit()
call SetText(1, 0, I2S(R2I(GetUnitItemPower(u, 1))) + "-" + I2S(R2I(GetUnitItemPower(u, 2))))
call SetText(1, 1, I2S(R2I(GetUnitItemPower(u, 3) + .5)) + "%")
call SetText(1, 2, I2S(R2I(GetUnitItemPower(u, 4)*100. + .5)) + "%")
call SetText(1, 3, I2S(R2I(GetUnitItemPower(u, 5) + .5)) + "%")
call SetText(1, 4, I2S(R2I(GetUnitItemPower(u, 6))) + "-" + I2S(R2I(GetUnitItemPower(u, 7))))
set u = null
return false
endfunction
private function Callback takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
set board = CreateMultiboardBJ(2, 5, "Attributes")
call MultiboardSetItemsWidth(board, .11)
call MultiboardMinimize(board, false)
call MultiboardSetItemsStyle(board, true, false)
call SetText(0, 0, "Damage: ")
call SetText(0, 1, "Critical Hit Chance: ")
call SetText(0, 2, "Critical Hit Damage: ")
call SetText(0, 3, "Block Chance: ")
call SetText(0, 4, "Block Amount: ")
call SetText(1, 0, "0-0")
call SetText(2, 1, "0%")
call SetText(3, 2, "0%")
call SetText(4, 3, "0%")
call SetText(5, 4, "0-0")
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 0., false, function Callback)
call RegisterItemEquipEvent(function OnEvent)
call RegisterItemUnequipEvent(function OnEvent)
endfunction
endscope
//TESH.scrollpos=17
//TESH.alwaysfold=0
scope SetupSetItems initializer Init
//____________
//EXAMPLE CODE
//¯¯¯¯¯¯¯¯¯¯¯¯
// Step 1: Setup item sets
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// API:
// static method create takes string itemSetName, string model, string attachPointName returns thistype
// method addItem takes integer itemid returns nothing
// method addAbility takes integer counter, integer abilityId returns nothing
// method addItemPower takes integer counter, integer power, real value returns nothing
//
// static method operator [] takes integer itemid returns thistype
// readonly string name
//
// static boolean enabled
// function RegisterItemSetEvent takes integer whichEvent, code func returns nothing
// --> GetTriggerItemSet()
// --> GetTriggerItemSetUnit()
//
private function ItemSetCompleteEvent takes nothing returns boolean
local ItemSet s = GetTriggerItemSet()
local string msg = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"
local player p = GetOwningPlayer(GetTriggerItemSetUnit())
call DisplayTimedTextToPlayer(p, .52, .96, 5., msg + GetUnitName(ItemSet.triggerUnit) + " has completed|r " + s.name + "!")
// Play sound?
// Run code?
return false
endfunction
private function ItemSetIncompleteEvent takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
// Step 2: Create a new set
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
local ItemSet iSet = ItemSet.create("|cff00ff00Harold's Kitchen|r", "Abilities\\Spells\\NightElf\\Immolation\\ImmolationTarget.mdl", "origin")
// Step 3: Add items to this set
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
call iSet.addItem('I00B')//Harold's Cleaver
call iSet.addItem('I00D')//Harold's Ring
call iSet.addItem('I00C')//Harold's Horn
// Step 4: Add bonuses
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Example 1: Add an ability.
// --> with two items worn 'A017' will be added.
call iSet.addAbility(2, 'A017')
// Example 2: Add an ItemPower. (Requires library ItemPower)
// --> with three items worn power 3 will be enhanced by 1.50
// --> According to the example settings of ItemPower this is 150% extra Critical Hit Damage.
call iSet.addItemPower(3, 4, 1.5)
// Example 3: Add a buff place.
// --> with two items worn 'A012' will be shown.
call iSet.addBuffIndicator(1, 'A010', 'B000')
call iSet.addBuffIndicator(2, 'A012', 'B001')
call iSet.addBuffIndicator(3, 'A018', 'B002')
//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ DONE ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Additional API:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//set iSet = ItemSet['I00B'] --> get the ItemSet instances of Harold's Kitchen from itemid Harold's Cleaver.
//call BJDebugMsg(iSet.name) --> display the set name --> DEFAULT_SET_COLOR + "Harold's Kitchen"
call RegisterItemSetEvent(ItemSet.COMPLETE, function ItemSetCompleteEvent)// --> Fires when a set is completed
call RegisterItemSetEvent(ItemSet.INCOMPLETE, function ItemSetIncompleteEvent)// --> Fires when a set is incompleted
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ItemPowerSetup
//____________
//EXAMPLE CODE
//¯¯¯¯¯¯¯¯¯¯¯¯
/**
* Module initializer to avoid malfunction.
*/
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct InitFirst extends array
private static method init takes nothing returns nothing
/**
* Create all desired ItemPowers. Can be of any type you want.
* You can use any index which is greater than 0 for ItemPower indexes.
*
* Damage, Crit, ... are just made up examples.
*/
call CreateItemPower(1, 0.)// Minimum Damage - Starts of with 0.00
call CreateItemPower(2, 0.)// Maximum Damage - Starts of with 0.00
call CreateItemPower(3, 0.)// Critical Hit Chance - Starts of with 0.00 --> GetRandomReal(GetUnitItemPower(who, 2), 100)
call CreateItemPower(4, 0.)// Critical Hit Damage - Starts of with 0.00 --> Damage + Damage*GetUnitItemPower(who, 3)
call CreateItemPower(5, 0.)// Block Chance
call CreateItemPower(6, 0.)// Minimum Block Amount
call CreateItemPower(7, 0.)// Maximum Block Amount
/**
* How to register items to ItemPower:
*
* function ItemPower_RegisterItem takes integer whichItem, integer power, real amount returns nothing
*/
//SilverSword gets 20 - 25 --> Avarage 22.50
call AddItemIdItemPower('I004', 1, 20.)
call AddItemIdItemPower('I004', 2, 25.)
//Slizer gets 10 - 27 Damage --> Avarage 18.5
//Also 15% Critical Hit Chance and 10% Critical Hit Damage
call AddItemIdItemPower('I005', 1, 10.)
call AddItemIdItemPower('I005', 2, 27.)
call AddItemIdItemPower('I005', 3, 15.)
call AddItemIdItemPower('I005', 4, 0.1)
//Amulet of Darkness gets 2 - 4 Damage
call AddItemIdItemPower('I00A', 1, 2.)
call AddItemIdItemPower('I00A', 2, 4.)
//Arthuriel gets + 50 % Crit Chance
call AddItemIdItemPower('I008', 3, 50.)
//Golem skin gets + 10 % Crit Damage
call AddItemIdItemPower('I003', 4, .1)
//Mask of Horror gets + 10 % Crit Damage
call AddItemIdItemPower('I009', 4, .1)
//Razor gets 100 - 270 Damage
//Also 200% Critical Hit Damage
call AddItemIdItemPower('I006', 1, 100.)
call AddItemIdItemPower('I006', 2, 270.)
call AddItemIdItemPower('I006', 5, 2.)
//Sacred Stone gets 4 - 8 Damage,
call AddItemIdItemPower('I000', 1, 4.)
call AddItemIdItemPower('I000', 2, 8.)
//Shield gets 35% Block Chance
//Also 25 - 45 Block Damage
call AddItemIdItemPower('I001', 5, 35.)
call AddItemIdItemPower('I001', 6, 25.)
call AddItemIdItemPower('I001', 7, 45.)
//Harolds Cleaver gets 55 - 75 Damage
call AddItemIdItemPower('I00B', 1, 55.)
call AddItemIdItemPower('I00B', 2, 75.)
//Harolds Ring gets +40 Crit Chance
call AddItemIdItemPower('I00D', 3, 40.)
/**
* How to register an unit:
*
* call CreateUnitItemPower(gg_unit_H001_0001)
*
* In this demo map I did this already in LAB, so leave it commented here.
*/
endmethod
implement Init
endstruct
endscope
//TESH.scrollpos=69
//TESH.alwaysfold=0
scope test initializer init
globals
private unit u
endglobals
private function kill takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
call RemoveUnit(u)
set u = null
endfunction
private function pa takes integer i returns nothing
call UnitAddAbility(u, i)
endfunction
private function pi takes integer i returns nothing
local item it = CreateItem(i, 0,0)
call RemoveItem(it)
set it = null
endfunction
private function init takes nothing returns nothing
set u = CreateUnit(Player(0),'hfoo', 0,0,270)
call TimerStart(CreateTimer(), 0.00, false, function kill)
call pi('I000')
call pi('I001')
call pi('I002')
call pi('I003')
call pi('I004')
call pi('I005')
call pi('I006')
call pi('I007')
call pi('I008')
call pi('I009')
call pi('I00A')
call pi('I00B')
call pi('I00C')
call pi('I00D')
call pa('A00Z')
call pa('A00A')
call pa('A00B')
call pa('A00C')
call pa('A00D')
call pa('A00E')
call pa('A003')
call pa('A004')
call pa('A007')
call pa('A00G')
call pa('A00F')
call pa('A00L')
call pa('A00M')
call pa('A000')
call pa('A008')
call pa('A009')
call pa('A00H')
call pa('A005')
call pa('A00W')
call pa('A017')
call pa('A00X')
call pa('A002')
call pa('A00R')
call pa('A00J')
call pa('A00K')
call pa('A00P')
call pa('A00O')
call pa('A00S')
call pa('A00T')
call pa('A00U')
call pa('A00V')
call pa('A001')
call pa('A016')
call pa('A00Y')
call pa('A006')
call pa('A013')
call pa('A014')
call pa('A015')
call pa('A010')
call pa('A012')
call pa('A018')
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Have fun exploring the system.")
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12.00)
call SuspendTimeOfDay(true)
call FogEnable(false)
call FogMaskEnable(false)
endfunction
endscope