• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJass] Advanced Inventory System v2.01b

This is an advanced Inventory System, fully written in vjass.
It allows the user to add heroes and advanced inventory system, which features:
  • Equipment Menu with 8 Slots
  • 8 Slot Backpack
  • Auto Stack Detection System
  • Advanced Item Handling
  • ....

Requires:
vJass
Table, TimerUtils & SimError (by Vexorian)
StringRead (short System by D1000)
BonusMod & SetUnitMaxState (by Earth-Fury)

Icons by:
BruZzl3R_17C

Fore more details, look in the librarys.
If you wan't to use this system correct, you have to disable 2 thinks in the Gameplay-Interface:
  • Text - Message - 'Inventory is full':
    Delate this message " ", else the normal wc3 Message will be shown, that you hero#s inventory is full
  • Sound - Inventory Full:
    Just "Shift-Klick", press space and then enter again. That disables the sound, which will be played, wenn the inventory is full.



Version 2.01b:
Demo Map:
- added a computer bot to demo map, so it can be runned direct through warcraft



//////////////////////////////////////////
// OLD CHANGELOGS: //
//////////////////////////////////////////

Version 2.01:
Inventory System:
- added two new globals for movement speed
Item List:
- added movement speed bonus
- fixed bug in addNormelItem method

Version 2.00:
Inventory System:
- added a 8 slot backpack
- added a auto stack detection system
- added fully triggered item pickup
- ALOT code changes and improvements
- Added some more constants
- now uses TimerUtils
- Item now can be equipped when the equipment menu is opened (through auto equip for example)
Item List:
- added more configurations
- removed / delated methods which are not required
- added some new methods / operators for handling with "Items"
- made some struct member private
- added possibility to add potions to the system (uses the new configurations)
- added the possibility to add "special efffects" to items (only for Equipment)
Demo Map:
- added 6 new Items

Version 1.02:
Inventory System:
-added auto equip to the system
-added normal functions to add heroes to the system, get its Inventory and specific items in slots
-some small code changes
Item List:
-added functions to add items directly to their Slot
Demo Map:
-added 3 example items

Version 1.01:
Inventory System:
-added custom Icons for "Emtpy Slots".
-you now can setup the position of the 'Inventory-Item'.
-After closing the inventory, the items now will be added to the place they where before closing.
-When equiping an item and its slot is full, the item will be switched to the same slot, the new equipped item was




JASS:
library InventorySystem requires Table, ItemList, SimError, BonusMod, SetUnitMaxState, SearchString
    
globals
    //The Rawcodes of the Items for opening the backpack and equipment
    private constant integer EQUIPMENT_OPEN_ID  = 'I000'
    private constant integer BACKPACK_OPEN_ID   = 'I00R'
    
    //The rawcodes of the Items for closing the backpack and equipment
    private constant integer EQUIPMENT_CLOSE_ID = 'I001'
    private constant integer BACKPACK_CLOSE_ID  = 'I00S'
    
    //The rawcodes of the Items changing the pages of the backpack and equipment menu
    private constant integer EQUIPMENT_PAGE_1   = 'I003'
    private constant integer BACKPACK_PAGE_1    = 'I00T'
    
    private constant integer EQUIPMENT_PAGE_2   = 'I002'
    private constant integer BACKPACK_PAGE_2    = 'I00U'
    
    //The slots, where the items for opening backpack and equipment menu should be created
    private constant integer EQUIPMENT_SLOT     = 4
    private constant integer BACKPACK_SLOT      = 5
    
    //The place, where required items should be stored
    private constant real STORE_ITEM_X          = 0.
    private constant real STORE_ITEM_Y          = 0.
    
    //The Maximum Movespeed a unit can have
    private constant real MAX_MOVEMENT_SPEED    = 400.
    //The Minimum Movespeed a unit can have
    private constant real MIN_MOVEMENT_SPEED    = 150.
    
    //The Max Number of Charges, an item can has maximum
    constant integer MAX_ITEM_CHARGES           = 8
    
    //If true, the equipment menu will change to first page when opened
    private constant boolean CHANGE_TO_FIRST    = true
    //If trie, an item being picked up will be auto equipped, if its slot is emtpy and the hero can wear it
    private constant boolean AUTO_EQUIP         = true
    //The distance, an item can be picked up by a hero
    private constant real ITEM_PICKUP_DIST      = 225.00
    
    //Don't change this!!!
    private integer array DUMMY_ITEMS[8]
    
endglobals

private function SETUP_DUMMY_ITEMS takes nothing returns nothing
    set DUMMY_ITEMS[0] = 'I004' //Head
    set DUMMY_ITEMS[1] = 'I005' //Chest
    set DUMMY_ITEMS[2] = 'I006' //Main Hand
    set DUMMY_ITEMS[3] = 'I007' //Secondary Hand
    set DUMMY_ITEMS[4] = 'I008' //Gloves
    set DUMMY_ITEMS[5] = 'I009' //Feet
    set DUMMY_ITEMS[6] = 'I00A' //First Acessory
    set DUMMY_ITEMS[7] = 'I00B' //Second Acessory
endfunction

//EVERYTHING BELOW HERE IS SYSTEM CODE!!!
private keyword addItem

//This is the triggered item pickup and stack detection System.
private struct StackData
    private delegate Inventory inv = 0
    item item = null
    real x = 0
    real y = 0
    integer order = 0
    
    private static integer count = 0
    private static thistype array datas
    private static trigger walkTo
    
    //creates a new instance of the stackdata
    static method create takes unit hero, item which returns StackData
        local StackData this = StackData.allocate()
        set .inv = Inventory.table[hero]
        set .item = which
        set .x = GetItemX(.item)
        set .y = GetItemY(.item)
        call IssuePointOrder(.Owner, "move", .x, .y)
        set .order = GetUnitCurrentOrder(.Owner)
        set .datas[.count] = this
        set .count = .count + 1
        return this
    endmethod
    
    //checks if the issued target was an item and the order is correct
    static method check takes nothing returns boolean
        if GetOrderTargetItem() != null and (GetIssuedOrderId() == 851971) then
            call StackData.create(GetTriggerUnit(), GetOrderTargetItem())
            return true
        endif
        return false
    endmethod
    
    //just sorts and checks each stackdata...
    static method periodic takes nothing returns nothing
        local integer i = .count - 1
        loop
            exitwhen i < 0        
            if not .datas[i].control() then
                set .count = .count - 1
                if .count < 0 then
                    set .count = 0
                else
                    set .datas[i] = .datas[.count]
                endif
            endif
            set i = i - 1
        endloop
    endmethod
    
    //checks the hero and the item every interval
    method control takes nothing returns boolean
        local real dx = GetUnitX(.Owner) - .x
        local real dy = GetUnitY(.Owner) - .y
        local Item it = Item.getItemById(GetItemTypeId(.item))
        if GetUnitCurrentOrder(.Owner) != .order or GetItemX(.item) != .x or GetItemY(.item) != .y or GetWidgetLife(.Owner) <= 0.405 then
            return false
        elseif SquareRoot(dx * dx + dy * dy) <= ITEM_PICKUP_DIST and GetItemX(.item) == .x or GetItemY(.item) == .y and GetWidgetLife(.Owner) > 0.405 then
            call IssueImmediateOrder(.Owner, "stop")
            if (AUTO_EQUIP and it.equippable and .isSlotEmpty(it.slot) and .checkHero(it, false) and .allowauto) then
                return Inventory.equipItem(.Owner, .item, it)
            endif                
            if not .checkItemForStack(.item) then
                if not .getSlotForItem(.item) then
                    call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
                    return false
                endif
            endif
            return false
        elseif SquareRoot(dx * dx + dy * dy) > ITEM_PICKUP_DIST then
            return true
        endif
        return false
    endmethod
    
    //self explanatory..
    static method onInit takes nothing returns nothing
        set .walkTo = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(.walkTo, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
        call TriggerAddCondition(.walkTo, Condition(function thistype.check))
        call TimerStart(NewTimer(), 0.05, true, function thistype.periodic)
    endmethod
    
endstruct

//private struct required to "recreate" items in their slots when their are dragged or dropped incrorrect
private struct addItem
    delegate Inventory inv
    integer id
    integer charges
    integer slot   
    private timer t
    
    //creates a new instance for an item to a move it to a slot
    static method create takes item it, integer slot, Inventory inv returns thistype
        local thistype this = thistype.allocate()
        set .inv = inv
        set .id = GetItemTypeId(it)
        set .charges = GetItemCharges(it)
        set .slot = slot
        set .dropped = false
        call RemoveItem(it)
        set .dropped = true
        set .t = NewTimer()
        call SetTimerData(.t, this)
        call TimerStart(.t, 0.0, false, function thistype.expire)
        return this
    endmethod
    
    //0.0 second tiemr interval is required...
    private static method expire takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer())
        call UnitAddItemToSlotById(.Owner, .id, .slot)
        call SetItemCharges(UnitItemInSlot(.Owner, .slot), .charges)
        call ReleaseTimer(.t)
        call .destroy()
    endmethod

endstruct

