HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

UnitStruct?

02-27-2009, 10:31 AM#1
xombie
unitex struct

Collapse JASS:
library UnitStruct

globals
    private boolexpr    AntiRepeatExpr
    private boolexpr    TrueExpr
    private group       Enum            = CreateGroup()
    
    private boolean     AntiRepeatBool  = true
    private boolean     IsAttackBool
endglobals

constant function IsAttack takes nothing returns boolean
    return IsAttackBool
endfunction

private function AntiRepeat takes nothing returns boolean
    return AntiRepeatBool
endfunction
private function True takes nothing returns boolean
    return true
endfunction

private function interface onDamageInterface takes nothing returns nothing

struct unitex
    private unit    main    = null
    private onDamageInterface onDamageFunc = 0
    
    real backupLife
    real backupLifeMax
    
    private real    stunTime        = 0.00
    private real    animationSpeed  = 1.00
    
    private integer mVertexRed      = 255
    private integer mVertexGreen    = 255
    private integer mVertexBlue     = 255
    private integer mVertexAlpha    = 255

    // Custom Stun associated
    private static unit                 Dummy               = null
    private static constant integer     DummyUnitId         = 'h000'
    private static constant integer     DummyStunAbilityId  = 'STUN'
    private static constant integer     DummyStunBuffId     = 'BPSE'
    private static constant string      DummyStunOrderStr   = "thunderbolt"
    
    // SetUnitMaxState associated
    private static constant integer     MaxLifeModifierId   = 'LIFE'
    private static constant integer     MaxManaModifierId   = 'MANA'
    
    // Generic Timer
    private static timer                Refresh             = CreateTimer()
    private static constant real        RefreshTimeout      = 0.2
    private static real                 RefreshCounter      = 0.0
    
    // DamageDetection associated
    private static trigger              OnDamage            = CreateTrigger()
    private static constant real        OnDamageRefreshRate = 1200
    private static constant integer     AttackAbilityId     = 'Att2'
    private static constant integer     AttackBuffId        = 'Bttk'
    
    // Main unit
    method operator unit takes nothing returns unit
        return this.main
    endmethod
    
    // Unit Type ID
    method operator typeId takes nothing returns integer
        return GetUnitTypeId(this.main)
    endmethod
    
    // Unit Color/Ownership
    method operator color= takes playercolor color returns nothing
        call SetUnitColor(this.main, color)
    endmethod
    
    method operator owner takes nothing returns player
        return GetOwningPlayer(this.main)
    endmethod
    method operator owner= takes player owner returns nothing
        call SetUnitOwner(this.main, owner, false)
    endmethod
    method setOwner takes player owner, boolean changeColor returns nothing
        call SetUnitOwner(this.main, owner, changeColor)
    endmethod
    
    // Unit Orders
    method issueOrder takes string order returns boolean
        return IssueImmediateOrder(this.main, order)
    endmethod
    method issueOrderTarget takes string order, widget target returns boolean
        return IssueTargetOrder(this.main, order, target)
    endmethod
    method issueOrderPoint takes string order, real x, real y returns boolean
        return IssuePointOrder(this.main, order, x, y)
    endmethod
    
    // Unit Positioning
    method operator x takes nothing returns real
        return GetUnitX(this.main)
    endmethod
    method operator y takes nothing returns real
        return GetUnitY(this.main)
    endmethod
    method operator x= takes real x returns nothing
        call SetUnitX(this.main, x)
    endmethod
    method operator y= takes real y returns nothing
        call SetUnitY(this.main, y)
    endmethod
    method operator height takes nothing returns real
        return GetUnitFlyHeight(this.main)
    endmethod
    method operator height= takes real height returns nothing
        call SetUnitFlyHeight(this.main, height, 0)
    endmethod
    method setFlyHeight takes real height, real rate returns nothing
        call SetUnitFlyHeight(this.main, height, rate)
    endmethod
    
    method operator position takes nothing returns location
        return GetUnitLoc(this.main)
    endmethod
    method operator position= takes location pos returns nothing
        call SetUnitPositionLoc(this.main, pos)
    endmethod
    
    method operator facing takes nothing returns real
        return GetUnitFacing(this.main)
    endmethod
    method operator facing= takes real facing returns nothing
        call SetUnitFacing(this.main, facing)
    endmethod
    method setFacingTimed takes real facing, real duration returns nothing
        call SetUnitFacingTimed(this.main, facing, duration)
    endmethod
    
    // Unit Animation/Art
    method operator animation= takes string animation returns nothing
        call SetUnitAnimation(this.main, animation)
    endmethod
    method operator animationIndex= takes integer index returns nothing
        call SetUnitAnimationByIndex(this.main, index)
    endmethod
    method operator queueAnimation= takes string animation returns nothing
        call QueueUnitAnimation(this.main, animation)
    endmethod
    
    method setVertexColor takes integer r, integer g, integer b, integer alpha returns nothing
        call SetUnitVertexColor(this.main, r, g, b, alpha)
        set this.mVertexRed = r
        set this.mVertexGreen = g
        set this.mVertexBlue = b
        set this.mVertexAlpha = alpha
    endmethod
    method operator vertexRed takes nothing returns integer
        return this.mVertexRed
    endmethod
    method operator vertexGreen takes nothing returns integer
        return this.mVertexGreen
    endmethod
    method operator vertexBlue takes nothing returns integer
        return this.mVertexBlue
    endmethod
    method operator vertexAlpha takes nothing returns integer
        return this.mVertexAlpha
    endmethod
    method operator vertexRed= takes integer red returns nothing
        call SetUnitVertexColor(this.main, red, this.mVertexGreen, this.mVertexBlue, this.mVertexAlpha)
        set this.mVertexRed = red
    endmethod
    method operator vertexGreen= takes integer green returns nothing
        call SetUnitVertexColor(this.main, this.mVertexRed, green, this.mVertexBlue, this.mVertexAlpha)
        set this.mVertexGreen = green
    endmethod
    method operator vertexBlue= takes integer blue returns nothing
        call SetUnitVertexColor(this.main, this.mVertexRed, this.mVertexGreen, blue, this.mVertexAlpha)
        set this.mVertexBlue = blue
    endmethod
    method operator vertexAlpha= takes integer alpha returns nothing
        call SetUnitVertexColor(this.main, this.mVertexRed, this.mVertexGreen, this.mVertexBlue, alpha)
        set this.mVertexAlpha = alpha
    endmethod
    
    method operator timeScale takes nothing returns real
        return this.animationSpeed
    endmethod
    method operator timeScale= takes real scale returns nothing
        call SetUnitTimeScale(this.main, scale)
        set this.animationSpeed = scale
    endmethod
    
    // Acquiring Range
    method operator acquireRange takes nothing returns real
        return GetUnitAcquireRange(this.main)
    endmethod
    method operator acquireRange= takes real range returns nothing
        call SetUnitAcquireRange(this.main, range)
    endmethod
    
    // Attacks Flying/Ground
    method operator attacksFlying takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_ATTACKS_FLYING)
    endmethod
    method operator attacksGround takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_ATTACKS_GROUND)
    endmethod
    
    // Unit Type Comparisons
    method operator isAncient takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_ANCIENT)
    endmethod
    method operator isDead takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_DEAD)
    endmethod
    method operator isEthereal takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_ETHEREAL)
    endmethod
    method operator isFlying takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_FLYING)
    endmethod
    method operator isGiant takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_GIANT)
    endmethod
    method operator isGround takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_GROUND)
    endmethod
    method operator isHero takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_HERO)
    endmethod
    method operator isMagicImmune takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_MAGIC_IMMUNE)
    endmethod
    method operator isMechanical takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_MECHANICAL)
    endmethod
    method operator isMelee takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_MELEE_ATTACKER)
    endmethod
    method operator isPeon takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_PEON)
    endmethod
    method operator isPlagued takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_PLAGUED)
    endmethod
    method operator isPoisoned takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_POISONED)
    endmethod
    method operator isPolymorphed takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_POLYMORPHED)
    endmethod
    method operator isRanged takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_RANGED_ATTACKER)
    endmethod
    method operator isResistant takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_RESISTANT)
    endmethod
    method operator isSapper takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_SAPPER)
    endmethod
    method operator isSleeping takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_SLEEPING)
    endmethod
    method operator isSnared takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_SNARED)
    endmethod
    method operator isStructure takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_STRUCTURE)
    endmethod
    method operator isStunned takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_STUNNED)
    endmethod
    method operator isSummoned takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_SUMMONED)
    endmethod
    method operator isTauren takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_TAUREN)
    endmethod
    method operator isTownhall takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_TOWNHALL)
    endmethod
    method operator isUndead takes nothing returns boolean
        return IsUnitType(this.main, UNIT_TYPE_UNDEAD)
    endmethod
    
    // Unit Movespeed
    method operator movespeed takes nothing returns real
        return GetUnitMoveSpeed(this.main)
    endmethod
    method operator movespeed= takes real movespeed returns nothing
        call SetUnitMoveSpeed(this.main, movespeed)
    endmethod
    
    method operator turnRate takes nothing returns real
        return GetUnitTurnSpeed(this.main)
    endmethod
    method operator turnRate= takes real rate returns nothing
        call SetUnitTurnSpeed(this.main, rate)
    endmethod
    
    // Unit State Modifying
    method operator life takes nothing returns real
        return GetWidgetLife(this.main)
    endmethod
    method operator mana takes nothing returns real
        return GetUnitState(this.main, UNIT_STATE_MANA)
    endmethod
    method operator life= takes real life returns nothing
        call SetWidgetLife(this.main, life)
        set this.backupLife = life
    endmethod
    method operator mana= takes real mana returns nothing
        call SetUnitState(this.main, UNIT_STATE_MANA, mana)
    endmethod
    method operator lifeMaximum takes nothing returns real
        return GetUnitState(this.main, UNIT_STATE_MAX_LIFE)
    endmethod
    method operator manaMaximum takes nothing returns real
        return GetUnitState(this.main, UNIT_STATE_MAX_MANA)
    endmethod
    method setLifeMaximum takes integer new, boolean backup returns boolean
        local integer dif = new - R2I(this.lifeMaximum)
        if new < this.life then
            return false
        endif
        if backup then
            set this.backupLifeMax = this.lifeMaximum
        endif
        
        if dif > 0 then
            loop
                exitwhen dif == 0
                call UnitAddAbility(this.main, unitex.MaxLifeModifierId)
                if dif >= 10000 then
                    set dif = dif - 10000
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 6)
                elseif dif >= 1000 then
                    set dif = dif - 1000
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 5)
                elseif dif >= 100 then
                    set dif = dif - 100
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 4)
                elseif dif >= 10 then
                    set dif = dif - 10
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 3)
                else
                    set dif = dif - 1
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 2)
                endif
                call UnitRemoveAbility(this.main, unitex.MaxLifeModifierId)
            endloop
        elseif dif < 0 then
            set dif = -dif
            loop
                exitwhen dif == 0
                call UnitAddAbility(this.main, unitex.MaxLifeModifierId)
                if dif >= 10000 then
                    set dif = dif - 10000
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 11)
                elseif dif >= 1000 then
                    set dif = dif - 1000
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 10)
                elseif dif >= 100 then
                    set dif = dif - 100
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 9)
                elseif dif >= 10 then
                    set dif = dif - 10
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 8)
                else
                    set dif = dif - 1
                    call SetUnitAbilityLevel(this.main, unitex.MaxLifeModifierId, 7)
                endif
                call UnitRemoveAbility(this.main, unitex.MaxLifeModifierId)
            endloop
        endif
        return true
    endmethod
    method setManaMaximum takes integer new returns boolean
        local integer dif = new - R2I(this.manaMaximum)
        if new < this.mana then
            return false
        endif
        
        if dif > 0 then
            loop
                exitwhen dif == 0
                call UnitAddAbility(this.main, unitex.MaxManaModifierId)
                if dif >= 10000 then
                    set dif = dif - 10000
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 6)
                elseif dif >= 1000 then
                    set dif = dif - 1000
                    call SetUnitAbilityLevel(this.main,unitex.MaxManaModifierId, 5)
                elseif dif >= 100 then
                    set dif = dif - 100
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 4)
                elseif dif >= 10 then
                    set dif = dif - 10
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 3)
                else
                    set dif = dif - 1
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 2)
                endif
                call UnitRemoveAbility(this.main, unitex.MaxManaModifierId)
            endloop
        elseif dif < 0 then
            set dif = -dif
            loop
                exitwhen dif == 0
                call UnitAddAbility(this.main, unitex.MaxManaModifierId)
                if dif >= 10000 then
                    set dif = dif - 10000
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 11)
                elseif dif >= 1000 then
                    set dif = dif - 1000
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 10)
                elseif dif >= 100 then
                    set dif = dif - 100
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 9)
                elseif dif >= 10 then
                    set dif = dif - 10
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 8)
                else
                    set dif = dif - 1
                    call SetUnitAbilityLevel(this.main, unitex.MaxManaModifierId, 7)
                endif
                call UnitRemoveAbility(this.main, unitex.MaxManaModifierId)
            endloop
        endif
        return true
    endmethod
        
    // Unit Abilities
    method addAbility takes integer abilId returns nothing
        call UnitAddAbility(this.main, abilId)
    endmethod
    method removeAbility takes integer abilId returns nothing
        call UnitRemoveAbility(this.main, abilId)
    endmethod
    method setAbilityLevel takes integer abilId, integer level returns nothing
        call SetUnitAbilityLevel(this.main, abilId, level)
    endmethod
    method abilityLevel takes integer abilId returns integer
        return GetUnitAbilityLevel(this.main, abilId)
    endmethod
    method incAbilityLevel takes integer abilId returns nothing
        call IncUnitAbilityLevel(this.main, abilId)
    endmethod
    method decAbilityLevel takes integer abilId returns nothing
        call DecUnitAbilityLevel(this.main, abilId)
    endmethod
        
    // Custom Stun
    method stun takes real duration returns boolean
        local boolean retValue
        local real durationEx = 0.0
        
        if duration <= 0.0 then
            return true
        endif
        call SetUnitOwner(unitex.Dummy, this.owner, false)
        set retValue = (IssueTargetOrder(unitex.Dummy, unitex.DummyStunOrderStr, this.main))
        
        loop
            set durationEx = durationEx + unitex.RefreshTimeout
            if durationEx > duration then
                set duration = durationEx - unitex.RefreshTimeout
                exitwhen (true)
            endif
        endloop
        
        if (this.stunTime > duration) then
            return retValue
        elseif (this.stunTime < duration) then
            set this.stunTime = duration
        endif
        return retValue
    endmethod
    
    static method onDamageRefresh takes nothing returns nothing
        local unitex udata = unitex(GetUnitUserData(GetEnumUnit()))
        call TriggerRegisterUnitEvent(unitex.OnDamage, udata.main, EVENT_UNIT_DAMAGED)
    endmethod
    static method onRefreshEnum takes nothing returns nothing
        local unitex udata = unitex(GetUnitUserData(GetEnumUnit()))
        if (udata != 0) then
            set udata.stunTime = udata.stunTime - unitex.RefreshTimeout
            if (udata.stunTime <= 0.0) then
                call UnitRemoveAbility(udata.main, unitex.DummyStunBuffId)
            endif
        endif
    endmethod
    static method onRefresh takes nothing returns nothing
        call GroupEnumUnitsInRect(Enum, GetWorldBounds(), TrueExpr)
        call ForGroup(Enum, function unitex.onRefreshEnum)
        call GroupClear(Enum)
        
        set unitex.RefreshCounter = unitex.RefreshCounter + unitex.RefreshTimeout
        if (unitex.RefreshCounter >= unitex.OnDamageRefreshRate) then
            set unitex.RefreshCounter = 0.0
            call TriggerClearConditions(unitex.OnDamage)
            call TriggerClearActions(unitex.OnDamage)
            call DestroyTrigger(unitex.OnDamage)
            
            set unitex.OnDamage = CreateTrigger()
            call TriggerAddCondition(unitex.OnDamage, AntiRepeatExpr)
            call TriggerAddAction(unitex.OnDamage, function unitex.onDamage)
            
            call GroupEnumUnitsInRect(Enum, GetWorldBounds(), TrueExpr)
            call ForGroup(Enum, function unitex.onDamageRefresh)
            call GroupClear(Enum)
        endif
    endmethod
    
    method onDamageRegister takes onDamageInterface func returns nothing
        set this.onDamageFunc = func
    endmethod
    static method onDamage takes nothing returns nothing
        local unit damaged = GetTriggerUnit()
        local unitex udata = GetUnitUserData(damaged)
        if GetUnitAbilityLevel(damaged, unitex.AttackBuffId) > 0 then
            call UnitRemoveAbility(damaged, unitex.AttackBuffId)
            set IsAttackBool = true
        else
            set IsAttackBool = false
        endif
        
        // Whenever unit is damaged, or he has his life set, it alters the
        // value of the stored life. (For damage negation)
        set udata.backupLife = udata.backupLife - GetEventDamage()
        
        set AntiRepeatBool = false
        if udata.onDamageFunc != 0 then
            call udata.onDamageFunc.execute()
        endif
        set AntiRepeatBool = true
    endmethod
    
    static method onInit takes nothing returns nothing
        set unitex.Dummy = CreateUnit(Player(0), unitex.DummyUnitId, 0, 0, 0)
        call UnitAddAbility(unitex.Dummy, 'Amov')
        call UnitAddAbility(unitex.Dummy, unitex.DummyStunAbilityId)
        
        set TrueExpr = Filter(function True)
        set AntiRepeatExpr = Filter(function AntiRepeat)
        
        call TimerStart(unitex.Refresh, unitex.RefreshTimeout, true, function unitex.onRefresh)
        call TriggerAddCondition(unitex.OnDamage, AntiRepeatExpr)
        call TriggerAddAction(unitex.OnDamage, function unitex.onDamage)
    endmethod
    
    static method create takes player forPlayer, integer unitId, real x, real y, real facing returns unitex
        local unitex udata = unitex.allocate()
        set udata.main = CreateUnit(forPlayer, unitId, x, y, facing)
        set udata.backupLife = udata.life
        
        call SetUnitUserData(udata.main, udata)
        call TriggerRegisterUnitEvent(unitex.OnDamage, udata.main, EVENT_UNIT_DAMAGED)
        return udata
    endmethod
    
    method kill takes boolean destroy returns nothing
        call KillUnit(this.main)
        if destroy then
            call this.destroy()
        endif
    endmethod
