• 🏆 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!

[vJASS] ItemPower

Level 19
Joined
Mar 18, 2012
Messages
1,716

ItemPower


Extends your map by custom bonuses. Mainly designed to be used along with item type ids.


Description

ItemPower enables the option to register real values to integers.
An ItemPower as requires a unique index and a base value. Create a new ItemPowers via:
function CreateUnitItemPower takes unit whichUnit returns nothing

For example:
set ITEM_POWER_CRITICAL_HIT_DAMAGE = CreateItemPower(1, 0.00)

A practical example of how to make use of this would be with an item id ( 'I000' )
call AddItemIdItemPower('I000', ITEM_POWER_CRITICAL_HIT_DAMAGE, 15.)

ItemPowers for an integer are stored in a singly linked list in a hashtable.
Hence an integer can have as many ItemPowers as you want.
Let's continue the example above:

JASS:
call AddItemIdItemPower('I000', ITEM_POWER_CRITICAL_HIT_DAMAGE, 15.)
call AddItemIdItemPower('I000', ITEM_POWER_CRITICAL_HIT_CHANCE, 15.)
call AddItemIdItemPower('I000', ITEM_POWER_MINIMUM_DAMAGE, 7.)
call AddItemIdItemPower('I000', ITEM_POWER_MAXIMUM_DAMAGE, 28.)
Units must be registered to the system via
function CreateUnitItemPower takes unit whichUnit returns nothing

Run these two function on an equip/unequip item event to add/remove powers to units:
function ItemPower_UnitRemoveItemId takes unit whichUnit, integer itemId returns nothing
function ItemPower_UnitAddItemId takes unit whichUnit, integer itemId returns nothing

You can also manipulate ItemPowers for a unit directly:
function AddUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
function SetUnitItemPower takes unit whichUnit, integer power, real amount returns nothing

Make use of this by getting the total amount of an ItemPower for a unit:
For example in a custom damage system or a triggered spell.
function GetUnitItemPower takes unit whichUnit, integer power returns real

Of course ItemPowers are not limited to item ids. They were just the main reason I coded this library.


System which use ItemPowerDescription and explanation
[td]Equipment is a custom inventory interface based on dummy abilities.
ItemPower is integrated in the code and automatically evaluated on item equip and unequip event.
[tr][TD][TD]Expands equipment systems by item sets. Such as in famous RPG's like Blizzard's Diablo II/III.
ItemSets has the option to add ItemPowers as bonuses.
[tr]

