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

[Snippet] UnitData

Level 11
Joined
Sep 30, 2009
Messages
697
Main Library
JASS:
//############################### CREDITS ###################################//
//##
//## Version:       0.04 Alpha
//## System:        Axarion
//## AutoIndex:     grim001
//## AIDS:          Jesus4Lyf
//## UnitIndexer:   Nestharus
//##
//############################### DESCRIPTION ###############################//
//##
//## This system allows an easier manipulation of units. If AutoCreateData is
//## set to true it will also automatically create one UnitData or HeroData
//## instance per unit. The instances are destroyed when the unit leaves the
//## map so you don't have to worry about unused struct instances. 
//##
//## The main part of this system are the modules and everything is optional.
//## Just import UnitData, HeroData and all modules you want into your map.
//## If you feel like doing a module for this system you are free to do so
//## and I will include it if it works well and credit you.
//##
//## Look into the modules to see what can be used. It would just take too much 
//## time and place to list all functions and operators.
//##
//## Note:
//## Right now not all features are done, but I will add the other stuff
//## as fast as possible and include BonusMod, UnitStatus and so on...
//## 
//############################################################################//

library UnitData requires optional UnitIndexer, optional AutoIndex, optional AIDS //one is required!

struct UnitData extends array
    unit InstanceUnit
    integer UnitUserData
    
    //implementing the modules
    implement optional UnitDataCoordinates
    implement optional UnitDataAbilities
    implement optional UnitDataMisc
    implement optional UnitDataState
    implement optional UnitDataOrder
    implement optional UnitDataItem
    
    implement optional HeroDataMisc
    implement optional HeroDataStats
    implement optional HeroDataLevel
    
    
    static method operator [] takes unit u returns thistype
        static if LIBRARY_AIDS then
            return GetUnitIndex(u)
        else
            return GetUnitId(u)
        endif
    endmethod
    
    static method operator []= takes unit u, thistype this returns nothing
        set UnitData[u] = this
    endmethod
    
    static method get takes unit u returns thistype
        return UnitData[u]
    endmethod
    
    static method fromUnit takes unit u returns thistype
        set UnitData[u].InstanceUnit = u                
        return UnitData[u]
    endmethod
    
    static method create takes player p, integer id, real x, real y, real f returns thistype
        return UnitData[CreateUnit(p, id, x, y, f)]
    endmethod

//###################################### INDEXER STUFF ######################################//
    
    // for UnitIndexer
    static if LIBRARY_UnitIndexer then
        
        private static method UnitDataDeindexed takes nothing returns boolean
            set UnitData[GetIndexedUnit()].InstanceUnit = null
            return false
        endmethod
        
        private static method UnitDataIndexed takes nothing returns boolean
            call thistype.fromUnit(GetIndexedUnit())
            return false
        endmethod

        
        private static method onInit takes nothing returns nothing
            call OnUnitDeindex(Condition(function thistype.UnitDataDeindexed))
            call OnUnitIndex  (Condition(function thistype.UnitDataIndexed))
        endmethod
        
    endif
    
    //for AutoIndex
    static if LIBRARY_AutoIndex and not LIBRARY_AIDS then
        
        private static method UnitDataDeindexed takes unit u returns nothing
            set UnitData[u].InstanceUnit = null
        endmethod
        
        static if AutoCreateData then
            private static method UnitDataIndexed takes unit u returns nothing
                call thistype.fromUnit(u)
            endmethod
        endif
        
        private static method onInit takes nothing returns nothing
            call OnUnitDeindexed(thistype.UnitDataDeindexed)
            call OnUnitIndexed(thistype.UnitDataIndexed)
        endmethod
        
    endif
endstruct

// for people with AIDS 
static if LIBRARY_AIDS then
    private struct UnitDataAIDS extends array

        method AIDS_onCreate takes nothing returns nothing
            call UnitData.fromUnit(.unit)
        endmethod
        
        method AIDS_onDestroy takes nothing returns nothing
            set UnitData[.unit].InstanceUnit = null
        endmethod
        //! runtextmacro optional AIDS()
    endstruct