//The Inventory struct..
struct Inventory
    unit Owner
    string HeroClass = ""
    string ItemClasses = ""
    item array equip[8]
    integer array items[6]
    integer array backp[8]
    
    integer array charges[6]
    integer array bcharges[8]
    boolean allowauto = true
    integer bfree = 8
    boolean dropped = true
    
    private effect array fx[8]
    private boolean open = false
    private boolean bopen = false
    private integer page = 1
    private integer bpage = 1
    private real array MsBoni[8]
    public static HandleTable table
    private static trigger Use = CreateTrigger()
    private static trigger Pickup = CreateTrigger()
    private static trigger Drag = CreateTrigger()
    private static trigger Drop = CreateTrigger()
    
    //creates a new Inventory for the unit
    static method create takes unit whichUnit, string heroClass, string availableItemClasses returns thistype
        local thistype this = 0
        local integer i = 0
        if not .table.exists(whichUnit) then
            set this = thistype.allocate()
            set .Owner = whichUnit
            set .HeroClass = heroClass
            set .ItemClasses = availableItemClasses
            loop
                exitwhen i > 8
                set .equip[i] = CreateItem(DUMMY_ITEMS[i], STORE_ITEM_X, STORE_ITEM_Y)
                call SetItemVisible(.equip[i], false)
                set i = i + 1
            endloop
        
            call TriggerRegisterUnitEvent(.Use, .Owner, EVENT_UNIT_USE_ITEM)
            call TriggerRegisterUnitEvent(.Pickup, .Owner, EVENT_UNIT_PICKUP_ITEM)
            call TriggerRegisterUnitEvent(.Drag, .Owner, EVENT_UNIT_ISSUED_TARGET_ORDER)
            call TriggerRegisterUnitEvent(.Drop, .Owner, EVENT_UNIT_DROP_ITEM)
            
            call UnitAddItemToSlotById(.Owner, EQUIPMENT_OPEN_ID, EQUIPMENT_SLOT)
            call UnitAddItemToSlotById(.Owner, BACKPACK_OPEN_ID, BACKPACK_SLOT)
            call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
            set .table[.Owner] = this
            return this
        endif
        return 0
    endmethod
    
    //opens the backpack for the unit
    public static method openBackpack takes unit whichUnit returns boolean
        local integer i = 0
        local integer i2 = 0
        local thistype this = .table[whichUnit]
        local item temp = null
        local integer add = 0
        if not .table.exists(whichUnit) then
            return false
        elseif not .bopen and not .open then
            set .bopen = true
            set .allowauto = false
             loop
                exitwhen i > 5
                set temp = UnitItemInSlot(.Owner, i)
                set .charges[i] = GetItemCharges(temp)
                set .items[i] = GetItemTypeId(temp)
                call RemoveItem(temp)
                set i = i + 1
            endloop
            call UnitAddItemToSlotById(.Owner, BACKPACK_CLOSE_ID, 5)
            if .bpage <= 1 then
                set i = 0
                loop
                    exitwhen i > 3
                    if .backp[i] != 0 then
                        call UnitAddItemToSlotById(.Owner, .backp[i], i)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), .bcharges[i])
                    endif
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_2, 4)
            elseif .bpage >= 2 then
                set i = 0
                loop
                    exitwhen i > 3
                    if .backp[i+4] != 0 then
                        call UnitAddItemToSlotById(.Owner, .backp[i+4], i)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), .bcharges[i+4])
                    endif
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_1, 4)
            endif 
            set temp = null  
            set .allowauto = true
            return true
        endif
        return true
    endmethod
    
    //opens the equipment menu
    public static method openEquipment takes unit whichUnit returns boolean
        local integer i = 0
        local thistype this = .table[whichUnit]
        local item temp = null
        if not .table.exists(whichUnit) then
            return false
        elseif not .open and not .bopen then
            set .open = true
            set .allowauto = false
            if .page <= 1 or CHANGE_TO_FIRST then
                loop
                    exitwhen i > 5
                    set temp = UnitItemInSlot(.Owner, i)
                    set .charges[i] = GetItemCharges(temp)
                    set .items[i] = GetItemTypeId(temp)
                    call RemoveItem(temp)
                    if i < 4 then
                        call SetItemVisible(.equip[i], true)
                        call UnitAddItem(.Owner, .equip[i])
                    endif
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, EQUIPMENT_PAGE_2, 4)
            elseif .page >= 2 and not CHANGE_TO_FIRST then
                loop
                    exitwhen i > 5
                    set temp = UnitItemInSlot(.Owner, i)
                    set .charges[i] = GetItemCharges(temp)
                    set .items[i] = GetItemTypeId(temp)
                    call RemoveItem(temp)
                    if i < 4 then
                        call SetItemVisible(.equip[i+4], true)
                        call UnitAddItem(.Owner, .equip[i+4])
                    endif
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, EQUIPMENT_PAGE_1, 4)
            endif            
            call UnitAddItemToSlotById(.Owner, EQUIPMENT_CLOSE_ID, 5)
            set .allowauto = true
            return true
        endif
        return true
    endmethod
    
    //closes the hero's backpack
    public static method closeBackpack takes unit whichUnit returns boolean
        local integer i = 0
        local item t = null
        local thistype this = .table[whichUnit]
        local item temp = null
        if not .table.exists(whichUnit) then
            return false
        elseif .bopen then
            set .bopen = false
            set .allowauto = false
            call RemoveItem(UnitItemInSlot(.Owner, 5))
            call RemoveItem(UnitItemInSlot(.Owner, 4))
            if .bpage <= 1 then
                loop
                    exitwhen i > 3
                    set temp = UnitRemoveItemFromSlot(.Owner, i)
                    set .backp[i] = GetItemTypeId(temp)
                    set .bcharges[i] = GetItemCharges(temp)
                    call RemoveItem(temp)
                    set i = i + 1
                endloop
            elseif .bpage >= 2 then
                loop
                    exitwhen i > 3
                    set temp = UnitRemoveItemFromSlot(.Owner, i)
                    set .backp[i+4] = GetItemTypeId(temp)
                    set .bcharges[i+4] = GetItemCharges(temp)
                    call RemoveItem(temp)
                    set i = i + 1
                endloop
            endif
            
            set i = 0
            loop
                exitwhen i > 5
                if .items[i] != 0 then
                    call UnitAddItemToSlotById(.Owner, .items[i], i)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), .charges[i])
                endif
                set i = i + 1
            endloop
            set .bfree = .getBackpackPlace()
            call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
            set .allowauto = true
            set temp = null
            return true
        endif
        return false
    endmethod
    
    //closes the hero's equipment menu
    public static method closeEquipment takes unit whichUnit returns boolean
        local integer i = 0
        local item t = null
        local thistype this = .table[whichUnit]
        local integer add = 0
        if not .table.exists(whichUnit) then
            return false
        elseif .open then
            set .open = false
            set .allowauto = false
            if .page >= 2 then
                set add = 4
            endif
            loop
                exitwhen i > 3
                set .equip[i+add] = UnitRemoveItemFromSlot(.Owner, i)
                call SetItemPosition(.equip[i+add], STORE_ITEM_X, STORE_ITEM_Y)
                call SetItemVisible(.equip[i+add], false)
                set i = i + 1
            endloop
            call RemoveItem(UnitItemInSlot(.Owner, 5))
            call RemoveItem(UnitItemInSlot(.Owner, 4))
            
            set i = 0
            loop
                exitwhen i > 5
                if .items[i] != 0 then
                    call UnitAddItemToSlotById(.Owner, .items[i], i)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), .charges[i])
                endif
                set i = i + 1
            endloop
                
            call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
            if CHANGE_TO_FIRST then
                set .page = 1
            endif
            set .allowauto = true
            return true
        endif
        return false
    endmethod
    
    //changes the backpack pages of the unit
    public static method changePageBackpack takes unit whichUnit returns boolean
        local integer i = 0
        local thistype this = .table[whichUnit]
        local item temp = null
        if .bopen and this != 0 then
            set .dropped = false
            call RemoveItem(UnitItemInSlot(.Owner, 4))
            set .dropped = true
            set .allowauto = false
            if .bpage <= 1 then
                loop
                    exitwhen i > 3
                    set temp = UnitRemoveItemFromSlot(.Owner, i)
                    set .backp[i] = GetItemTypeId(temp)
                    set .bcharges[i] = GetItemCharges(temp)
                    call UnitAddItemToSlotById(.Owner, .backp[i+4], i)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), .bcharges[i+4])
                    call RemoveItem(temp)
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_1, 4)
                set .bpage = 2
            elseif .bpage >= 2 then
                loop
                    exitwhen i > 3
                    set temp = UnitRemoveItemFromSlot(.Owner, i)
                    set .backp[i+4] = GetItemTypeId(temp)
                    set .bcharges[i+4] = GetItemCharges(temp)
                    call UnitAddItemToSlotById(.Owner, .backp[i], i)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), .bcharges[i])
                    call RemoveItem(temp)
                    set i = i + 1
                endloop
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_2, 4)
                set .bpage = 1
            endif
            set .allowauto = true
            set temp = null
            return true
        endif
        return false
    endmethod
    
    //changes the equipment menu pages for the unit 
    public static method changePageEquipment takes unit whichUnit returns boolean
        local integer i = 0
        local thistype this = .table[whichUnit]
        local integer add = 0
        if .open and this != 0 then
            if .page >= 2 then
                set add = 4
            endif
            set .allowauto = false
            loop
                exitwhen i > 3
                set .equip[i+add] = UnitRemoveItemFromSlot(.Owner, i)
                call SetItemPosition(.equip[i+add], STORE_ITEM_X, STORE_ITEM_Y)
                call SetItemVisible(.equip[i+add], false)
                call UnitAddItem(.Owner, .equip[i+4-add])
                set i = i + 1
            endloop
            set .dropped = false
            call RemoveItem(UnitItemInSlot(.Owner, 4))
            set .dropped = true
            if .page >= 2 then
                call UnitAddItemToSlotById(.Owner, EQUIPMENT_PAGE_2, 4)
                set .page = 1
            elseif .page <= 1 then
                call UnitAddItemToSlotById(.Owner, EQUIPMENT_PAGE_1, 4)
                set .page = 2
            endif
            set .allowauto = true
            return true
        endif
        return false
    endmethod
    
    //Only call this method, if you can be shure, that the Item's slot is empty or it will bug!
    public static method quickEquip takes unit u, Item it returns boolean
        local thistype this = .table[u]
        if .checkHero(it, true) then
            set .equip[it.slot] = CreateItem(it.itemId, STORE_ITEM_X, STORE_ITEM_Y)
            call SetItemVisible(.equip[it.slot], false)
            call SetItemDroppable(.equip[it.slot], false)
            call .applyItemStats(it)
            return true
        endif
        return false
    endmethod    
    
    //equips an item for a hero
    public static method equipItem takes unit u, item t, Item it returns boolean
        local thistype this = .table[u]
        local integer itd1 = 0
        local integer itd2 = 0
        local integer slot = 0
        local integer i = 0
        if .checkHero(it, true) then
            if not .isItemEquipped(t) and .isSlotEmpty(it.slot) then
                if not .open then
                    call RemoveItem(.equip[it.slot])
                    set .equip[it.slot] = t
                    call SetItemPosition(.equip[it.slot], STORE_ITEM_X, STORE_ITEM_Y)
                    call SetItemVisible(.equip[it.slot], false)
                    call SetItemDroppable(.equip[it.slot], false)
                    call .applyItemStats(it)
                    return true
                elseif .open then
                    set .dropped = false
                    if .page <= 1 then
                        if it.slot <= 3 then
                            if not (GetItemTypeId(UnitItemInSlot(.Owner, it.slot)) == DUMMY_ITEMS[it.slot]) then
                                if not .getSlotForItem(UnitItemInSlot(.Owner, it.slot)) then
                                    call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
                                    return false
                                endif
                            else
                                call RemoveItem(UnitItemInSlot(.Owner, it.slot))
                            endif
                            call UnitAddItemToSlotById(.Owner, it.itemId, it.slot)
                            call .applyItemStats(it)
                            return true
                        elseif it.slot > 3 then
                            if not (GetItemTypeId(.equip[it.slot]) == DUMMY_ITEMS[it.slot]) then
                                if not .getSlotForItem(.equip[it.slot]) then
                                    call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
                                    return false
                                endif
                            else
                                call RemoveItem(.equip[it.slot])
                            endif
                            set .equip[it.slot] = t
                            call SetItemPosition(.equip[it.slot], STORE_ITEM_X, STORE_ITEM_Y)
                            call SetItemVisible(.equip[it.slot], false)
                            call SetItemDroppable(.equip[it.slot], false)
                            call .applyItemStats(it)
                            return true
                        endif
                    elseif .page >= 2 then
                        if it.slot > 3 then
                            if not (GetItemTypeId(UnitItemInSlot(.Owner, it.slot - 4)) == DUMMY_ITEMS[it.slot]) then
                                if not .getSlotForItem(UnitItemInSlot(.Owner, it.slot - 4)) then
                                    call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
                                    return false
                                endif
                            else
                                call RemoveItem(UnitItemInSlot(.Owner, it.slot - 4))
                            endif
                            call UnitAddItemToSlotById(.Owner, it.itemId, it.slot - 4)
                            call .applyItemStats(it)
                            return true
                        elseif it.slot <= 3 then
                            if not (GetItemTypeId(.equip[it.slot]) == DUMMY_ITEMS[it.slot]) then
                                if not .getSlotForItem(UnitItemInSlot(.Owner, it.slot)) then
                                    call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
                                    return false
                                endif
                            else
                                call RemoveItem(.equip[it.slot])
                            endif
                            set .equip[it.slot] = t
                            call SetItemPosition(.equip[it.slot], STORE_ITEM_X, STORE_ITEM_Y)
                            call SetItemVisible(.equip[it.slot], false)
                            call SetItemDroppable(.equip[it.slot], false)
                            call .applyItemStats(it)
                            return true
                        endif
                    endif
                endif
            elseif not .isSlotEmpty(it.slot) then
                set itd1 = GetItemTypeId(t)
                set slot = .getItemSlot(t)
                call RemoveItem(t)
                set itd2 = GetItemTypeId(.equip[it.slot])
                call RemoveItem(.equip[it.slot])
                call .removeItemStats(Item.getItemById(itd2))
                call .applyItemStats(it)
                set .equip[it.slot] = CreateItem(itd1, STORE_ITEM_X, STORE_ITEM_Y)
                call SetItemVisible(.equip[it.slot], false)
                call UnitAddItemToSlotById(.Owner, itd2, slot)
                call SetItemDroppable(.equip[it.slot], false)
                return true
            endif
        endif
        return false
    endmethod
    
    //un-equips an item for the hero
    public static method unequipItem takes unit u, item t, Item it returns boolean
        local thistype this = .table[u]
        if not .getSlotForItem(t) then
            call SimError(GetOwningPlayer(.Owner), "Inventory and Backpack are full.")
            return false
        else
            call .removeItemStats(it)
            set .equip[it.slot] = UnitAddItemById(.Owner, DUMMY_ITEMS[it.slot])
            return true
        endif       
    endmethod
    
    //Returns true, if the given item is equipped (or an item with the same ItemTypeId)
    public method isItemEquipped takes item whichItem returns boolean //INLINE FRIENDLY
        return GetItemTypeId(.equip[Item.getItemById(GetItemTypeId(whichItem)).slot]) == GetItemTypeId(whichItem)
    endmethod
    
    //Returns true, if the given slot is empty
    public method isSlotEmpty takes integer Slot returns boolean //INLINE FRIENDLY
        return GetItemTypeId(.equip[Slot]) == DUMMY_ITEMS[Slot]
    endmethod
    
    //checks, if the hero has the possibility to stack the given item with another
    public method checkItemForStack takes item for returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(for)
        local integer bonus = 0
        local Item it = Item.getItemById(GetItemTypeId(for))
        if not .open and not .bopen and not it.equippable then
            loop
                exitwhen i > 5
                if GetItemTypeId(UnitItemInSlot(.Owner, i)) == id and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= MAX_ITEM_CHARGES and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= it.maxCharges then
                    set bonus = GetItemCharges(for)
                    call RemoveItem(for)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(UnitItemInSlot(.Owner, i)) + bonus)
                    return true
                endif
                set i = i + 1
            endloop
            
            set i = 0
            loop
                exitwhen i > 7
                if .backp[i] == id and .bcharges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(for) <= it.maxCharges then
                    set bonus = GetItemCharges(for)
                    set .bcharges[i] = .bcharges[i] + bonus
                    call RemoveItem(for)
                    return true
                endif
                set i = i + 1
            endloop
        
        elseif .open then
            loop
                exitwhen i > 5
                if .items[i] == id and .charges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .charges[i] + GetItemCharges(for) <= it.maxCharges then
                    set bonus = GetItemCharges(for)
                    call RemoveItem(for)
                    set .charges[i] = .charges[i] + bonus
                    return true
                endif
                set i = i + 1
            endloop
            
            set i = 0
            loop
                exitwhen i > 7
                if .backp[i] == id and .bcharges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(for) <= it.maxCharges then
                    set bonus = GetItemCharges(for)
                    call RemoveItem(for)
                    set .bcharges[i] = .bcharges[i] + bonus
                    return true
                endif
                set i = i + 1
            endloop
        
        elseif .bopen then
            loop
                exitwhen i > 5
                if .items[i] == id and .charges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .charges[i] + GetItemCharges(for) <= it.maxCharges then
                    set bonus = GetItemCharges(for)
                    call RemoveItem(for)
                    set .charges[i] = .charges[i] + bonus
                    return true
                endif
                set i = i + 1
            endloop
            
            if .bpage <= 1 then
                set i = 0
                loop
                    exitwhen i > 3
                    if GetItemTypeId(UnitItemInSlot(.Owner, i)) == id and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= MAX_ITEM_CHARGES and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= it.maxCharges then
                        set bonus = GetItemCharges(for)
                        call RemoveItem(for)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(UnitItemInSlot(.Owner, i)) + bonus)
                        return true
                    endif
                    set i = i + 1
                endloop 
                
                set i = 4
                loop
                    exitwhen i > 7
                    if .backp[i] == id and .bcharges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(for) <= it.maxCharges then
                        set bonus = GetItemCharges(for)
                        call RemoveItem(for)
                        set .bcharges[i] = .bcharges[i] + bonus
                        return true
                    endif
                    set i = i + 1
                endloop
                
            elseif .bpage >= 2 then
            
                set i = 0
                loop
                    exitwhen i > 3
                    if GetItemTypeId(UnitItemInSlot(.Owner, i)) == id and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= MAX_ITEM_CHARGES and GetItemCharges(UnitItemInSlot(.Owner, i)) + GetItemCharges(for) <= it.maxCharges then
                        set bonus = GetItemCharges(for)
                        call RemoveItem(for)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(UnitItemInSlot(.Owner, i)) + bonus)
                        return true
                    endif
                    set i = i + 1
                endloop
                
                set i = 0
                loop
                    exitwhen i > 3
                    if .backp[i] == id and .bcharges[i] + GetItemCharges(for) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(for) <= it.maxCharges then
                        set bonus = GetItemCharges(for)
                        set .bcharges[i] = .bcharges[i] + bonus
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
            endif
            
        endif
        return false
    endmethod
    
    //checks if the hero has a empty item slot
    public method getSlotForItem takes item for returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(for)
        if not .open and not .bopen then
            loop
                exitwhen i > 5
                if UnitItemInSlot(.Owner, i) == null then
                    call UnitAddItemToSlotById(.Owner, id, i)
                    call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(for))
                    call RemoveItem(for)
                    return true
                endif
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i > 7
                if .backp[i] == 0 then
                    set .backp[i] = id
                    set .bcharges[i] = GetItemCharges(for)
                    call RemoveItem(for)
                    set .bfree = .bfree - 1
                    if not .open or .bopen then
                        call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
                    endif
                    return true
                endif
                set i = i + 1
            endloop
            
        elseif .open then
            loop
                exitwhen i > 5
                if .items[i] == 0 then
                    set .items[i] = id
                    set .charges[i] = GetItemCharges(for)
                    call RemoveItem(for)
                    return true                    
                endif
                set i = i + 1
            endloop
            set i = 0
            
            loop
                exitwhen i > 7
                if .backp[i] == 0 then
                    set .backp[i] = id
                    set .bcharges[i] = GetItemCharges(for)
                    call RemoveItem(for)
                    set .bfree = .bfree - 1
                    if not .open or .bopen then
                        call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
                    endif   
                    return true
                endif
                set i = i + 1
            endloop
            
        elseif .bopen then
        
            if .bpage <= 1 then
                set i = 0
                loop
                    exitwhen i > 3
                    if UnitItemInSlot(.Owner, i) == null then
                        call UnitAddItemToSlotById(.Owner, id, i)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(for))
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop 
                
                set i = 4
                loop
                    exitwhen i > 7
                    if .backp[i] == 0 then
                        set .backp[i] = id
                        set .bcharges[i] = GetItemCharges(for)
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
                
                set i = 0
                loop
                    exitwhen i > 5
                    if .items[i] == 0 then
                        set .items[i] = id
                        set .charges[i] = GetItemCharges(for)
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
                
            elseif .bpage >= 2 then
            
                set i = 0
                loop
                    exitwhen i > 3
                    if UnitItemInSlot(.Owner, i) == null then
                        call UnitAddItemToSlotById(.Owner, id, i)
                        call SetItemCharges(UnitItemInSlot(.Owner, i), GetItemCharges(for))
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
                
                set i = 0
                loop
                    exitwhen i > 3
                    if .backp[i] == 0 then
                        set .backp[i] = id
                        set .bcharges[i] = GetItemCharges(for)
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
                
                set i = 0
                loop
                    exitwhen i > 5
                    if .items[i] == 0 then
                        set .items[i] = id
                        set .charges[i] = GetItemCharges(for)
                        call RemoveItem(for)
                        return true
                    endif
                    set i = i + 1
                endloop
            endif
            
        endif
        return false
    endmethod
    
    //Applies the item's stats to the hero
    private method applyItemStats takes Item it returns nothing
        if it.Ms + GetUnitMoveSpeed(.Owner) > MAX_MOVEMENT_SPEED then
            set .MsBoni[it.slot] = MAX_MOVEMENT_SPEED - GetUnitMoveSpeed(.Owner)
        else
            set .MsBoni[it.slot] = it.Ms
        endif
        call SetUnitMoveSpeed(.Owner, GetUnitMoveSpeed(.Owner) + .MsBoni[it.slot])
        call UnitAddBonus(.Owner, 0, it.Armor)
        call UnitAddBonus(.Owner, 1, it.Dmg)
        call UnitAddBonus(.Owner, 2, it.As)
        call UnitAddBonus(.Owner, 3, it.MpReg)
        call UnitAddBonus(.Owner, 4, it.HpReg)
        call UnitAddBonus(.Owner, 5, it.Str)
        call UnitAddBonus(.Owner, 6, it.Agi)
        call UnitAddBonus(.Owner, 7, it.Int)
        call AddUnitMaxState(.Owner, UNIT_STATE_MAX_LIFE, it.Life)
        call AddUnitMaxState(.Owner, UNIT_STATE_MAX_MANA, it.Mana)
        if it.Effect != "" and it.EffectAttach != "" then
            set .fx[it.slot] = AddSpecialEffectTarget(it.Effect, .Owner, it.EffectAttach)
        endif
        if it.Abil != 0 then
            call UnitAddAbility(.Owner, it.Abil)
        endif
    endmethod
    
    //removes the ItemStats for the hero
    private method removeItemStats takes Item it returns nothing
        if GetUnitMoveSpeed(.Owner) - .MsBoni[it.slot] < MIN_MOVEMENT_SPEED then
            call SetUnitMoveSpeed(.Owner, MIN_MOVEMENT_SPEED)
        else
            call SetUnitMoveSpeed(.Owner, GetUnitMoveSpeed(.Owner) - .MsBoni[it.slot])
        endif
        set .MsBoni[it.slot] = 0.
        call UnitAddBonus(.Owner, 0, -(it.Armor))
        call UnitAddBonus(.Owner, 1, -(it.Dmg))
        call UnitAddBonus(.Owner, 2, -(it.As))
        call UnitAddBonus(.Owner, 3, -(it.MpReg))
        call UnitAddBonus(.Owner, 4, -(it.HpReg))
        call UnitAddBonus(.Owner, 5, -(it.Str))
        call UnitAddBonus(.Owner, 6, -(it.Agi))
        call UnitAddBonus(.Owner, 7, -(it.Int))
        call AddUnitMaxState(.Owner, UNIT_STATE_MAX_LIFE, -(it.Life))
        call AddUnitMaxState(.Owner, UNIT_STATE_MAX_MANA, -(it.Mana))
        if it.Effect != "" and it.EffectAttach != "" then
            call DestroyEffect(.fx[it.slot])
        endif
        if it.Abil != 0 then
            call UnitRemoveAbility(.Owner, it.Abil)
        endif
    endmethod
    
    //returns the slot of an item
    private method getItemSlot takes item it returns integer
        local item temp = null
        local integer i = 0
        loop
            set temp = UnitItemInSlot(.Owner, i)
            exitwhen i > 5 or temp == it
            set i = i + 1
        endloop
        set temp = null
        return i
    endmethod
        
    //Returns the first free Inventory slot of the hero
    private method getFreeSlot takes nothing returns integer
        local integer i = 0
        local integer free
        loop
            exitwhen i > 4
            if .items[i] == 0 then
                set free = i
            endif
            exitwhen .items[i] == 0
            set i = i + 1
        endloop
        return free
    endmethod
    
    //Returns the first free backpack slot of the hero
    private method getFreeBackpackSlot takes nothing returns integer
        local integer i = 0
        local integer free
        loop
            exitwhen i > 7
            if .backp[i] == 0 then
                set free = i
            endif
            exitwhen .backp[i] == 0
            set i = i + 1
        endloop
        return free
    endmethod
    
    //Returns the free place of the hero's backpack
    private method getBackpackPlace takes nothing returns integer
        local integer i = 0
        local integer i2 = 8
        loop
            exitwhen i > 7
            if .backp[i] != 0 then
                set i2 = i2 - 1
            endif
            set i = i + 1
        endloop
        return i2
    endmethod
    
    //Returns the free place of the hero's Inventory
    private method getInventoryPlace takes nothing returns integer
        local integer i = 0
        local integer i2 = 4
        loop
            exitwhen i > 3
            if .items[i] != 0 then
                set i2 = i2 - 1
            endif
            set i = i + 1
        endloop
        return i2
    endmethod
    
    //moves an item to the hero's backpack
    private method moveToBackpack takes item Moved, item Bag returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(Moved)
        local integer Slot
        if .getBackpackPlace() > 0 then
            loop
                exitwhen i > 7
                if .backp[i] == id and .bcharges[i] + GetItemCharges(Moved) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(Moved) <= Item.getItemById(GetItemTypeId(Moved)).maxCharges then
                    set .bcharges[i] = .bcharges[i] + GetItemCharges(Moved)
                    call RemoveItem(Bag)
                    call RemoveItem(Moved)                    
                    call UnitAddItemToSlotById(.Owner, BACKPACK_OPEN_ID, BACKPACK_SLOT)
                    call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
                    return true
                endif
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i > 7
                if .backp[i] == 0 then
                    set .backp[i] = id
                    set .bcharges[i] = GetItemCharges(Moved)
                    set .bfree = .bfree - 1
                    call RemoveItem(Bag)
                    call RemoveItem(Moved)
                    call UnitAddItemToSlotById(.Owner, BACKPACK_OPEN_ID, BACKPACK_SLOT)
                    call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree)
                    return true
                endif
                set i = i + 1
            endloop
        elseif .getBackpackPlace() <= 0 then
            set Slot = .getItemSlot(Moved)
            set i = GetItemCharges(Moved)
            call RemoveItem(Bag)
            call RemoveItem(Moved)      
            call UnitAddItemToSlotById(.Owner, BACKPACK_OPEN_ID, BACKPACK_SLOT)
            call SetItemCharges(UnitItemInSlot(.Owner, BACKPACK_SLOT), .bfree) 
            call UnitAddItemToSlotById(.Owner, id, Slot)
            call SetItemCharges(UnitItemInSlot(.Owner, Slot), i)
            call SimError(GetOwningPlayer(.Owner), "Backpack is full!")
            return false
        endif
        return false
    endmethod
    
    //moves an item from the first page to the second page
    private method moveToSecondPage takes item Moved, item Second returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(Moved)
        local integer Slot
        loop
            exitwhen i > 3
            if .backp[i+4] == id and .bcharges[i+4] + GetItemCharges(Moved) <= MAX_ITEM_CHARGES and .bcharges[i+4] + GetItemCharges(Moved) <= Item.getItemById(GetItemTypeId(Moved)).maxCharges then
                set .bcharges[i+4] = .bcharges[i+4] + GetItemCharges(Moved)
                call RemoveItem(Second)
                call RemoveItem(Moved)     
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_2, 4)
                return true
            endif
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen i > 3
            if .backp[i+4] == 0 then
                set .backp[i+4] = id
                set .bcharges[i+4] = GetItemCharges(Moved)
                call RemoveItem(Second)
                call RemoveItem(Moved)
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_2, 4)
                return true
            endif
            set i = i + 1
        endloop
        
        set Slot = .getItemSlot(Moved)
        set i = GetItemCharges(Moved)
        call RemoveItem(Second)
        call RemoveItem(Moved)      
        call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_2, 4) 
        call UnitAddItemToSlotById(.Owner, id, Slot)
        call SetItemCharges(UnitItemInSlot(.Owner, Slot), i)
        call SimError(GetOwningPlayer(.Owner), "Cannot move item to a full page!")
        return false
    endmethod
    
    //moves an item from the second page to the first page
    private method moveToFirstPage takes item Moved, item First returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(Moved)
        local integer Slot
        loop
            exitwhen i > 3
            if .backp[i] == id and .bcharges[i] + GetItemCharges(Moved) <= MAX_ITEM_CHARGES and .bcharges[i] + GetItemCharges(Moved) <= Item.getItemById(GetItemTypeId(Moved)).maxCharges then
                set .bcharges[i] = .bcharges[i] + GetItemCharges(Moved)
                call RemoveItem(First)
                call RemoveItem(Moved)     
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_1, 4)
                return true
            endif
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen i > 3
            if .backp[i] == 0 then
                set .backp[i] = id
                set .bcharges[i] = GetItemCharges(Moved)
                call RemoveItem(First)
                call RemoveItem(Moved)
                call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_1, 4)
                return true
            endif
            set i = i + 1
        endloop
        
        set Slot = .getItemSlot(Moved)
        set i = GetItemCharges(Moved)
        call RemoveItem(First)
        call RemoveItem(Moved)
        call UnitAddItemToSlotById(.Owner, BACKPACK_PAGE_1, 4) 
        call UnitAddItemToSlotById(.Owner, id, Slot)
        call SetItemCharges(UnitItemInSlot(.Owner, Slot), i)
        call SimError(GetOwningPlayer(.Owner), "Cannot move item to a full page!")
        return false
    endmethod
        
    //moves an item to the heroes inventory
    private method moveToInventory takes item Moved, item Close returns boolean
        local integer i = 0
        local integer id = GetItemTypeId(Moved)
        local integer Slot
        if .getInventoryPlace() > 0 then
            loop
                exitwhen i > 3
                if .items[i] == id and .charges[i] + GetItemCharges(Moved) <= MAX_ITEM_CHARGES and .charges[i] + GetItemCharges(Moved) <= Item.getItemById(GetItemTypeId(Moved)).maxCharges then
                    set .charges[i] = .charges + GetItemCharges(Moved)
                    set .bfree = .bfree + 1
                    call RemoveItem(Close)
                    call RemoveItem(Moved)                    
                    call UnitAddItemToSlotById(.Owner, BACKPACK_CLOSE_ID, 5)
                    return true
                endif
                set i = i + 1
            endloop
            
            set i = .getFreeSlot()
            set .items[i] = id
            set .charges[i] = GetItemCharges(Moved)
            set .bfree = .bfree + 1
            call RemoveItem(Close)
            call RemoveItem(Moved)
            call UnitAddItemToSlotById(.Owner, BACKPACK_CLOSE_ID, 5)
            return true
            
        elseif .getInventoryPlace() <= 0 then
            set Slot = .getItemSlot(Moved)
            set i = GetItemCharges(Moved)
            call RemoveItem(Close)
            call RemoveItem(Moved)      
            call UnitAddItemToSlotById(.Owner, BACKPACK_CLOSE_ID, 5)
            call UnitAddItemToSlotById(.Owner, id, Slot)
            call SetItemCharges(UnitItemInSlot(.Owner, Slot), i)
            call SimError(GetOwningPlayer(.Owner), "Inventory is full!")
            return false
        endif
        return false
    endmethod 
    
    //checks, if a hero can equip an item
    public method checkHero takes Item it, boolean UseError returns boolean
        if it.HeroClasses != "" then
            if not SearchString(it.HeroClasses, .HeroClass) then
                if UseError then
                    call SimError(GetOwningPlayer(.Owner), "Only " + it.HeroClasses + " can equip this item.")
                endif
                return false
            endif
        endif
        if it.ItemClass != "" then
            if not SearchString(.ItemClasses, it.ItemClass) then
                if UseError then
                    call SimError(GetOwningPlayer(.Owner), "This unit can't equip " + it.ItemClass + ".")
                endif
                return false
            endif
        endif
        if GetHeroLevel(.Owner) >= it.Level then
            return true
        else
            if UseError then
                call SimError(GetOwningPlayer(.Owner), "This item requires a hero level of " + I2S(it.Level) + " or greater.")
            endif
        endif
        return false
    endmethod
    
    //will be runned, whenever an item is used
    private static method useItem takes nothing returns boolean   
        local thistype this = .table[GetTriggerUnit()]
        local Item it = Item.getItemById(GetItemTypeId(GetManipulatedItem()))
        if GetItemTypeId(GetManipulatedItem()) == EQUIPMENT_OPEN_ID then
            return .openEquipment(GetTriggerUnit())
            
        elseif GetItemTypeId(GetManipulatedItem()) == BACKPACK_OPEN_ID then
            return .openBackpack(GetTriggerUnit())
            
        elseif GetItemTypeId(GetManipulatedItem()) == EQUIPMENT_CLOSE_ID or GetItemTypeId(GetManipulatedItem()) == BACKPACK_CLOSE_ID then
            if .open then
                return .closeEquipment(GetTriggerUnit())
            elseif .bopen then
                return .closeBackpack(GetTriggerUnit())
            endif
            
        elseif GetItemTypeId(GetManipulatedItem()) == EQUIPMENT_PAGE_1 or GetItemTypeId(GetManipulatedItem()) == EQUIPMENT_PAGE_2 or GetItemTypeId(GetManipulatedItem()) == BACKPACK_PAGE_1 or GetItemTypeId(GetManipulatedItem()) == BACKPACK_PAGE_2 then
            if .open then
                return .changePageEquipment(GetTriggerUnit())
            elseif .bopen then
                return .changePageBackpack(GetTriggerUnit())
            endif
            
        elseif it.equippable and not .isItemEquipped(GetManipulatedItem()) then
            return .equipItem(GetTriggerUnit(), GetManipulatedItem(), it)
        elseif it.equippable and .isItemEquipped(GetManipulatedItem()) then
            return .unequipItem(GetTriggerUnit(), GetManipulatedItem(), it)
        endif
        
        return false
    endmethod
    
    //Will be runned, whenever an item is dragged on another item or on an empty slot
    private static method dragItem takes nothing returns boolean
        local item manipul = GetOrderTargetItem()
        local integer slot = GetIssuedOrderId() - 852002
        local thistype this = .table[GetTriggerUnit()]
        local item dropped = UnitItemInSlot(.Owner, slot)
        local integer id = GetItemTypeId(manipul)
        local integer idd = GetItemTypeId(dropped)
        
        if (GetIssuedOrderId() > 852001) and (GetIssuedOrderId() < 852008) and this != 0 then
        
            if manipul != dropped and id != BACKPACK_PAGE_1 and id != BACKPACK_PAGE_2 and id != BACKPACK_CLOSE_ID and id != BACKPACK_OPEN_ID then
                
                if idd == BACKPACK_OPEN_ID then
                    set .dropped = false
                    call .moveToBackpack(manipul, dropped)
                    set .dropped = true
                    set manipul = null
                    set dropped = null
                    return false
                    
                elseif idd == BACKPACK_CLOSE_ID then
                    set .dropped = false
                    call .moveToInventory(manipul, dropped)
                    set .dropped = true
                    set manipul = null
                    set dropped = null
                    return false
                    
                elseif idd == BACKPACK_PAGE_2 then
                    set .dropped = false
                    call .moveToSecondPage(manipul, dropped)
                    set .dropped = true
                    set manipul = null
                    set dropped = null
                    return false
                    
                elseif idd == BACKPACK_PAGE_1 then
                    set .dropped = false
                    call .moveToFirstPage(manipul, dropped)
                    set .dropped = true
                    set manipul = null
                    set dropped = null
                    return false
                endif
            endif
        endif
        
        if id == BACKPACK_OPEN_ID or id == BACKPACK_CLOSE_ID or id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2 and dropped == null then
            
            if id == BACKPACK_OPEN_ID and not .bopen and not .open then
                call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
                call addItem.create(manipul, BACKPACK_SLOT, this)
                set manipul = null
                set dropped = null
                return false
                
            elseif id == BACKPACK_CLOSE_ID or id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2 and .bopen then
                call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
                if id == BACKPACK_CLOSE_ID then
                    call addItem.create(manipul, 5, this)
                elseif id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2 then
                    call addItem.create(manipul, 4, this)
                endif
                set manipul = null
                set dropped = null
                return false
            endif
        endif
        
        if (id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2) and idd == BACKPACK_CLOSE_ID and .bopen then
            call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
            call addItem.create(manipul, 4, this)  
            call addItem.create(dropped, 5, this)
            set manipul = null
            set dropped = null
            return false
            
        elseif id == BACKPACK_CLOSE_ID and (idd == BACKPACK_PAGE_1 or idd == BACKPACK_PAGE_2) and .bopen then        
            call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
            call addItem.create(manipul, 5, this)  
            call addItem.create(dropped, 4, this)
            set manipul = null
            set dropped = null
            return false
            
        endif        
        set manipul = null
        set dropped = null
        return true
    endmethod
    
    //will be runned whenever an item is picked up
    private static method pickupItem takes nothing returns boolean
        local thistype this = .table[GetTriggerUnit()]
        local item i = GetManipulatedItem()
        local Item it = Item.getItemById(GetItemTypeId(i))
        
        if AUTO_EQUIP and it.equippable and .isSlotEmpty(it.slot) and .checkHero(it, false) and .allowauto then        
            call thistype.equipItem(GetTriggerUnit(), GetManipulatedItem(), it)
            set i = null
            return true
        endif
        
        return false
    endmethod
    
    //Will be runned whenever an item is dropped
    private static method dropItem takes nothing returns boolean
        local thistype this = .table[GetTriggerUnit()]
        local item ite = GetManipulatedItem()
        local integer id = GetItemTypeId(ite)
        if this == 0 or ite == null or not .dropped then
            return false
            
        elseif this != 0 and ite != null and .dropped then
        
            if id == BACKPACK_OPEN_ID and not .bopen and not .open then
                call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
                call addItem.create(ite, BACKPACK_SLOT, this)
                set ite = null
                return true
                
            elseif id == BACKPACK_CLOSE_ID or id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2 and (.bopen or .open) then
                
                if id == BACKPACK_CLOSE_ID then
                    call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
                    call addItem.create(ite, 5, this)
                    set ite = null
                    return true
                    
                elseif id == BACKPACK_PAGE_1 or id == BACKPACK_PAGE_2 then
                    call SimError(GetOwningPlayer(.Owner), "Cannot drop this item.")
                    call addItem.create(ite, 4, this)
                    set ite = null
                    return true
                endif
            endif
        endif
        return false
    endmethod
    
    //initialization of the Inventory struct
    private static method onInit takes nothing returns nothing
        call TriggerAddCondition(.Use, Condition(function thistype.useItem))
        call TriggerAddCondition(.Pickup, Condition(function thistype.pickupItem))
        call TriggerAddCondition(.Drag, Condition(function thistype.dragItem))
        call TriggerAddCondition(.Drop, Condition(function thistype.dropItem))
        
        set .table = HandleTable.create()
        call SETUP_DUMMY_ITEMS()
    endmethod
    