JASS:
library ItemPower/* v4.4
*************************************************************************************
*
*   ItemPower allows to assigns reals to items.
*   The attaches there reals to units wearing such items.
*   This way items can have a huge range of bonus attributes,
*   normally not provided by warcraft 3.
*
*   Examples:
*   ¯¯¯¯¯¯¯¯¯
*       Block amount, Block chance, Magic find, Bonus exp, Resistance, ...
*
*   When an unit equips or unequips an item, these values will get summarized for that unit.
*   By default the operation is either + or -
*
*   One item can have as many "powers" as you want.
*  
*   Can be used independant from items by using the AddUnitItemPower and SetUnitItemPower functions.
*  
*************************************************************************************
*
*   */ uses /*
*  
*       */ optional ErrorMessage /* [url]https://github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage[/url]
*
************************************************************************************
*
*   1. Import instruction
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       Copy the ItemPower script into your map.
*       ErrorMessage is optional, but useful to have.
*                       ¯¯¯¯¯¯¯¯
*   2. API
*   ¯¯¯¯¯¯
*
*   function CreateItemPower takes integer power, real base returns integer
*       - Create an unique ItemPower index.
*       - Start at 1 and use the next even number per new power. Ex:(1, 2, 3, ...)
*       - Negative or 0 as index is forbidden!
*
*   function AddItemIdItemPower takes integer itemId, integer power, real amount returns nothing
*       - Assigns an unique power value to an item id for an ItemPower.
*           --> call AddItemIdItemPower('I000', 1, 20.)
*
*   function CreateUnitItemPower takes unit whichUnit returns nothing
*       - Register an unit to ItemPower.
*       - Properly sets all base values for that unit.
*       - Required because powers can have base values beyond 0.
*
*   function DestroyUnitItemPower takes unit whichUnit returns nothing
*       - Flushes the child key. Memory management.
*       - Use this if you wish to keep the hashtable cleaner, once an unit is no longer used.
*
*   function GetUnitItemPower takes unit whichUnit, integer power returns real
*       - Returns total ItemPower amount for an unit's power.
*           --> set damage = GetUnitItemPower(unit, 1)
*
*   public function UnitAddItemId takes unit whichUnit, integer itemId returns nothing
*   public function UnitRemoveItemId takes unit whichUnit, integer itemId returns nothing
*       - Removes/Adds all assigned power values of an item to the unit.
*       - Use this function on item equip/unequip events.
*           --> ItemPower_UnitAddItemId(unit, 'I00I')  
*
*   function SetUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
*   function AddUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
*       - Change ItemPower values independant from item ids. (per spell, abiilty, ...)
*       - extends ItemPower usage to be very flexible and useful.
*           --> AddUnitItemPower(unit, 1, 10)
*  
*   function UnitUsesItemPower takes unit whichUnit returns boolean
*   function ItemIdUsesItemPower takes integer itemId returns boolean
*
*/
//ItemPower code. Edit on your own risk.
    globals
        private constant hashtable TABLE = InitHashtable()
        private integer powers  = -1
    endglobals
    
    function ItemIdUsesItemPower takes integer itemId returns boolean
        return HaveSavedBoolean(TABLE, itemId, 0)
    endfunction
   
    function UnitUsesItemPower takes unit whichUnit returns boolean
        return HaveSavedReal(TABLE, GetHandleId(whichUnit), powers) and (GetUnitTypeId(whichUnit) != 0)
    endfunction
       
    function GetUnitItemPower takes unit whichUnit, integer power returns real
        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((not UnitUsesItemPower(whichUnit)),   "ItemPower", "GetUnitItemPower", "whichUnit", 0,     GetUnitName(whichUnit) + " Is not using ItemPower!")
            debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "GetUnitItemPower", "power",     power, "Attempt To get an invalid ItemPower!")
        endif
        
        return LoadReal(TABLE, GetHandleId(whichUnit), power)
    endfunction
    
    function AddUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
        local integer id = GetHandleId(whichUnit)
           
        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((not UnitUsesItemPower(whichUnit)),   "ItemPower", "AddUnitItemPower", "whichUnit", id,    GetUnitName(whichUnit) + " Is not using ItemPower!")
            debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "AddUnitItemPower", "power",     power, "Attempt to manipulate an invalid ItemPower!")
        endif
       
        if (UnitUsesItemPower(whichUnit)) then
            call SaveReal(TABLE, id, power, LoadReal(TABLE, id, power) + amount)
        endif
    endfunction
   
    function SetUnitItemPower takes unit whichUnit, integer power, real amount returns nothing
        local integer id = GetHandleId(whichUnit)
        
        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((not UnitUsesItemPower(whichUnit)),   "ItemPower", "SetUnitItemPower", "whichUnit", id,    GetUnitName(whichUnit) + " Is Not Using ItemPower!")
            debug call ThrowWarning((not HaveSavedReal(TABLE, 0, power)), "ItemPower", "SetUnitItemPower", "power",     power, "Attempt To Manipulate An Invalid ItemPower!")
        endif
        
        if (UnitUsesItemPower(whichUnit)) then
            call SaveReal(TABLE, id, power, amount)
        endif
    endfunction
   
    public function UnitRemoveItemId takes unit whichUnit, integer itemId returns nothing
        local integer next = LoadInteger(TABLE, itemId, 0)
        local integer id   = GetHandleId(whichUnit)

        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "ItemPower_RemoveUnitItemId", "whichUnit", 0, GetUnitName(whichUnit) + " is not using ItemPower!")
        endif

        //*  Minimal overhead for important safety.
        if UnitUsesItemPower(whichUnit) and ItemIdUsesItemPower(itemId) then
            loop
                exitwhen (0 == next)
                call SaveReal(TABLE, id, next, LoadReal(TABLE, id, next) - LoadReal(TABLE, itemId, next))
                set next = LoadInteger(TABLE, itemId, next)
            endloop
        endif
    endfunction

    public function UnitAddItemId takes unit whichUnit, integer itemId returns nothing
        local integer next = LoadInteger(TABLE, itemId, 0)
        local integer id   = GetHandleId(whichUnit)

        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((not UnitUsesItemPower(whichUnit)), "ItemPower", "ItemPower_AddUnitItemId", "whichUnit", id, GetUnitName(whichUnit) + " is not using ItemPower!")
        endif
           
        if UnitUsesItemPower(whichUnit) and ItemIdUsesItemPower(itemId) then
            loop
                exitwhen (0 == next)
                call SaveReal(TABLE, id, next, LoadReal(TABLE, id, next) + LoadReal(TABLE, itemId, next))
                set next = LoadInteger(TABLE, itemId, next)
            endloop
        endif
    endfunction
   
    //*  Flushes the child key.
    function DestroyUnitItemPower takes unit whichUnit returns nothing
        if UnitUsesItemPower(whichUnit) then
            call FlushChildHashtable(TABLE, GetHandleId(whichUnit))
       
        debug else
            static if LIBRARY_ErrorMessage then
                debug call ThrowWarning((true), "ItemPower", "DestroyUnitItemPower", "whichUnit", GetHandleId(whichUnit), GetUnitName(whichUnit) + " is not using ItemPower!")
            endif        
        endif
    endfunction
   
    function CreateUnitItemPower takes unit whichUnit returns nothing
        local integer id   = GetHandleId(whichUnit)
        local integer next = 0
           
        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((GetUnitTypeId(whichUnit) == 0), "ItemPower", "CreateUnitItemPower", "GetUnitTypeId", 0, "Invalid unit handle ( null )!")
        endif
           
        if (GetUnitTypeId(whichUnit) == 0) then
            return
        endif
           
        loop
            exitwhen next > powers
            call SaveReal(TABLE, id, next, LoadReal(TABLE, 0, next))  
            set next = next + 1
        endloop
    endfunction

    function AddItemIdItemPower takes integer itemId, integer power, real amount returns nothing
        static if LIBRARY_ErrorMessage then
            debug call ThrowError((itemId == 0), "ItemPower", "AddItemIdItemPower", "itemId", 0, "Invalid ItemId ( 0 )!")
        else
            if (itemId == 0) then
                return
            endif
        endif
        if not HaveSavedReal(TABLE, itemId, power) then
            call SaveInteger(TABLE, itemId, power, LoadInteger(TABLE, itemId, 0))
            call SaveInteger(TABLE, itemId, 0, power)
        endif
        call SaveReal(TABLE, itemId, power, amount)
        call SaveBoolean(TABLE, itemId, 0, true)
    endfunction
       
    function CreateItemPower takes integer power, real base returns integer
        static if LIBRARY_ErrorMessage then
            debug call ThrowWarning((HaveSavedReal(TABLE, 0, power)), "ItemPower", "CreateItemPower", "power", power, "Attempt to override an existing ItemPower!")
            debug call ThrowError((0 >= power),                       "ItemPower", "CreateItemPower", "power", power, "ItemPower indexes must be greater than zero!")
        else
            if (0 >= power) then
                debug call BJDebugMsg(SCOPE_PREFIX + "ERROR: CreateItemPower, Invalid ItemPower [" + I2S(power) + "]!!!")
                return power
            endif
        endif
        if (powers < power) then
            set powers = power
        endif
       
        call SaveReal(TABLE, 0, power, base)
        return power
    endfunction
   