endstruct

endlibrary



Changelog

- Removed method operator for blending time.
- Removed method operator for setting unit color.
- Removed unitColor struct member.
- Added 'queueAnimation' method operator.
- Removed lifeStored, manaStored, lifeMaxStored.
- Added 'backupLife' and 'backupLifeMax' in place of previous.
- Added the 'backup' option to .setLifeMaximum.
- Added unit order issuing (immediate, target, point).


Pros

- Easy manipulation of units, so long as they are created using 'unitex.create'.
- Built-in DamageDetection.
- Built-in "Custom Stun" (originally by blu_da_noob)
- Built-in "SetUnitMaxState" (originally by blade.dk)
- Life amount backup for restoring damage that was dealt properly.
- Added .vertexRed through .vertexAlpha method operators so you can know get and set them.
- Whenever animation speed is set via triggers, it is stored in variable for reference.


Cons (to my knowledge)

- It is a pretty large block of code.
- Custom Stun will not work along-side in-game stuns.
- Uses SetUnitUserData.
02-27-2009, 02:01 PM#2
Vexorian
I don't think more events would be too necessary. Uh I don't like it too much for the reason that it is huge, maybe I have to add ruby's modules to vJass...
02-27-2009, 05:48 PM#3
xombie
More events?
02-27-2009, 05:58 PM#4
Vexorian
Quote:
I need to add a generic event too, some how.
I thought you were going to add generic events to it.