endstruct
    
endlibrary


JASS:
library ItemList

globals
    //If true, item abilitys will be preloaded on map init, when adding one to the systen (longer laod time, but no laggs if first time used/equipped)
    private constant boolean PRELOAD_ABILITYS       = false
    //The unit for preloading item abilitys
    private constant integer PRELOAD_DUMMY_ID       = 'hpea'
endglobals


//The struct for each item, you have registred
struct Item
    integer Life = 0
    integer Mana = 0
    integer Str = 0
    integer Agi = 0
    integer Int = 0
    integer Dmg = 0
    integer Armor = 0
    integer Level = 1
    integer HpReg = 0
    integer MpReg = 0
    integer As = 0
    integer Abil = 0
    real Ms = 0.
    string Effect = ""
    string EffectAttach = ""
    
    string ItemClass
    string HeroClasses
    
    private integer ItemId = 0
    private integer Slot
    private boolean AllowStacks = false
    private boolean Equipment = true
    private integer id = 0
    private integer MaxStacks = 0
    
    private static Table table
    private static boolean allowAdd = true
    private static trigger Disabler = CreateTrigger()
    private static unit dummy = null
    
    //Use this method to register an Item, which IS equippable to the system
    //Example: call Item.addEquipment('I000', 2, "Bloogmages", "Cloth Armors")
    //This would register the item as a weapon (see above), which ItemTypeId is 'I000' to the system
    //has the ItemClass "Cloth Armors" and is only equippable by "Bloodmages"
    static method addEquipment takes integer ItemId, integer Slot, string availableHeroClasses, string itemClass returns thistype
        local thistype this = 0 
        if .allowAdd then
            set this = .allocate()
            set .ItemId = ItemId
            set .Slot = Slot
            set .HeroClasses = availableHeroClasses
            set .ItemClass = itemClass
            set .id = .id + 1
            set .table[.ItemId] = this
            return this
        debug else
            call BJDebugMsg("Can only register Items in Map Initialization")
        endif
        return 0
    endmethod
    
    //Use this method to register an Item, which IS NOT equippable to the system
    //Example: call Item.addNormalItem('I001', true, 6)
    //This would register the item, which ItemTypeId is 'I001' to the system, enables Stacks for it and sets its MaxStacks to 6
    static method addNormalItem takes integer ItemId, boolean AllowStacks, integer MaxStacks returns thistype
        local thistype this = 0
        if .allowAdd then
            set this = .allocate()
            set .ItemId = ItemId
            set .Equipment = false
            set .MaxStacks = MaxStacks
            set .id = .id + 1
            set .table[.ItemId] = this
            return this
        debug else
            call BJDebugMsg("Can only register Items in Map Initialization")
        endif
        return 0
    endmethod
    
    //call this method after adding an ability to an item. If you set the boolean "inSpellbook" to true, 
    //the system disables the spellbook for every player (not the ability in it!)
    method HasAbility takes boolean inSpellbook returns nothing
        local integer i = 0
        if PRELOAD_ABILITYS then
            call UnitAddAbility(.dummy, .Abil)
            call UnitRemoveAbility(.dummy, .Abil)
        endif
        if inSpellbook then
            loop
                call SetPlayerAbilityAvailable(Player(i), .Abil, false)
                set i = i + 1
                exitwhen i >= bj_MAX_PLAYER_SLOTS
            endloop
        endif
    endmethod
    
    //use this to get the slot of the item. Returns -1 if the item isn't equipable
    method operator slot takes nothing returns integer
        if .Equipment then
            return .Slot
        else
            return -1
        endif
        return -1
    endmethod
    
    //returns true, if item is equippable
    method operator equippable takes nothing returns boolean //INLINE FRIENDLY
        return .Equipment
    endmethod
    
    //Returns the item Id of the used Item
    //Can be used for Save & Load Systems
    method operator itemId takes nothing returns integer //INLINE FRIENDLY
        return .ItemId
    endmethod
    
    //Returns the MaxCharges of an item
    method operator maxCharges takes nothing returns integer //INLINE FRIENDLY
        return .MaxStacks
    endmethod
    
    //Returns the Item struct by using its index
    static method getItemByIndex takes integer Id returns Item //INLINE FRIENDLY
        return Item(Id)
    endmethod
    
    //Returns the Item Struct by the item Type Id
    static method getItemById takes integer ItemId returns Item //INLINE FRIENDLY
        return Item(.table[ItemId])
    endmethod

    //Disables the registring after map Initialization
    private static method disableAdd takes nothing returns boolean
        set .allowAdd = false
        call DestroyTrigger(.Disabler)
        if PRELOAD_ABILITYS then
            call KillUnit(.dummy)
        endif
        return .allowAdd
    endmethod
        
    //self-explanatory
    private static method onInit takes nothing returns nothing
        if PRELOAD_ABILITYS then
            set .dummy = CreateUnit(Player(15), PRELOAD_DUMMY_ID, 0., 0., 0.)
        endif
        call TriggerRegisterTimerEvent(.Disabler, 0., false)
        call TriggerAddCondition(.Disabler, Condition(function Item.disableAdd))
        set .table = Table.create()  
    endmethod
    
