1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. Lead your forces to battle in the 15th Techtree Contest. The call is yours, commander!
    Dismiss Notice
  5. The reforging of the races is complete. Come see the 14th Techtree Contest Results.
    Dismiss Notice
  6. It's time to choose your horse in the race - the 32nd Modeling Contest Poll is up!
    Dismiss Notice
  7. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Equipment v2.5.2

Submitted by BPower
This bundle is marked as approved. It works and satisfies the submission rules.
Equipment
Code

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.



~~~~~~~~~~~~~~~~~~~~~~~~~~
[​IMG] Import Instructions

  • Copy library Equipment and ErrorMessage into your map.
  • Copy the equipment dummy unit from the demo map into your map.
  • Read over the global user settings inside library Equipment.



~~~~~~~~~~~~~~~~~~~~~~~~~~

[​IMG] Library Equipment

Code (vJASS):
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
*  
************************************************************************************
*
*   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 file != null 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 0 == bonuses 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 0 >= index
                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




~~~~~~~~~~~~~~~~~~~~~~~~~~
[​IMG] Requirements

  • Equipment can draw back on the following optional requirements:
    ____
    • Earth-Fury's BonusMod to register abstract bonuses to item ids.
    • Nestharus's Bonus to register abstract bonuses to item ids.
    • BPower's ItemSet to create item collections.



~~~~~~~~~~~~~~~~~~~~~~~~~~
[​IMG] API
Code (vJASS):

*       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 should not 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.
 




~~~~~~~~~~~~~~~~~~~~~~~~~~
[​IMG] Demo Code

  • See first comment.


Keywords:
equipment, item, system, Equipment System, modification, hero, unit, bonus, stat, equip, Set, ItemSet, SetItems, Inventory, inventory
Contents

Equipment (Map)