I am not sure a generic event is a great idea, but that's rather easy to do isn't it? Just have an array of those function interfaces and run all the ones in the array as well as the one attached to the unit.
02-27-2009, 06:19 PM#5
xombie
Yea sorry I had a brain fart or somethin'. Yea adding the generic event wasn't hard, it was like 5-6AM last night when I put this together.

I am leaving adding anything else out until I get more feedback, though, for now I'm trying to find ways of cleaning it up and making it look nicer.

I updated the first post, added a changelog to keep track of what has been covered and what hasn't.
02-27-2009, 07:50 PM#6
Zerzax
I'd personally use "z" instead of flyHeight in the operator wrappers, though that is just my own taste. Other than that, this is a really nice idea, that seems well organized and definitely piques the curiosity. It seems like at least half of it, however, could be ignored if the person just knows how to use simple unit natives.
02-27-2009, 08:09 PM#7
xombie
The reason I did not use 'z' is because that is not how the game labels theirs. I will add another few method operators though so that you are able to set a unit's z, which would be a combination of GetLocationZ and GetUnitFlyHeight.
02-27-2009, 09:07 PM#8
Vexorian
Uh desyncs such a great idea.
02-27-2009, 09:26 PM#9
xombie
Well you don't have to use GetLocationZ, it is just as much risk as using the native.