endstruct

endlibrary


Please give credits to the above named mapper, if you use this system and to me :D

Keywords:
Inventory, advanced, items, custom, hero classes, item classes, equipment, bag, backpack, stack, charges
Contents

Inventory System v2.01b (Map)

Reviews
14:46, 8th Oct 2009 The_Reborn_Devil: The code looks very nice and I couldn't find a leak. This is definitely useful for someone. Approved. I give this system the rating Recommended.

Moderator

M

Moderator

14:46, 8th Oct 2009
The_Reborn_Devil:

The code looks very nice and I couldn't find a leak.
This is definitely useful for someone.
Approved.
I give this system the rating Recommended.
 
Level 8
Joined
Aug 2, 2008
Messages
193
Hi,

@Godslayer:
I really don't know, how i could add active abilitys to items...

@xxdingo93xx:
Yeah, i made this for my RPG and liked the AAA Inventory, but this is very old and not 1.24 compatible, so I made it by my own ;)

@Deuterium:
Will post the Code of the required System Librarys (Inventory System and Item List) soon.

Edit:
Will upload the code, after I added a small feature, which allows the user to setup, if an picked item will be auto eqipped, if the slot for the item is empty-
 
Hell, I checked the code, and I liked it.
Although, I was making nearly the same system.
But mine works as following:

I used hashtables.

And also mine is able to use the bonus systems,
but unlike yours, its not an requirement.

Mine also allows automaticly pickup and drop events
which refresh the inventory. And you can have items
in your inventory which are not shown. Unlike yours.

And, mine is limited to the 6 slot inventory yet, but its no problem
you can do a workaround...

So in all:

Yours is more advanced getting to the UnitMaxMod and such,
because mine does not require them. But with mine you can
also stack items, combine them and make many more thing.

I would look forward seeing a system without the pages but with
the bonus mod.
 
Level 8
Joined
Aug 2, 2008
Messages
193
Of course you could add stackable items an so on, but i think for an equippment system, that isn't needed^^
At the beginning, I also wanted to use a hashtable for the items but structs are quite faster ;)
But what do you mean with "Mine also allows automaticly pickup and drop events
which refresh the inventory"
In Version 1.02, the inventory can auto-equip items, if their slot is empty. Maybe I'll add the feature, that you can drop the items directly from your inventory on the ground, will look, how i could do that...
But why do you want invisible equipped items? Don't see any feature of it.
And i wanted to have the system as simple as possible to create new Items and ingame, I think thats, what it is or?^^
 
Of course you could add stackable items and so on, but i think for an equippment system, that isn't needed^^
Ok, I have an collection, you only a single system.