Reviews
Moderator
14 Dec 2015: Bribe This is a great system which has purposes the demo map probably can't fully show off. When I think of the amount of work that went into coding this customized machine from the ground up (as WC3 provides none of this...
  1. 14 Dec 2015:
    Bribe

    This is a great system which has purposes the demo map probably can't fully show off. When I think of the amount of work that went into coding this customized machine from the ground up (as WC3 provides none of this functionality natively), I feel that deserves a bump. Includes Lots of features and is as optimized as it gets.

    Normally, I would give 4/5 due to the huge amount of Object Editor configuration required on the user's side, but due to the amount of work saved if the user had been attempting something like this, the resource should be a full 5/5.
     
  2. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Demo Code:

    Code (vJASS):
    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
     
    Last edited: Jun 7, 2016
  3. JAKEZINC

    JAKEZINC

    Joined:
    Aug 13, 2013
    Messages:
    1,458
    Resources:
    9
    Spells:
    9
    Resources:
    9
    So this calculates the damage, stats of the item and the unit?
     
  4. Matarael

    Matarael

    Joined:
    Dec 9, 2005
    Messages:
    513
    Resources:
    12
    Models:
    10
    Icons:
    1
    Maps:
    1
    Resources:
    12
    Jesus Christ if this works this is awesome.
     
  5. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    No it just calculates numbers linked to the items.
    For example:
    A unit has item1, item2 and now we equip item3.
    On equip event ( or unequip) the system gets all values of the equipping item from an hashtable: item3.
    Reals and Integers of item1 and item2 are already calculated and saved to that unit in a TableArray.
    Now the real and Integer linked to item3 will be will be summed up (on unequip subtracted) to the correspsondig prior group for that unit .

    This happens on equip ( Its actually a loop for each prior group i)
    this refers to the unit in the TableArray, i is the prior group, id is the ItemId
    set tb[this].real[i] = tb[this].real[i] + LoadReal(hash, id, i)
    set tb[this][i] = tb[this][i] + LoadInteger(hash, id, i)




    Unit stats stay untouched, because only Hero's have stats and you cannot call UnitArmor or Damage. Such functions just don't exist, you calculate them approximately with DDS.

    The demo map shows pretty much, what this system does.
     
  6. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Fixed a small issue in the demo map.

    Edit: Added function SetItemPower to manipulate values at any time. The idea was to increase/ decrease values over a period of time, for instance increase CritChance by 10% for the next 15 seconds.

    Note: It's a tricky function and can screw up the whole math if used wrong.
     
    Last edited: Nov 23, 2013
  7. Arhowk

    Arhowk

    Joined:
    Aug 8, 2007
    Messages:
    2,752
    Resources:
    0
    Resources:
    0
    Whats the advantage of using this over a hashtable/table array?

    set unitPowers[GetUnitUserData(u)][indexOfPower] = unitPowers[GetUnitUserData(u)][indexOfPower] + 1

    also, how do you accomodate for units dying? you use unitIndex[unit], but if a unit dies and a new one takes its place, that one will have all of its "powers" as the old one

    the values don't stack properly together IMO. For one, 10% crit rate + 15% crit rate shouldn't be 25% as this will make stacking incredibilly imba. (10% + 15% should be 22.75%, which is n + (n#2 * (1 - n)) + (n#3 * (1-n-(n#2*(1-n)))) + . . . (for the infinite series form, theres a formula to do it without an infinite series) (though thats my personal opinion. If you ever played the game rappelz, recent updates totally destroyed the game because they could get 100% 6x crit rate and just do infinite amounts of damage)

    I also feel like this system is too raw in its current form (not counting if you add the stuff in this post). If your targeting an audience that has no idea how to make a flexible system like this for their own maps, than you should do stuff like increasing health if the type of power is health, (etc for damage and whatever) applying the resistance / crit bonuses on damage event, yada.
     
  8. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    UnitIndexer will assign user data in the range of 0-8190, using a static thistype array seems handy to me.

    Sorry, but total nonesense.
    You miss the point of this system. It was never designed to balance attack features like crit. Critical Hit it is just an example for a possible cenario.
    What you mention here is just a personal opinion based on a random map. I can't take that serious.

    I'll will change that variable's name. It has nothing to do with UnitIndexer its just a static thistype array. You have to register every unit individually to the system. When registering all previous values will be overwritten. EDIT:Changed name to pointer.

    [hidden="Quote]
    I would recommend writing an isolated crit system, if you want to use a specific algoritm.[/hidden]
    It is simple and versatile in one and that's effective, at least in my opinion.
    That's what bonus libs are/were for. (If Lua ever get fixed)

    EDIT:
    Yes you are right, in v.1.0.0.0 I've added a small prototype of how to you this system with DDS, but deleted it later because the map is already packed full of libraries from MUI Advanced EquipSystem.
    That's the reason I designed two small spells (Cleave and missle) and coupled them with ItemPower.
    I'll improve their readability.
     
    Last edited: Nov 24, 2013
  9. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Added an optional second system which allows users to create ItemSets.
    Works hand in hand with ItemPower and Advanced Equipment System
     
  10. deathismyfriend

    deathismyfriend

    Joined:
    Oct 24, 2012
    Messages:
    6,526
    Resources:
    14
    Spells:
    12
    Tutorials:
    2
    Resources:
    14
    I'm guessing by item set you mean item recipes ? or do you mean item restrictions. (left right hand armor helmet and other things ?)
     
  11. Arhowk

    Arhowk

    Joined:
    Aug 8, 2007
    Messages:
    2,752
    Resources:
    0
    Resources:
    0
    Hm, if your looking to overhaul it...

    Set System

    0) It looks nice, not really much more you can do with it, but
    Code (vJASS):

    *           static method init takes integer Set, string s, string e, string a returns nothing
    *               - Init a new ItemSet
     


    Some more documentation on those arguments would be nice. (see #8)

    2) (isn't really a needed change but) I don't like the usage of the "Set" variable. Your essentially emulating the use of the "this" variable, which vJass is designed to do. Why not just make it a library, make all of the variables arrays and do name[Set], for example.

    3) the entire addBonus method should be in a static if.

    4) I'd change the name of the "Bonus" struct to something else, to clarify that its a specific bonus (ex. AtCountBonus)

    5) Set bonuses should be re-evaluated when removeBonus is called

    6) Static if's hold the same indentation principles as normal if's, the way you did it is strange

    7) camelcase. If its not a global or a static method name, it should be lowerCamelCase.

    8) make your variable names longer. (ex. the Bonus.p should be like Bonus.appliedAbility)

    ItemPower

    0) Personal opinion, but I'd remove the integer arguments when adding bonuses. You can still return integer values, but just return R2I(whatever)

    1) As i said before, needs more overall functionality. Good things to do would be stacking percentage management (evasion, block, crit rate, resistances, etc you dont want going above/reaching 100), damage event integration, constants tables (a table of good bonuses to be implemented into an RPG, can be modified upon desire of the mapmaker)

    2) More documentation for the ManipulateItemPower function (and more documentation overall)
     
  12. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    No, if a unit has x Items of an ItemSet in his inventory a bonus is applied. Note that it uses a custom inventory system.
    Have you ever played Diablo 2, it works 100 % the same as Sets work there.

    Example: an ItemSet contains out of a helmet, boot and a sword.
    With example names: ItemSet Harold's Kitchen contains Harold's Cleaver, Harold's Helmet and Harold's Footprints.
    Bonuses are: with 2 Items hold +2 Defense, with 3 items +2 Defenses (This Bonus stays), +10 Damage, +150 Life.
    • 2 Items of this Set in the inventory --> + 2 Defense
    • 3 Items of this Set in the inventory --> + 10 Damage
    • 3 Items of this Set in the inventory --> + 150 Life
    • Unit drops one of those item --> 10 Damage and 150 Life will be removed
    • 2 Items of this Set in the inventory --> 2 Defense stay's

    Suggest a more distinct library name, if you don't like ItemSet.
    Best would be to just test the map.
     
    Last edited: Nov 27, 2013
  13. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    @Arhowk

    Me too. It was the first variable name that came into my mind. I'll change it.
    Both Bonuses for the ItemPower library or simple ability bonuses are applied with this method. The Boolean true/ false determines which one to use.
    Code (vJASS):
        public function ItemSetAddAbility takes integer Set, integer ItemCount, integer id returns nothing
            call ItemSet.addBonus(Set, ItemCount, false, id, 0., 0)
        endfunction
       
        public function ItemSetAddPower takes integer Set, integer ItemCount, integer Power, real Real, integer Int returns nothing
            call ItemSet.addBonus(Set, ItemCount, true, Power, Real, Int)
        endfunction


    It's a private struct with no access from outside the library and I think the name Bonus fits very well. The function is
    Bonus.create(..)
    what yould be more distinct?

    Yes I know, I would rather remove the whole function as I've also seen that problem.
    It could only work this way: Unequip complete inventory, remove the Bonus reequip the inventory.
    Yeah, after all its v1.0.0.0 I'll fix that.
    I seldom use more than 4 characters outside the API. I'll try to make them more distinct, but not longer.
    In the first version of ItemPower I didn't use integers and run into a nasty rounding issue. Have you ever used
    I2S(R2I(0.99))
    , well it is 0. Most times the returned integer will be smaller than the real, for instance R2I(0.15*100) may be sometimes 14 if composed out of two values ( 0.1+0.05). Although integers are redundant, when it comes to damage calculations there are still few cases you want to return the exact integer ( not rounded), for instance on a multiboard.
    Okey-dokey! I'll do that.

    Thank you for your opinion.:thumbs_up:

    Edit: I know you don't like the math used in ItemPower, but that is not discussible. :/

    EDIT: Updated
    • Improved the documentation
    • Exchanged dubious variables
    • Fixed typos and all methods now follow the JPAG convention
    • Added debug part for the removeBonus method
     
    Last edited: Nov 27, 2013
  14. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    Right now an idea is developing in my head. I will add a Table or use an existing if possible, which offers the following: add specific algorithm. So each value can be calculated different. At the moment I'm not sure if either it can be done in one function or the user has to add a specific math and connect it with a booleanexpression --> if boolexpr (myExpr != null) call myMath(..) else use default math. Something like this.

    Edit: it will be a module --> implement optional ItemPowerAlgorithm. I just need to figure out which way is the best to determine if the system should suppress the origional math function and instead run the specific one or not. Also I need a counter for counting ho many Items of each Power a unit has.

    At first I didn't understand your argument, but now I'm hooked by this idea.
     
    Last edited: Nov 27, 2013
  15. Arhowk

    Arhowk

    Joined:
    Aug 8, 2007
    Messages:
    2,752
    Resources:
    0
    Resources:
    0
    I don't feel like individually qouting each of those atm but...

    R2I() is also known as trunc(), meaning it just gets rid of the decimal point

    if you want to round it to the nearest whole number, use R2I(x+0.5)

    As far as what your talking about with the algorithm, not too bad of an idea. What you could do is pass a trigger with one global variable being the input and another being the output. You can have the user call TriggerAddCondition() that will take in the input and set the output to whatever than add TriggerAddAction() to manipulate the output / reset the input. For example

    Code (vJASS):

    globals
        integer input
        integer output
    endglobals

    function manipulateInput takes nothing returns nothing
        set output = input * 2
    endfunction

    function init takes nothing returns nothing
        local trigger t = AddCustomAlgorithm(...)
        call TriggerAddCondition(t, function manipulateInput)
        set t = null
    endfunction
    function manipulateOutut takes nothing returns nothing
        call printi(output)
    endfunction

    function AddCustomAlgorithm takes /*whatever used to keep track of the differnent algorithms*/ returns trigger
        local trigger t = CreateTrigger()
        call TriggerAddAction(function manipulateOutput)
        return t
    endfunction


    Also, if your interested, the formula for what I've been talking about goes something like this

    Code (vJASS):

            static method getUnitResistance takes unit u, integer resistance returns real
                local real a = (100-customBonuses[resistance].real[GetUnitUserData(u)])/100
                local real b = (100-customBonuses[TRAIT_TYPE_ALL_RESISTANCE].real[GetUnitUserData(u)])/100
                return 100-((a*b)*100)
            endmethod


    this is just a small example, the customBonus[][] values were pre-compiled via

    Code (vJASS):

           
            private static method convert takes integer i returns integer
                if i == 0 then
                    return TRAIT_TYPE_FIRE_RESISTANCE
                elseif i == 1 then
                    return TRAIT_TYPE_LIGHTNING_RESISTANCE
                elseif i == 2 then
                    return TRAIT_TYPE_COLD_RESISTANCE
                elseif i == 3 then
                    return TRAIT_TYPE_POISON_RESISTANCE
                elseif i == 4 then
                    return TRAIT_TYPE_DARKNESS_RESISTANCE
                elseif i == 5 then
                    return TRAIT_TYPE_HOLY_RESISTANCE
                elseif i == 6 then
                    return TRAIT_TYPE_PHYSICAL_RESISTANCE
                elseif i == 7 then
                    return TRAIT_TYPE_ALL_RESISTANCE
                elseif i == 8 then
                    return TRAIT_TYPE_SPELL_RESISTANCE
                endif
                return -1
            endmethod
           
            private static method r_compile takes unit u, integer r returns nothing
                local integer i = 0
                local integer id = GetUnitUserData(u)
                local integer id2 = id*8192
                local integer max = currentResistancePoint[r+id2]
                local real ti
                local real tt = 1
                local real array n
                loop
                    exitwhen i > max
                    set ti = resistances[r].real[i+id2]
                    set tt = ((100-ti)/100)*tt
                    set i = i + 1
                endloop
                set customBonuses[convert(r)].real[id] = 100-((tt)*100)
            endmethod
     
  16. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    The prototype of IPMath looks like this:
    You have 2 arguments to work with:
    real old is the value before equipping and counter to keep track of the Items with have influence of the specific power. ( for example 3 items in your inventory have crit --> counter = 3
    Also real new which is the new real value.
    IPMHead takes function name and which power, if I get it to work you can paste any caculation inside.
    Code (vJASS):
                //! runtextmacro IPMHead("increaseCrit", "4")
                    set old = old*1000
                    set new = old + 1*counter
                    //debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,2,R2S(new))
                //! runtextmacro IPMIncreaseEnd()


    Edit: It's done, hopefully I get the API finished until the weekend :)
    Edit 2: I've change the API of both systems, they now work perfectly. I just need more some time to make a proper demonstration. :thumbs_up:

    EDIT 3: Its done, I'll upload it later tonight. If someone feels more variables are required for any custom calculation plz tell me.

    Custom Formula
    Code (vJASS):
    *   Any custom formulas will be added here, the design is always the same
    *   An example is worth more than 1000 Words.
    */
        //Now you have 4 variables to your need: value, old, new and counter
       
        //value --> The value the current item has.
        //          For instance +20% MagicDamage
        //old --> is the current total value for your unit.
        //          For instance +160% Magic Damage composed out of 3 items
        //new --> is the value which will be saved in the end
        //          For instance new = old + value --> 180% = 160% + 20%
        //counter --> is the number of items hold by your unit, which increase that ItemPower
        //          For instance 1.Boots +100% 2.Gloves +20% 3.Sword +40% --> counter = 3
       
        //My custom formula for MagicDamage. --> //! textmacro IPMIncreaseHead takes TYPE, INTEGER
       
        //Scenario Increase value:
        //Step 1: type runtextmacro IPMIncreaseHead("NameOfYourFunction", "WhichItemPower")
            //! runtextmacro IPMIncreaseHead("increaseMagicDamage", "2")
                    set new = old + (value*counter)//Step 2: define your very own formula
            //! runtextmacro IPMIncreaseEnd()
        //Step 3: type runtextmacro IMPIncreaseEnd()
       
        //And the other way around.
       
        //Scenario Decrease value:
        //Step 1: type runtextmacro IPMDecreaseHead("NameOfYourFunction", "WhichItemPower")
            //! runtextmacro IPMDecreaseHead("decreaseMagicDamage", "2")
                    set new = old - (value*counter)//Step 2: define your very own formula
            //! runtextmacro IPMDecreaseEnd()
            //Step 3: type runtextmacro IMPDecreaseEnd()
           
        //Step 4: register your function name with the equipsystem within the onInit method.
            private static method onInit takes nothing returns nothing
                implement ItemPowerInitHack//Don't touch
                call RegisterItemEquipEvent(function thistype.increaseMagicDamage)//We register the method with the increase formula to the equip event
                call RegisterItemUnequipEvent(function thistype.decreaseMagicDamage)//We register the method with the decrease formula to the unequip event


    The exmaple is simple, but shows the purpose very well.

    Opinions and feedbacks are very welcome especially concerning the following issues:
    • Logical mistakes within the calculation part
    • Missing variables to ensure any calculation is possible (for example saving the value an item gave to a unit if a custom formula was used)
    • Reorganization of the whole library or the modules to improve readability
    • Custom formulas, so I can provide a better example than the actual one
    • Any additional features this system should provide
    • typos

    Upate:
    Commented the exmaple for a custom formula, as it should not be used by default and added a small manual. Also did some reorganization, I hope its now easier to understand.
     
    Last edited: Nov 30, 2013
  17. Maker

    Maker

    Joined:
    Mar 6, 2006
    Messages:
    9,174
    Resources:
    17
    Maps:
    2
    Spells:
    14
    Tutorials:
    1
    Resources:
    17
    Maybe you could give bonuses constant id, for example

    Code (vJASS):

    globals
        private constant integer BONUS_CRIT_CHANCE = 1
        private constant integer BONUS_CRIT_MODIFIER = 2
        private constant integer BONUS_STRENGTH = 3
    endglobals
     


    Null the destroyed effect immediately in the item set.
     
  18. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    I never intended to impose certain bonuses. As they are "only" real numbers, a bonus could be anything (Increase raidus for group enumaration, Unit scale value, Increase Chances of any type, ...)
    I've chosen crit as example, just because everyone is familiar with it. But I do agree that the configuration part is user unfriendly in its current state.
    Bonus strength is a bad example, because the system can't provide such bonuses as Armor, Strength, Agility, Int, Movement Speed, Abilities. But the Witcher's equipsystem can.

    Code (vJASS):
    if (Many == .count) then//Is the set completed?
        call this.itemSetComplete(u)
    endif
    private method itemSetIncomplete takes unit u returns nothing
        static if ATTACH_EFFECT then
            local integer i = GetUnitId(u)
            call DestroyEffect(ind[i].effect[this])
        endif
        call DisplayTimedTextToPlayer(GetOwningPlayer(u), 0,0,TIME, MESSAGE_COLOR + INCOMPLETE  + "|r" + ITEM_SET_COLOR +this.name)
    endmethod

    On the next update, I'll change it to:
    Code (vJASS):
    if (Many == .count) then//Is the set completed?
        static if ATTACH_EFFECT then
            call DestroyEffect(ind[GetUnitId(u)].effect[this])
        endif
        call DisplayTimedTextToPlayer(GetOwningPlayer(u), 0,0,TIME, MESSAGE_COLOR + INCOMPLETE  + "|r" + ITEM_SET_COLOR +this.name)
    endif
     
  19. Maker

    Maker

    Joined:
    Mar 6, 2006
    Messages:
    9,174
    Resources:
    17
    Maps:
    2
    Spells:
    14
    Tutorials:
    1
    Resources:
    17
    ^Remove the handle.