endif

endlibrary

  • UnitDataState
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module UnitDataState
        method operator life takes nothing returns real
            return GetWidgetLife(.InstanceUnit)
        endmethod
        
        method operator life= takes real newLife returns nothing
            call SetWidgetLife(.InstanceUnit, newLife)
        endmethod
        
        method operator maxlife takes nothing returns real
            return GetUnitState(.InstanceUnit, UNIT_STATE_MAX_LIFE)
        endmethod
        
        method operator mana takes nothing returns real
            return GetUnitState(.InstanceUnit, UNIT_STATE_MANA)
        endmethod
        
        method operator mana= takes real newMana returns nothing
            call SetUnitState(.InstanceUnit, UNIT_STATE_MANA, newMana)
        endmethod
        
        method operator maxmana takes nothing returns real
            return GetUnitState(.InstanceUnit, UNIT_STATE_MAX_MANA)
        endmethod
    endmodule
  • UnitDataMisc
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module UnitDataMisc
        method operator name takes nothing returns string
            return GetUnitName(.InstanceUnit)
        endmethod
        
        method operator id takes nothing returns integer
            return GetUnitTypeId(.InstanceUnit)
        endmethod
        
        method operator owner takes nothing returns player
            return GetOwningPlayer(.InstanceUnit)
        endmethod
        
        method operator owner= takes player p returns nothing
            call SetUnitOwner(.InstanceUnit, p, false)
        endmethod
        
        method operator level takes nothing returns integer
            return GetUnitLevel(.InstanceUnit)
        endmethod
        
        method kill takes nothing returns nothing
            call KillUnit(.InstanceUnit)
        endmethod
    
        method remove takes nothing returns nothing
            call RemoveUnit(.InstanceUnit)
        endmethod
        
        method operator hide= takes boolean val returns nothing
            call ShowUnit(.InstanceUnit, val)
        endmethod
        
        method operator acquireRange takes nothing returns real
            return GetUnitAcquireRange(.InstanceUnit)
        endmethod
        
        method operator acquireRange= takes real range returns nothing
            call SetUnitAcquireRange(.InstanceUnit, range)
        endmethod
        
        method operator foodUsed takes nothing returns integer
            return GetUnitFoodUsed(.InstanceUnit)
        endmethod
        
        method operator useFood= takes boolean flag returns nothing
            call SetUnitUseFood(.InstanceUnit, flag)
        endmethod
        
        method operator foodMade takes nothing returns integer
            return GetUnitFoodMade(.InstanceUnit)
        endmethod
        
        method isType takes unittype whichUnitType returns boolean
            return IsUnitType(.InstanceUnit, whichUnitType)
        endmethod
    endmodule
  • UnitDataCoordinates
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module UnitDataCoordinates
        method operator x takes nothing returns real
            return GetUnitX(.InstanceUnit)
        endmethod
        
        method operator x= takes real x returns nothing
            call SetUnitX(.InstanceUnit, x)
        endmethod
        
        method operator y takes nothing returns real
            return GetUnitY(.InstanceUnit)
        endmethod
        
        method operator y= takes real y returns nothing
            call SetUnitY(.InstanceUnit, y)
        endmethod
        
        method operator z takes nothing returns real
            return GetUnitFlyHeight(.InstanceUnit)
        endmethod
        
        method operator z= takes real z returns nothing
            call SetUnitFlyHeight(.InstanceUnit, z, 0)
        endmethod
        
        method operator facing takes nothing returns real
            return GetUnitFacing(.InstanceUnit)
        endmethod
        
        method operator facing= takes real facing returns nothing
            call SetUnitFacing(.InstanceUnit, facing)
        endmethod
    endmodule
  • UnitDataAbilities
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module UnitDataAbilities
        method addAbility takes integer id returns boolean
            return UnitAddAbility(.InstanceUnit, id)
        endmethod
        
        method removeAbility takes integer id returns boolean
            return UnitRemoveAbility(.InstanceUnit, id)
        endmethod
        
        method setAbilityLevel takes integer id, integer level returns integer
            return SetUnitAbilityLevel(.InstanceUnit, id, level)
        endmethod
        
        method resetAbilityCooldown takes nothing returns nothing
            call UnitResetCooldown(.InstanceUnit)
        endmethod
        
        method makeAbilityPermanent takes integer id, boolean permanent returns boolean
            return UnitMakeAbilityPermanent(.InstanceUnit, permanent, id)
        endmethod
    endmodule
  • UnitDataOrder
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module UnitDataOrder
        
        method operator order takes nothing returns integer
            return GetUnitCurrentOrder (.InstanceUnit)
        endmethod
        
        method pointOrder takes string order, real x, real y returns boolean
            return IssuePointOrder(.InstanceUnit, order, x, y)
        endmethod
        
        method pointOrderId takes integer orderId, real x, real y returns boolean
            return IssuePointOrderById(.InstanceUnit, orderId, x, y)
        endmethod
        
        method targetOrder takes string order, widget target returns boolean
            return IssueTargetOrder(.InstanceUnit, order, target)
        endmethod
        
        method targetOrderId takes integer orderId, widget target returns boolean
            return IssueTargetOrderById(.InstanceUnit, orderId, target)
        endmethod
        
        method immediateOrder takes string order returns boolean
            return IssueImmediateOrder(.InstanceUnit, order)
        endmethod
    
        method immediateOrderId takes integer orderId returns boolean
            return IssueImmediateOrderById(.InstanceUnit, orderId)
        endmethod
    endmodule
  • UnitDataItem
    JASS:
    module UnitDataItem 
        
        method addItem takes item i returns boolean
            return UnitAddItem(.InstanceUnit, i)
        endmethod
        
        method addItemId takes integer id returns item
            return UnitAddItemById(.InstanceUnit, id)
        endmethod
        
        method removeItem takes item i returns nothing
            call UnitRemoveItem(.InstanceUnit, i)
        endmethod
        
        method useItem takes item i returns boolean
            return UnitUseItem(.InstanceUnit, i)
        endmethod
        
        method useItemTarget takes item i, widget target returns boolean
            return UnitUseItemTarget(.InstanceUnit, i, target)
        endmethod
        
        method useItemXY takes item i, real x, real y returns boolean
            return UnitUseItemPoint(.InstanceUnit, i, x ,y)
        endmethod
        
        method itemInSlot takes integer slot returns item
            return UnitItemInSlot(.InstanceUnit, slot)
        endmethod
        
        method hasItem takes item i returns boolean
            return UnitHasItem(.InstanceUnit, i)
        endmethod
        
    endmodule
  • HeroDataMisc
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module HeroDataMisc
        method revive takes real x, real y, boolean doEyecandy returns boolean
            return ReviveHero(.InstanceUnit, x, y, doEyecandy)
        endmethod
        
        method operator heroname takes nothing returns string
            return GetHeroProperName(.InstanceUnit)
        endmethod
    endmodule
  • HeroDataStats
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module HeroDataStats
        method operator strength takes nothing returns integer
            return GetHeroStr(.InstanceUnit, false)
        endmethod
        
        method operator strength= takes integer i returns nothing
            call SetHeroStr(.InstanceUnit, i, true)
        endmethod
        
        method operator agility takes nothing returns integer
            return GetHeroAgi(.InstanceUnit, false)
        endmethod
        
        method operator agility= takes integer i returns nothing
            call SetHeroAgi(.InstanceUnit, i, true)
        endmethod
        
        method operator intelligence takes nothing returns integer
            return GetHeroInt(.InstanceUnit, false)
        endmethod
        
        method operator intelligence= takes integer i returns nothing
            call SetHeroInt(.InstanceUnit, i, true)
        endmethod
    endmodule
  • HeroDataLevel
    JASS:
    //############################### INFORMATION ##################################//
    //## Creator: Axarion                                                           //
    //##############################################################################//
    
    module HeroDataLevel
        method operator experience takes nothing returns integer
            return GetHeroXP(.InstanceUnit)
        endmethod
        
        method operator experience= takes integer i returns nothing
            call SetHeroXP(.InstanceUnit, i, true)
        endmethod
        
        method operator herolevel takes nothing returns integer
            return GetHeroLevel(.InstanceUnit)
        endmethod
        
        method operator herolevel= takes integer i returns nothing
            call SetHeroLevel(.InstanceUnit, i, true)
        endmethod
        
        method operator skillpoints takes nothing returns integer
            return GetHeroSkillPoints(.InstanceUnit)
        endmethod
        
        method operator skillpoints= takes integer i returns nothing
            call UnitModifySkillPoints(.InstanceUnit, i - .skillpoints)
        endmethod
     endmodule