At the beginning, I also wanted to use a hashtable for the items but structs are quite faster ;)
Sure, but hashtables are easier to use.

But what do you mean with "Mine also allows automaticly pickup and drop events which refresh the inventory"
Normally, when you drop items with my system, it applies effects of the itemtype to the hero and while being dropped, they get removed. The item will also instantly be added to the inventory object of the unit. (Which is also automaticly created if you want)

In Version 1.02, the inventory can auto-equip items, if their slot is empty. Maybe I'll add the feature, that you can drop the items directly from your inventory on the ground, will look, how i could do that...
Yeah, something like that.

But why do you want invisible equipped items? Don't see any feature of it.
You could add a window to manage your inventory items and don't have to use the damn 6 slot inventory for gods sake.

And i wanted to have the system as simple as possible to create new Items and ingame, I think thats, what it is or?^^
My system would be even easier, since you don't even need to call anything ingame if you don't want to, and it would handle the items nonetheless.
 
Level 8
Joined
Aug 2, 2008
Messages
193
Hmmm
I like using structs more like hashtables, because in my opinion, they are very simple to use ;)
And I wanna add a small bagpack system to the Inventory, giving the hero 8 more slots (also with pages^^). That would use Drag and Drop, will look how I'll do that best...
 
Level 8
Joined
Aug 2, 2008
Messages
193
Important!!

Hi,

I'll gonna update the whole system in a short time, with alot new features.


  • New 8 Slot Backpack
  • Automatic Stack Detection
  • Fully triggered Item Pickup
  • Drag and Drop (used by the Backpack)
  • Some more Item Configurations
  • Alot code changes (bugs, leaks and so on)
 

  • New 8 Slot Backpack
  • Automatic Stack Detection
  • Fully triggered Item Pickup
  • Drag and Drop (used by the Backpack)
  • Some more Item Configurations
  • Alot code changes (bugs, leaks and so on)
Looks sweet :D I have also made my system, but it has no stack detection.

Do I know have the ability to give items spells without removing the posability to remove them by inventory / by just drop them?

What else does this system provide to make it better as the other ones?
 
Level 8
Joined
Aug 2, 2008
Messages
193
In my tests, it is a little bit faster.
And I'll explain the Stack System a little bit more exactly:
When you give your unit an order to pickup an item, the system will detect that, and just orders the unit to move to the item. When the distance between caster and item is smaller then a constant value, the item will be added on this way:
1. Check, if the item is equippable, autopick if enabled, its equipment slot is empty and the unit can wear the item: Equip it.
2. If 1 isn't true, the sytem will checks, if the unit has any type of this item in its inventory and backpack. The start of the serach depends on the current opened "inventory". If an item of this type was found, it will be stacked with it, if its stack + the item's stack is smaller than MAX_STACK_SIZE and the item's stack size.
3. If 2 isn't true, the system will search for the first free slot, where this item can be stored, also depending on the current opened inventory. So if I have opened page 2, it will first serach for the backpack page 2, then the normal inventory and at last, the first backpack page.
4. If 4 also not true, you become an error, that you inventory and backpack is full :)

And maybe, I'll change the way of equipping an item to drag and drop... maybe...^^
 
Level 8
Joined
Aug 2, 2008
Messages
193
thanks :D
w8 for the update ^^

Edit:
Can't update today, because of a big bug..
Will udate it tomorrow i think...

Edit2:
The bug is fixed, the code is nearly done. Just want to add the possibility to equip items with opened equipment menu :D
Btw, the code has atm exactly 1337 Lines of code^^

Edit3:
It's nearly done. Iv'e just added a small feature to allow to equip items with opened equipment menu page.
 
Last edited:
Level 8
Joined
Aug 2, 2008
Messages
193
You don't need to do that :D
It would be an easy calculation:

When i add movespeed:

JASS:
local real bonus = it.ms
if bonus + GetUnitMoveSpeed(.Owner) > MAX_MOVE_SPEED then
    set bonus = MAX_MOVE_SPEED - GetUnitMoveSpeed(.Owner)
endif
call SetUnitMoveSpeed(.Owner, GetUnitMoveSpeed(.Owner) + bonus)

Edit:
The same would be with MIN_MOVE_SPEED I think...
If someone has an MIN_MOVE_SPEED of 200 for example..
 

JASS:
//////////////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ Bonus Mod
//@=======================================================================================
//@ Credits:
//@---------------------------------------------------------------------------------------
//@ Written by:
//@  dhk_undead_lord aka Anachron
//@ Based on the work of:
//@  Earth-Fury
//@  weaaddar
//@
//@ If you use this system, please credit all of the people mentioned above in your map.
//@=======================================================================================
//@ Bonus Mod Readme
//@---------------------------------------------------------------------------------------
//@
//@ BonusMod is a system for adding bonuses to a single unit. For example, you may wish
//@ to add a +40 damage bonus, or a -3 armor 'bonus'. Bonus mod works by adding abilitys
//@ to a unit which effect the particular stat by a power of two. By combining diffrent
//@ powers of two, you can reach any number between 0 and 2^(n+1) - 1, where n is the
//@ largest power of 2 used. Bonus mod can also apply negative bonuses, by adding an
//@ ability which has a 'bonus' of -2^(n+1), where again, n is the maximum power of 2.
//@ With the negative bonus, you can add anywhere between 1 and 2^(n+1)-1 of a bonus. This
//@ gives bonus mod a range of bonuses between -2^(n+1) and 2^(n+1)-1. By default, n is
//@ set at 11, giving us a range of bonuses between -4096 and +4095.
//@
//@---------------------------------------------------------------------------------------
//@ Adding Bonus Mod to your map:
//@
//@ Copy this library in to a trigger named "BonusMod" in your map.
//@
//@ After the script is copied, the hard part begins. You will have to transfer all of the
//@ bonus abilitys found in this map to yours. However, this is really easy to do if you
//@ are using the JASS NewGen editor. (Which you will have to be anyway, considering this
//@ system is written in vJASS.) Included with this library are macros for the Object
//@ Merger included in NewGen. Simply copy the Object Merger script included with this
//@ system in to your map in its own trigger. Save your map. (Saving will take a while.
//@ Up to 5 min if you have a slow computer.) Close your map, and reopen it. Disable the
//@ trigger you copied the ObjectMerger script in to.
//@ Your map now has all the abilitys it needs!
//@
//@---------------------------------------------------------------------------------------
//@ Functions:
//@
//@ boolean UnitSetBonus(unit <target unit>, integer <bonus type>, integer <bonus ammount>)
//@
//@ This function clears any previously applied bonus on <target unit>, setting the
//@ unit's bonus for <bonus type> to <bonus ammount>. <bonus type> should be one of the
//@ integer type constants below. This function will return false if the desired bonus is
//@ not a valid bonus type, or out of the range of bonuses that can be applied.
//@
//@ integer UnitGetBonus(unit <target unit>, integer <bonus type>)
//@
//@ Returns the bonus ammount of <bonus type> currently applied to <target unit>.
//@
//@ boolean UnitAddBonus(unit <target unit>, integer <bonus type>, integer <bonus ammount>)
//@
//@ This function will add <bonus ammount> to the bonus of type <bonus type> on the
//@ unit <target unit>. <bonus ammount> can be a negative value. This function will return
//@ false if the new bonus will be out of the range which bonus mod can apply.
//@
//@ nothing UnitClearBonus(unit <target unit>, integer <bonus type>)
//@
//@ This function will effectively set the bonus of type <bonus type> for the unit
//@ <target unit> to 0. It is advised you use this function over UnitSetBonus(..., ..., 0)
//@
//@---------------------------------------------------------------------------------------
//@ Variables:
//@
//@ BonusMod_MaxBonus
//@ The maximum bonus that Bonus Mod can apply
//@ BonusMod_MinBonus
//@ The minimum bonus that Bonus Mod can apply
//@---------------------------------------------------------------------------------------
//@ Increasing the Range of Bonuses:
//@
//@ By default, bonus mod uses 13 abilitys per bonus type. This gives each bonus type a
//@ range of -4096 to +4095. To increase this range, you will have to create one new
//@ ability for each ability, for each power of two you increase bonus mod by. You will
//@ also have to edit the negative bonus ability to apply a bonus of -2^(n+1), where n is
//@ the largest power of two you will be using for positive bonuses. You will need to edit
//@ the ABILITY_COUNT constant found below to reflect the new total number of abilitys
//@ each individual bonus will use. You will also have to add the abilitys to the function
//@ InitializeAbilitys. Note that the number in the array index indicates which power of
//@ 2 is held there. So, for instance, set BonusAbilitys[i + 15] would hold an ability
//@ which changes the relivent stat by 32768. (2^15 = 32768) The last ability in the array
//@ must apply a negative bonus.
//@
//@ Here is an example of the bonus BONUS_ARMOR using 15 abilitys instead of 12:
//@
//@ set i = BONUS_ARMOR * ABILITY_COUNT
//@ set BonusAbilitys[i + 0] = 'ZxA0' // +1
//@ set BonusAbilitys[i + 1] = 'ZxA1' // +2
//@ set BonusAbilitys[i + 2] = 'ZxA2' // +4
//@ set BonusAbilitys[i + 3] = 'ZxA3' // +8
//@ set BonusAbilitys[i + 4] = 'ZxA4' // +16
//@ set BonusAbilitys[i + 5] = 'ZxA5' // +32
//@ set BonusAbilitys[i + 6] = 'ZxA6' // +64
//@ set BonusAbilitys[i + 7] = 'ZxA7' // +128
//@ set BonusAbilitys[i + 8] = 'ZxA8' // +256
//@ set BonusAbilitys[i + 9] = 'ZxA9' // +512
//@ set BonusAbilitys[i + 10] = 'ZxAa' // +1024
//@ set BonusAbilitys[i + 11] = 'ZxAb' // +2048
//@ set BonusAbilitys[i + 12] = 'ZxAc' // +4096
//@ set BonusAbilitys[i + 13] = 'ZxAd' // +8192
//@ set BonusAbilitys[i + 14] = 'ZxAe' // +16384
//@ set BonusAbilitys[i + 15] = 'ZxAf' // -32768
//@
//@---------------------------------------------------------------------------------------
//@ Adding and Removing Bonus Types:
//@
//@ Removing a bonus type is simple. First, delete it from the list of constants found
//@ below. Make sure the constants are numberd 0, 1, 2, 3, etc. without any gaps. Change
//@ the BONUS_TYPES constant to reflect the new number of bonuses. You must then remove
//@ the lines of array initialization for the bonus you removed from the
//@ InitializeAbilitys function. You can then delete the abilitys for that bonus type, and
//@ you are then done removing a bonus type.
//@
//@ Adding a bonus type is done in much the same way. Add a constant for it to the list of
//@ constants below, ensuring they are numberd 0, 1, 2, 3 etc. withour any gaps. Change
//@ the BONUS_TYPES constant to reflect the new number of bonuses. You must then create
//@ all the needed abilitys for the new bonus type. Ensure the bonus they each apply is a
//@ power of 2, as with the already included bonuses. See the section Increasing the Range
//@ of Bonuses for more information. After all the abilitys are added, you must add the
//@ needed lines to the InitializeAbilitys function. The existing lines should be a clear
//@ enogh example.
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//////////////////////////////////////////////////////////////////////////////////////////
library BonusMod initializer Initialize requires BonusModMS