endlibrary
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
I think that since 2 systems in Spells section that use this are already approved that there is no reason to not approve this one, but one thing bites my ass too much.

JASS:
*   function CreateItemPower takes integer power, real base returns integer
*       - Create an unique ItemPower index.
*       - Start at 1 and use the next even number per new power. Ex:(1, 2, 3, ...)
*       - Negative or 0 as index is forbidden!

I think passing the "power" argument into that is completly useless, you could just be incrementing powers in there and always have the correct index, it is even mentioned that the proper use is to use incrementing numbers, but why put this burden to people, when you can do this internally very easily?
 
I think passing the "power" argument into that is completly useless
agreed

also I'm not sure why registrating units is required.
Since BPower is a friend and busy atm, I felt free to write a newer suggestion, which should be more modular and structured in usage. (original ItemPower also was written several years ago already I believe)

2 structs are there now,
analogous to BPower's ItemPower, one struct, the first one would be a single "Power", and the second struct can be "ItemPower", so several Powers bundled together.

BPower, reviewers, and others, maybe some opnion on this matter?
My argument is basicly that this resource is anyways very specific and abstract, so it won't find much usage. (BPower uses such system though in his equipment sysem linked somewhere on top)
And when the purpose of the system is so rare of use already, we should make the best to write it as modular and easy to use as possible.