Changelog
  • Version 0.01 Alpha: First Release
  • Version 0.02 Alpha: Added UnitDataOrder module, fixed a minor fail
  • Version 0.03 Alpha: Recoded some things, made it an array struct, merged UnitData and HeroData, removed AutoCreateData boolean
  • Version 0.04 Alpha: Added UnitDataItem
 

Attachments

  • UnitData.w3x
    58.1 KB · Views: 185
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
JASS:
    private struct UnitData_AIDS extends array // This throws a syntax error: invalid struct name.  Should be UnitDataAIDS
        //! runtextmacro optional AIDS()
        // Having the textmacro at the top of an AIDS struct has very
        // negative side-effects when JassHelper compiles.  It is efficient
        // to put it at the VERY BOTTOM of the struct.  The difference in 
        // wastefully-generated (JassHelper-cloned) text is absurd, and it
        // uses function interfaces (slow evaluations) instead of direct
        // calls.
        
        static if AutoCreateData then
            method AIDS_onCreate takes nothing returns nothing
                if IsUnitType(.unit, UNIT_TYPE_HERO) then
                    call HeroData.fromHero(.unit)
                else
                    call UnitData.fromUnit(.unit)
                endif

            endmethod
        endif
        
        method AIDS_onDestroy takes nothing returns nothing
            call UnitData.get(.unit).destroy()
        endmethod
    endstruct