globals
//========================================================================================
// Bonus Type Constants
//========================================================================================

    constant integer BONUS_ARMOR = 0 // Armor Bonus
    constant integer BONUS_DAMAGE = 1 // Damage Bonus
    constant integer BONUS_SIGHT_RANGE = 2 // Sight Range Bonus
    constant integer BONUS_MANA_REGEN = 3 // Mana Regeneration Bonus (A % value)
    constant integer BONUS_LIFE_REGEN = 4 // Life Regeneration Bonus (An absolute value)
    constant integer BONUS_HERO_STR = 5 // Strength Bonus
    constant integer BONUS_HERO_AGI = 6 // Agility Bonus
    constant integer BONUS_HERO_INT = 7 // Intelligence Bonus
    constant integer BONUS_SPEED_MOVE = 8 // Movespeed Bonus (A % value)
    constant integer BONUS_SPEED_ATTACK = 9 // Attackspeed Bonus (A % value)

    // The number of bonus type constants above:
    constant integer BONUS_TYPES = 10

//========================================================================================
// Other Configuration
//========================================================================================

    // The number of abilitys used per bonus type:
    private constant integer ABILITY_COUNT = 13

    // Note: Setting the following to false will decrease loading time, but will cause a
    // small ammount of lag when a bonus is first applied. (Especially a negative bonus)
    // If set to true, all BonusMod abilitys will be preloaded:
    private constant boolean PRELOAD_ABILITYS = true

    // Only applies if PRELOAD_ABILITYS is set to true.
    // The unit type used to preload abilitys on:
    private constant integer PRELOAD_DUMMY_UNIT = 'hpea'
endglobals

//========================================================================================
// Ability Initialization
//----------------------------------------------------------------------------------------
// The following function is used to define the rawcodes for all the abilitys bonus mod
// uses. If you use the text macros included with BonusMod, and if you do not wish to add,
// remove, or change the range of bonuses, you will not have to edit the following.
//
// Note that if your map already has abilitys with rawcodes that begin with Zx followed by
// an upper-case letter, the ObjectMerger macros included with this library will not work
// and you will have to edit the lines below. However, you could use the find and replace
// feature in JASS NewGen's Trigger Editor Syntax Highlighter to replace all occurances of
// Zx both here and in the ObjectMerger macros to ease configuration.
//========================================================================================
private keyword BonusAbilitys
private function InitializeAbilitys takes nothing returns nothing
    local integer i

    // Bonus Mod - Armor abilitys
    set i = BONUS_ARMOR * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxA0' // +1
    set BonusAbilitys[i + 1] = 'ZxA1' // +2
    set BonusAbilitys[i + 2] = 'ZxA2' // +4
    set BonusAbilitys[i + 3] = 'ZxA3' // +8
    set BonusAbilitys[i + 4] = 'ZxA4' // +16
    set BonusAbilitys[i + 5] = 'ZxA5' // +32
    set BonusAbilitys[i + 6] = 'ZxA6' // +64
    set BonusAbilitys[i + 7] = 'ZxA7' // +128
    set BonusAbilitys[i + 8] = 'ZxA8' // +256
    set BonusAbilitys[i + 9] = 'ZxA9' // +512
    set BonusAbilitys[i + 10] = 'ZxAa' // +1024
    set BonusAbilitys[i + 11] = 'ZxAb' // +2048
    set BonusAbilitys[i + 12] = 'ZxAc' // -4096

    // Bonus Mod - Damage abilitys
    set i = BONUS_DAMAGE * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxB0' // +1
    set BonusAbilitys[i + 1] = 'ZxB1' // +2
    set BonusAbilitys[i + 2] = 'ZxB2' // +4
    set BonusAbilitys[i + 3] = 'ZxB3' // +8
    set BonusAbilitys[i + 4] = 'ZxB4' // +16
    set BonusAbilitys[i + 5] = 'ZxB5' // +32
    set BonusAbilitys[i + 6] = 'ZxB6' // +64
    set BonusAbilitys[i + 7] = 'ZxB7' // +128
    set BonusAbilitys[i + 8] = 'ZxB8' // +256
    set BonusAbilitys[i + 9] = 'ZxB9' // +512
    set BonusAbilitys[i + 10] = 'ZxBa' // +1024
    set BonusAbilitys[i + 11] = 'ZxBb' // +2048
    set BonusAbilitys[i + 12] = 'ZxBc' // -4096

    // Bonus Mod - Sight Range abilitys
    set i = BONUS_SIGHT_RANGE * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxC0' // +1
    set BonusAbilitys[i + 1] = 'ZxC1' // +2
    set BonusAbilitys[i + 2] = 'ZxC2' // +4
    set BonusAbilitys[i + 3] = 'ZxC3' // +8
    set BonusAbilitys[i + 4] = 'ZxC4' // +16
    set BonusAbilitys[i + 5] = 'ZxC5' // +32
    set BonusAbilitys[i + 6] = 'ZxC6' // +64
    set BonusAbilitys[i + 7] = 'ZxC7' // +128
    set BonusAbilitys[i + 8] = 'ZxC8' // +256
    set BonusAbilitys[i + 9] = 'ZxC9' // +512
    set BonusAbilitys[i + 10] = 'ZxCa' // +1024
    set BonusAbilitys[i + 11] = 'ZxCb' // +2048
    set BonusAbilitys[i + 12] = 'ZxCc' // -4096

    // Bonus Mod - Mana Regen abilitys
    set i = BONUS_MANA_REGEN * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxD0' // +1
    set BonusAbilitys[i + 1] = 'ZxD1' // +2
    set BonusAbilitys[i + 2] = 'ZxD2' // +4
    set BonusAbilitys[i + 3] = 'ZxD3' // +8
    set BonusAbilitys[i + 4] = 'ZxD4' // +16
    set BonusAbilitys[i + 5] = 'ZxD5' // +32
    set BonusAbilitys[i + 6] = 'ZxD6' // +64
    set BonusAbilitys[i + 7] = 'ZxD7' // +128
    set BonusAbilitys[i + 8] = 'ZxD8' // +256
    set BonusAbilitys[i + 9] = 'ZxD9' // +512
    set BonusAbilitys[i + 10] = 'ZxDa' // +1024
    set BonusAbilitys[i + 11] = 'ZxDb' // +2048
    set BonusAbilitys[i + 12] = 'ZxDc' // -4096

    // Bonus Mod - Life Regen abilitys
    set i = BONUS_LIFE_REGEN * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxE0' // +1
    set BonusAbilitys[i + 1] = 'ZxE1' // +2
    set BonusAbilitys[i + 2] = 'ZxE2' // +4
    set BonusAbilitys[i + 3] = 'ZxE3' // +8
    set BonusAbilitys[i + 4] = 'ZxE4' // +16
    set BonusAbilitys[i + 5] = 'ZxE5' // +32
    set BonusAbilitys[i + 6] = 'ZxE6' // +64
    set BonusAbilitys[i + 7] = 'ZxE7' // +128
    set BonusAbilitys[i + 8] = 'ZxE8' // +256
    set BonusAbilitys[i + 9] = 'ZxE9' // +512
    set BonusAbilitys[i + 10] = 'ZxEa' // +1024
    set BonusAbilitys[i + 11] = 'ZxEb' // +2048
    set BonusAbilitys[i + 12] = 'ZxEc' // -4096

    // Bonus Mod - Hero STR abilitys
    set i = BONUS_HERO_STR * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxF0' // +1
    set BonusAbilitys[i + 1] = 'ZxF1' // +2
    set BonusAbilitys[i + 2] = 'ZxF2' // +4
    set BonusAbilitys[i + 3] = 'ZxF3' // +8
    set BonusAbilitys[i + 4] = 'ZxF4' // +16
    set BonusAbilitys[i + 5] = 'ZxF5' // +32
    set BonusAbilitys[i + 6] = 'ZxF6' // +64
    set BonusAbilitys[i + 7] = 'ZxF7' // +128
    set BonusAbilitys[i + 8] = 'ZxF8' // +256
    set BonusAbilitys[i + 9] = 'ZxF9' // +512
    set BonusAbilitys[i + 10] = 'ZxFa' // +1024
    set BonusAbilitys[i + 11] = 'ZxFb' // +2048
    set BonusAbilitys[i + 12] = 'ZxFc' // -4096

    // Bonus Mod - Hero AGI abilitys
    set i = BONUS_HERO_AGI * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxG0' // +1
    set BonusAbilitys[i + 1] = 'ZxG1' // +2
    set BonusAbilitys[i + 2] = 'ZxG2' // +4
    set BonusAbilitys[i + 3] = 'ZxG3' // +8
    set BonusAbilitys[i + 4] = 'ZxG4' // +16
    set BonusAbilitys[i + 5] = 'ZxG5' // +32
    set BonusAbilitys[i + 6] = 'ZxG6' // +64
    set BonusAbilitys[i + 7] = 'ZxG7' // +128
    set BonusAbilitys[i + 8] = 'ZxG8' // +256
    set BonusAbilitys[i + 9] = 'ZxG9' // +512
    set BonusAbilitys[i + 10] = 'ZxGa' // +1024
    set BonusAbilitys[i + 11] = 'ZxGb' // +2048
    set BonusAbilitys[i + 12] = 'ZxGc' // -4096

    // Bonus Mod - Hero INT abilitys
    set i = BONUS_HERO_INT * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxH0' // +1
    set BonusAbilitys[i + 1] = 'ZxH1' // +2
    set BonusAbilitys[i + 2] = 'ZxH2' // +4
    set BonusAbilitys[i + 3] = 'ZxH3' // +8
    set BonusAbilitys[i + 4] = 'ZxH4' // +16
    set BonusAbilitys[i + 5] = 'ZxH5' // +32
    set BonusAbilitys[i + 6] = 'ZxH6' // +64
    set BonusAbilitys[i + 7] = 'ZxH7' // +128
    set BonusAbilitys[i + 8] = 'ZxH8' // +256
    set BonusAbilitys[i + 9] = 'ZxH9' // +512
    set BonusAbilitys[i + 10] = 'ZxHa' // +1024
    set BonusAbilitys[i + 11] = 'ZxHb' // +2048
    set BonusAbilitys[i + 12] = 'ZxHc' // -4096
    
    // Bonus Mod - Move Speed Abilities
    // Doesn't have any, because it uses the other system.
    
    // Bonus Mod - Attack Speed Abilities
    set i = BONUS_SPEED_ATTACK * ABILITY_COUNT
    set BonusAbilitys[i + 0] = 'ZxJ0' // +1
    set BonusAbilitys[i + 1] = 'ZxJ1' // +2
    set BonusAbilitys[i + 2] = 'ZxJ2' // +4
    set BonusAbilitys[i + 3] = 'ZxJ3' // +8
    set BonusAbilitys[i + 4] = 'ZxJ4' // +16
    set BonusAbilitys[i + 5] = 'ZxJ5' // +32
    set BonusAbilitys[i + 6] = 'ZxJ6' // +64
    set BonusAbilitys[i + 7] = 'ZxJ7' // +128
    set BonusAbilitys[i + 8] = 'ZxJ8' // +256
    set BonusAbilitys[i + 9] = 'ZxJ9' // +512
    set BonusAbilitys[i + 10] = 'ZxJa' // +1024
    set BonusAbilitys[i + 11] = 'ZxJb' // +2048
    set BonusAbilitys[i + 12] = 'ZxJc' // -4096