JASS:
library RealEquipment uses List

/*
    Allows you to equip/unqeuip units with <real> -type values, aka RealEquipment.
    There is also a struct which allows you to bind several RealEquipments to a bundle.
    With the bundle you may equip/unequip several RealEquipments at once, and bind it to an integer.
    So for example you can bind bundles to itemtype-id, abilitytype-id, etc, and easily can
    equip/unequip several RealEquipments at once, on events like onItemPickUp/onCast, etc.

 ___________________________________


    struct RealEquipment
 
        constructor:
            • static method create takes real r returns thistype
      
        destructor:
            • method destroy takes nothing returns nothing
      
        method operators:
  
            • get
            • set=
      
        useability:
  
            • method equip takes unit u returns boolean
            • method unequip takes unit u returns boolean
            • method isUnitEqipped takes unit u returns boolean
      
            • method setUnit  takes unit u, real r returns nothing
            • method getUnit takes unit u returns real
                (to spezialisize value for a unit)
      
___________________________________


    struct RealEquipmentBundle
 
        constructor:
            • static method create takes integer id returns thistype
      
                - "id" defines the index to which the RealEquipments will get bundled
                - any valid integer is allowed (not limited to be under 8190)
          
        destructor:
            • method destroy takes nothing returns nothing
        
      
        method operators:
            • [] takes integer id returns thistype
      
                - "id" defines the index on which bundle we refer to.
                - for example: call RealEquipmentBundle['I000'].destroy() ... will destroy the bundle with index 'I000'.
          
        useability:
  
            • method add takes RealEquipment re returns boolean
            • method remove takes RealEquipment re returns boolean
            • method addEx takes real r returns RealEquipment
                - "addEx" method accepts native "real" parameter because it will internaly create and return a RealEquipment.
      
            • method equip takes unit u returns boolean
            • method unequip takes unit u returns boolean
            • method isUnitEqipped takes unit u returns boolean
      
                - "equip" will automatically equip all binded RealEquipments from the bundle. vice versa for "uneuqip".
                - "isUnitEquiped" will only check if the bundle itself was equiped or not to the unit,
                    and not if the unit is equipped with the bundle's binded RealEquipments.
          
          
___________________________________
 
*/

    globals
        private hashtable table = InitHashtable()
    endglobals

    struct RealEquipment
  
        private real amount
        private thistype next
        private thistype prev
  
        static method create takes real r returns thistype
            local thistype this = thistype.allocate()
            set this.next = 0
            set this.prev = thistype(0).prev
            set thistype(0).prev.next = this
            set thistype(0).prev = this
            set .amount = r
            return this
        endmethod
  
        method destroy takes nothing returns nothing
            call this.deallocate()
            set this.next.prev = this.prev
            set this.prev.next = this.next
        endmethod
  
        method operator get takes nothing returns real
            return amount
        endmethod
  
        method operator set= takes real r returns nothing
            set amount = r
        endmethod
  
        method setUnit  takes unit u, real r returns nothing
            call SaveReal(table, -GetHandleId(u), this, r)
        endmethod
  
        method getUnit takes unit u returns real
            return LoadReal(table, -GetHandleId(u), this)
        endmethod
  
        // -id is used to check integrity, for not to collide with Bundle struct
        method equip takes unit u returns boolean
            local integer id = GetHandleId(u)
            if HaveSavedBoolean(table, -id, this) then
                return false
            endif
            call SaveBoolean(table, -id, this, true)
            call SaveReal(table, id, this, .amount)
            return true
        endmethod
  
        method unequip takes unit u returns boolean
            local integer id = GetHandleId(u)
            if not HaveSavedBoolean(table, -id, this) then
                return false
            endif
            call RemoveSavedBoolean(table, -id, this)
            call RemoveSavedReal(table, id, this)
            return true
        endmethod
  
        method isUnitEqipped takes unit u returns boolean
            return HaveSavedBoolean(table, -GetHandleId(u), this)
        endmethod
    endstruct
 
    private struct RealEquipmentData
        implement List
        RealEquipment realEquipment
    endstruct

    struct RealEquipmentBundle

        private RealEquipmentData EquiptmentList
        private integer id
  
        static method create takes integer id returns thistype
            local thistype this = thistype.allocate()
            set .id = id
            call SaveInteger(table, id, 0, this)
            set .EquiptmentList = RealEquipmentData.create()
            return this
        endmethod
  
        method destroy takes nothing returns nothing
            call .EquiptmentList.destroy()
            call RemoveSavedInteger(table, .id, 0)
            call .deallocate()
        endmethod
  
        static method operator [] takes integer id returns thistype
            return LoadInteger(table, id, 0)
        endmethod
  
        method addEx takes real r returns RealEquipment
            local RealEquipmentData data = .EquiptmentList.enqueue()
            set data.realEquipment = RealEquipment.create(r)
            call SaveBoolean(table, this, data.realEquipment, true)
            call SaveInteger(table, this, data.realEquipment, data) // for O(1) removal
            return data.realEquipment
        endmethod
  
        method add takes RealEquipment p returns boolean
            local RealEquipmentData data
            if HaveSavedBoolean(table, this, p) then
                return false
            endif
            call SaveBoolean(table, this, p, true)
            set data = .EquiptmentList.enqueue()
            call SaveInteger(table, this, p, data) // for O(1) removal
            set data.realEquipment = p
            return true
        endmethod
  
        method remove takes RealEquipment re returns boolean
            if HaveSavedBoolean(table, this, re) then
                call RealEquipmentData(LoadInteger(table, this, re)).remove()
                call RemoveSavedInteger(table, this, re)
                return true
            endif
            return false
        endmethod
  
        method equip takes unit u returns boolean
            local integer id = GetHandleId(u)
            local RealEquipmentData element
      
            if HaveSavedBoolean(table, id, this) then
                return false
            endif
            call SaveBoolean(table, id, this, true)
      
            set element = .EquiptmentList.first
            loop
                exitwhen 0 == element
                call SaveReal(table, id, element.realEquipment, element.realEquipment.get)
                set element = element.next
            endloop
            return true
        endmethod
  
        method unequip takes unit u returns boolean
            local integer id = GetHandleId(u)
            local RealEquipmentData element
      
            if not HaveSavedBoolean(table, id, this) then
                return false
            endif
            call RemoveSavedBoolean(table, id, this)
      
            set element = .EquiptmentList.first
            loop
                exitwhen 0 == element
                call RemoveSavedReal(table, id, element.realEquipment)
                set element = element.next
            endloop
            return true
        endmethod
  
        method isUnitEqipped takes unit u returns boolean
            return HaveSavedBoolean(table, GetHandleId(u), this)
        endmethod
  
    endstruct
  
endlibrary
 
Last edited:

Submission:
ItemPowe v4.4

Date:
2 November 16

Status:
Graveyard
Note:

I have some doubts towards it's current state and useability.
It should work fine as BPower also uses it inside his other Item system,
though, as standalone submission I wished to discuss/change some things first.
My thoughts are noted in the post a bit above. Graveyard for now.
 
Top