JASS:
    static method get takes unit u returns thistype
        return Units[GetUnitId(u)]
    endmethod

Personal preference, this should be changed to:

JASS:
    static method operator [] takes unit u returns thistype
        return Units[GetUnitId(u)]
    endmethod

I am also very curious why you are instanciating your own struct as opposed to making yours an array struct. All you succeed in doing is doubling the amount of array calls necessary. All of those unit-indexing libraries only return values 1-8190, so those are perfect for structs as-is. struct saver extends array

That would turn your get method into:

JASS:
    static method operator [] takes unit u returns thistype
        return GetUnitId(u)
    endmethod
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
He does that for simplicity and organization on the user-end. If you look at the compiled code difference you will see about 100 extra lines of code if you put the textmacro at the top of the struct.

// Generated method caller for AIDS_onCreate
(code block)

// Generated method executor for AIDS_onCreate
(code block)

And it adds triggers to your globals block, sets up a few statements in the map initialization header to initialize all the generated triggers... try it out. You would be disgusted by the wastefulness of JassHelper.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I've optimized your script and got rid of the "fromUnit" command as that kind of thing should be handled by the indexing libraries themselves or by UnitData.create.

InstanceUnit is readonly so that the user cannot tamper with it.

"unit" is a new method operator which returns InstanceUnit (just makes it easier to type and to work with).

Your initialization setup has been really cleaned up and made as optimal as I can think to make it.

I also attached links to the three unit-indexing libraries, something I always recommend doing.

Also, before posting, go to Advanced Options and uncheck 'automatically parse links in text' due to the generated url wrapper tags this forum uses.