endfunction

//========================================================================================
// System Code
//----------------------------------------------------------------------------------------
// Do not edit below this line unless you wish to change the way the system works.
//========================================================================================

globals
    // Contains all abilitys in a two-dimensional structure:
    private integer array BonusAbilitys

    // Precomputed powers of two to avoid speed and rounding issues with Pow():
    private integer array PowersOf2

    // Range constants (Read only please):
    public integer MaxBonus
    public integer MinBonus
endglobals

function UnitClearBonus takes unit u, integer bonusType returns nothing
    local integer i = 0

    if bonusType == BONUS_SPEED_MOVE then
        call BonusModMS_ClearMoveSpeed(u)
        return
    endif

    loop
        call UnitRemoveAbility(u, BonusAbilitys[bonusType * ABILITY_COUNT + i])

        set i = i + 1
        exitwhen i == ABILITY_COUNT - 2
    endloop
endfunction

function UnitSetBonus takes unit u, integer bonusType, integer ammount returns boolean
    local integer i = ABILITY_COUNT - 2

    if bonusType == BONUS_SPEED_MOVE then
        call BonusModMS_ChangeMoveSpeed(u, ammouont)
        return
    endif

    if ammount < MinBonus or ammount > MaxBonus then
        debug call BJDebugMsg("BonusSystem Error: Bonus too high or low (" + I2S(ammount) + ")")
        return false
    elseif bonusType < 0 or bonusType >= BONUS_TYPES then
        debug call BJDebugMsg("BonusSystem Error: Invalid bonus type (" + I2S(bonusType) + ")")
        return false
    endif

    if ammount < 0 then
        set ammount = MaxBonus + ammount + 1
        call UnitAddAbility(u, BonusAbilitys[bonusType * ABILITY_COUNT + ABILITY_COUNT - 1])
        call UnitMakeAbilityPermanent(u, true, BonusAbilitys[bonusType * ABILITY_COUNT + ABILITY_COUNT - 1])
    else
        call UnitRemoveAbility(u, BonusAbilitys[bonusType * ABILITY_COUNT + ABILITY_COUNT - 1])
    endif

    loop
        if ammount >= PowersOf2[i] then
            call UnitAddAbility(u, BonusAbilitys[bonusType * ABILITY_COUNT + i])
            call UnitMakeAbilityPermanent(u, true, BonusAbilitys[bonusType * ABILITY_COUNT + i])
set ammount = ammount - PowersOf2[i]
        else
            call UnitRemoveAbility(u, BonusAbilitys[bonusType * ABILITY_COUNT + i])
        endif

        set i = i - 1
        exitwhen i < 0
    endloop

    return true
endfunction

function UnitGetBonus takes unit u, integer bonusType returns integer
    local integer i = 0
    local integer ammount = 0

    if bonusType == BONUS_SPEED_MOVE then
        return BonusModMS_GetMoveSpeed(u, ammouont)
    endif

    if GetUnitAbilityLevel(u, BonusAbilitys[bonusType * ABILITY_COUNT + ABILITY_COUNT - 1]) > 0 then
        set ammount = MinBonus
    endif

    loop
        if GetUnitAbilityLevel(u, BonusAbilitys[bonusType * ABILITY_COUNT + i]) > 0 then
            set ammount = ammount + PowersOf2[i]
        endif

        set i = i + 1
        exitwhen i == ABILITY_COUNT - 2
    endloop

    return ammount
endfunction

function UnitAddBonus takes unit u, integer bonusType, integer ammount returns boolean
    return UnitSetBonus(u, bonusType, UnitGetBonus(u, bonusType) + ammount)
endfunction

private function Initialize takes nothing returns nothing
    local integer i = 1
    local unit u

    set PowersOf2[0] = 1
    loop
        set PowersOf2[i] = PowersOf2[i - 1] * 2
        set i = i + 1
        exitwhen i == ABILITY_COUNT
    endloop

    set MaxBonus = PowersOf2[ABILITY_COUNT - 1] - 1
    set MinBonus = -PowersOf2[ABILITY_COUNT - 1]

    call InitializeAbilitys()

    if PRELOAD_ABILITYS then
        set u = CreateUnit(Player(15), PRELOAD_DUMMY_UNIT, 0, 0, 0)
        set i = 0
        loop
            exitwhen i == BONUS_TYPES * ABILITY_COUNT
            call UnitAddAbility(u, BonusAbilitys[i])
            set i = i + 1
        endloop
        call RemoveUnit(u)
    endif
endfunction
endlibrary



JASS:
library BonusModMS initializer init requires Table

    globals
        private constant integer KEY_HANDLEID = 0
        private constant integer KEY_MSSPEED = 1
    
        private HandleTable MS_Table = 0
    endglobals
    
    public function ClearMoveSpeed takes unit u returns nothing
        call SetUnitMoveSpeed(u, GetUnitMoveSpeed(u) - MS_Table[u]) 
        call MS_Table.flush(u)
    endfunction
    
    public function ChangeMoveSpeed takes unit u, integer newVal returns nothing
        local real curSpeed = MS_Table[u] + newVal
        
        call SetUnitMoveSpeed(u, GetUnitMoveSpeed(u) + curSpeed)
        
        set MS_Table[u] = curSpeed     
    endfunction
    
    public function GetMoveSpeed takes unit u returns integer
        return MS_Table[u]  
    endfunction

    private function init takes nothing returns nothing
        set MS_Table = HandleTable.create()
    endfunction

endlibrary


Now you need table.

Does this compile?

You don't need to do that :D
It would be an easy calculation:

When i add movespeed:
JASS:
local real bonus = it.ms
if bonus + GetUnitMoveSpeed(.Owner) > MAX_MOVE_SPEED then
    set bonus = MAX_MOVE_SPEED - GetUnitMoveSpeed(.Owner)
endif
call SetUnitMoveSpeed(.Owner, GetUnitMoveSpeed(.Owner) + bonus)

Edit:
The same would be with MIN_MOVE_SPEED I think...
If someone has an MIN_MOVE_SPEED of 200 for example..
Well, but then, when you call GetBonus, you get the wrong value?

Edit2: Oh btw, with yours you can't remove the unitspeed bonus, with mine you can. (easily).

Also it is not part of the BonusMod anymore, but mine is.
 
One problem with you system could be, if the movespeed of the unit is changed by something other, like abilitys or am I wrong?^^
I changed the ClearMoveSpeed() function, now it should work even then.

Edit: Oh by the way, your system would have much more problems. (Since you don't store the correct movementspeed value, you never know when its over 522)
 
How will you do it? Lets share ideas.


JASS:
library BonusModMS requires Table

    globals
        private constant integer KEY_ID = 0
        private constant integer KEY_MS = 1
        private hashtable MS_Table = InitHashtable()
    endglobals
    
    public function ClearMoveSpeed takes unit u returns nothing
        call SetUnitMoveSpeed(u, GetUnitMoveSpeed(u) - GetMoveSpeed(u)) 
        call FlushChildHashtable(MS_Table, GetHandleId(u)) 
    endfunction
    
    public function ChangeMoveSpeed takes unit u, integer newVal returns boolean
        local integer key = GetHandleId(u)
        local integer curSpeed = GetUnitMoveSpeed(u)
        local integer newSpeed = curSpeed + newVal
        local integer curBouns = GetMoveSpeed(u) + newVal
        
        if curSpeed == 0 then
            return false
        endif
        
        call SetUnitMoveSpeed(u, newSpeed)
        call SaveInteger(MS_Table, key, KEY_MS, curBouns)
        
        return true
    endfunction
    
    public function GetMoveSpeed takes unit u returns integer
        return LoadInteger(MS_Table, GetHandleId(u), KEY_MS)  
    endfunction

endlibrary


Now it supports abilities and all that stuff too. (I think)
 
That's only, because you don't know vJass, I think :)
If you would know it, it's extremly easy, like the language itself.

well I dont have time to spend to understand the language... maybe if I will have time I'll try to understand and study it....

I can open the map in the editor but I cannot test the map... I use NewGenWE and my wc3 has 1.24b patch..
 
Did you tried to run the map through the JNGP Map Test or to Warcraft itself?
You must load it from the JNGP, because Warcraft can't load this map, because there are no enemies.

First I tried it at warcraft directly then I also tried the test map from JNGP..

btw, I have a map that has no enemies but it loads.
 
Top