JASS:
//############################### CREDITS ###################################//
//##
//## Version:       0.04 Alpha
//## System:        Axarion
//## AutoIndex:     grim001     http://www.wc3c.net/showthread.php?t=105643
//## AIDS:          Jesus4Lyf   http://www.thehelper.net/forums/showthread.php?t=130752
//## UnitIndexer:   Nestharus   http://www.hiveworkshop.com/forums/showthread.php?t=172090
//##
//############################### DESCRIPTION ###############################//
//##
//## This system allows an easier manipulation of units. The instances are
//## destroyed when the unit leaves the map so you don't have to worry about
//## unused struct instances.
//##
//## The main part of this system are the modules and everything is optional.
//## Just import UnitData, HeroData and all modules you want into your map.
//## If you feel like doing a module for this system you are free to do so
//## and I will include it if it works well and credit you.
//##
//## Look into the modules to see what can be used. It would just take too much
//## time and place to list all functions and operators.
//##
//## Note:
//## Right now not all features are done, but I will add the other stuff
//## as fast as possible and include BonusMod, UnitStatus and so on...
//##
//############################################################################//

library UnitData requires optional UnitIndexer, optional AutoIndex, optional AIDS
    
struct UnitData extends array
    
    static if LIBRARY_AutoIndex then
        private unit array units
    endif
    
    implement optional UnitDataCoordinates
    implement optional UnitDataAbilities
    implement optional UnitDataMisc
    implement optional UnitDataState
    implement optional UnitDataOrder
    implement optional UnitDataItem
    
    implement optional HeroDataMisc
    implement optional HeroDataStats
    implement optional HeroDataLevel
    
    
    static method operator [] takes unit u returns thistype
        return GetUnitId(u)
    endmethod
    
    static method get takes unit u returns thistype
        return GetUnitId(u)
    endmethod
    
    method operator unit takes nothing returns unit
        
        static if LIBRARY_UnitIndexer then
            return GetUnitById(this)
        elseif LIBRARY_AIDS then
            return GetIndexUnit(this)
        elseif LIBRARY_AutoIndex then
            return units[this]
        endif
        
    endmethod
    
    method operator InstanceUnit takes nothing returns unit
        return this.unit
    endmethod
    
    static method create takes player p, integer id, real x, real y, real f returns thistype
        static if LIBRARY_AutoIndex then
            
            return GetUnitId(CreateUnit(p, id, x, y, f))
            // I am pretty sure this returns 0 so don't use UnitData.create() with AutoIndex.
            
        elseif LIBRARY_AIDS then
            
            return GetUnitIndex(CreateUnit(p, id, x, y, f))
            
        elseif LIBRARY_UnitIndexer then
            
            return IndexUnit(CreateUnit(p, id, x, y, f))
        endif
    endmethod
    
    static if LIBRARY_AutoIndex then
        
        private static method UnitDataIndexed takes unit u returns nothing
            set units[GetUnitId(u)] = u
        endmethod
        
        private static method UnitDataDeindexed takes unit u returns nothing
            set units[GetUnitId(u)] = null
        endmethod
        
        private static method onInit takes nothing returns nothing
            call OnUnitIndexed(thistype.UnitDataIndexed)
            call OnUnitDeindexed(thistype.UnitDataDeindexed)
        endmethod
        
    endif
    
endstruct
    
endlibrary

I also recommend this:

JASS:
native UnitAlive takes unit id returns boolean

module UnitDataAlive
    method alive takes nothing returns boolean
        return UnitAlive(this.unit)
    endmethod
endmodule
 
Last edited:
Level 22
Joined
Nov 14, 2008
Messages
3,256
Are you now saying that Jasshelper now directly conflicts with the Optimizer?

Well, time to throw out the Optimizer. ExecuteFunc was one thing, but this...

Yes. It's kinda weird, maybe there is an option you can turn off to prevent this but when you want to optimal savings or use it as normal it corrupts the map, making it not-playable (you can't even start the game).

Wonder if I should PM Vex about this lol...

EDIT:

Sorry Axarion for all these off-topic stuff. I'll end it now.
 
Level 7
Joined
Dec 3, 2006
Messages
339
How would i go about adding a value that doesn't have a pre-defined function? For example if i added bounty to the UnitDataMisc i used:
JASS:
    method operator bountyValue takes nothing returns real
        return GetUnitBounty(.InstanceUnit)
    endmethod
 
    method operator bountyValue= takes real bounty returns nothing
       return SetUnitBounty(.InstanceUnit, bounty)
    endmethod

But how would i set up the SetUnitBounty() and GetUnitBounty() functions??? Or is there a better way to do this?
(if it helps i'm using AIDS, I mean I could use a struct in another trigger to attach data to a unit but that sort of defeats the purpose of using this system right?)
 
Level 7
Joined
Dec 3, 2006
Messages
339
Obviously i'm not talking about wc3's built in bounty system. I said in my post asking i want to attach values to units using this system without pre-defined functions. And I don't think the alternative of attaching a struct to the unit outside the system cause that defeats the purpose of this system doesn't it?
 
Level 11
Joined
Sep 30, 2009
Messages
697
Obviously i'm not talking about wc3's built in bounty system. I said in my post asking i want to attach values to units using this system without pre-defined functions. And I don't think the alternative of attaching a struct to the unit outside the system cause that defeats the purpose of this system doesn't it?

So you want to have some type of integer/real which stores the bonty of a unit or do i get you wrong?


JASS:
module UnitBounty

   real bounty 

   method operator bountyValue takes nothing returns real
       return GetUnitBounty(.InstanceUnit)
   endmethod
 
   method operator bountyValue= takes real newbounty returns nothing
     set .bounty = newbounty
   endmethod

endmodule
 
Level 7
Joined
Dec 3, 2006
Messages
339
Ah ok, since there was no documentation so i was a bit confused as to how you'd go about using your system to attach a value thats not already pre-defined. I think that should work though thanks.

Edit: This works. I ended up using this:
JASS:
module UnitBounty
   integer bounty
   method operator bountyValue takes nothing returns integer
       return UnitData[.InstanceUnit].bounty
   endmethod
 
   method operator bountyValue= takes integer newbounty returns nothing
     set .bounty = newbounty
   endmethod
endmodule
 
Last edited:
Level 10
Joined
Sep 3, 2009
Messages
458
This is pretty useful. I have a question.

Can I create a new data for a hero/unit? For example:

HeroAgi
HeroStr
Heroint

Now I want to put a HeroConstitution data for heroes. Is it possible to add a HeroConstitution Data? If it is possible, how?

Also I'm new to vJass and I'm not really anywhere near modules and some other terms so bear with me :)

EDIT: +rep for the awesomeness of this!
 
Level 11
Joined
Sep 30, 2009
Messages
697
This is pretty useful. I have a question.

Can I create a new data for a hero/unit? For example:

HeroAgi
HeroStr
Heroint

Now I want to put a HeroConstitution data for heroes. Is it possible to add a HeroConstitution Data? If it is possible, how?

Also I'm new to vJass and I'm not really anywhere near modules and some other terms so bear with me :)

EDIT: +rep for the awesomeness of this!

The hero stats module exists. If you want to store more data just create a variable in the UnitData Struct or in a module and implement it.
 
Level 1
Joined
Nov 7, 2010
Messages
3
Hey :/ i got a problem!
1.) When i try to do some struct / scope triggers with vJass (1.5d) then it says all statements are outside of function!!! i got over 50 errors with this bug....
2.) All Spells i downloadet which use scopes are ok but i cant test the map(i get translated to the wc3 main menu) :(
CAn anyone help me pls to fix this?
 
Level 7
Joined
Dec 3, 2006
Messages
339
Did this honestly deprecate this?: http://www.hiveworkshop.com/forums/graveyard-418/unitproperty-150525/

In my opinion the UnitProperty serves a completely different purpose. No other script exports data straight from the object editor as far as i've seen. Although I do seem to have trouble with getting it to work though (probably cause i'm not doing it completely right).

Also the clashing with a BonusMod and other things seems to be somewhat of a problem with UnitProperty, but could be worked around. If UnitProperty was modified to work with autoindex, aids, etc i think it'd be better than this and could rise from the graveyard xD.

Just the GMSI script addition alone helps benefit the idea of having large amounts of struct members attached to units way more.
 
Top