• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

[JASS] XP System

JASS:
function CalculateEquivalentXP takes integer heroLevel, integer victimLevel returns integer
    local integer baseXP = 15
    local integer xpPerLevelDiff = 5
    local integer levelDiff = victimLevel - heroLevel
    local integer finalXP = baseXP + (levelDiff * xpPerLevelDiff)

    if finalXP < 0 then
        return 0
    endif

    return finalXP
endfunction

function CustomXPSystem_Conditions takes nothing returns boolean
    local unit killer = GetKillingUnit()
    local unit victim = GetDyingUnit()
  
  
    local boolean isEnemy = IsUnitEnemy(victim, GetOwningPlayer(killer))
  
    local boolean notStructure = not IsUnitType(victim, UNIT_TYPE_STRUCTURE)

    set killer = null
    set victim = null

    return isEnemy and notStructure
endfunction

function CustomXPSystem_Actions takes nothing returns nothing
    local unit killer = GetKillingUnit()
    local unit victim = GetDyingUnit()
    local player killerOwner = GetOwningPlayer(killer)
  
  
    local real deadX = GetUnitX(victim)
    local real deadY = GetUnitY(victim)
    local integer victimLevel = GetUnitLevel(victim)
    local integer maxLevel = udg_MaxLevelChapter
  
  
    local group heroesInRange = CreateGroup()
    local unit u
    local player uOwner
    local integer heroLevel
    local integer xpToGive
  
  
    call GroupEnumUnitsInRange(heroesInRange, deadX, deadY, 1000.0, null)
  
 
    loop
        set u = FirstOfGroup(heroesInRange)
        exitwhen u == null
        call GroupRemoveUnit(heroesInRange, u)
      
      
        if IsUnitType(u, UNIT_TYPE_HERO) and GetWidgetLife(u) > 0.405 and IsUnitAlly(u, killerOwner) then
            set heroLevel = GetHeroLevel(u)
          
          
            if heroLevel < maxLevel then
                set xpToGive = CalculateEquivalentXP(heroLevel, victimLevel)
              
                if xpToGive > 0 then
                    set uOwner = GetOwningPlayer(u)
                  
                  
                    call SetPlayerHandicapXP(uOwner, 1.00)
                  
                  
                    call AddHeroXP(u, xpToGive, true)
                  
                    call SetPlayerHandicapXP(uOwner, 0.00)
                endif
            endif
        endif
    endloop

  
    call DestroyGroup(heroesInRange)
    set killer = null
    set victim = null
    set killerOwner = null
    set heroesInRange = null
    set u = null
    set uOwner = null
endfunction

function InitTrig_CustomXPSystem takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 0
  
    loop
        exitwhen i > 23
        call SetPlayerHandicapXP(Player(i), 0.00)
        set i = i + 1
    endloop
  
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t, Condition(function CustomXPSystem_Conditions))
    call TriggerAddAction(t, function CustomXPSystem_Actions)
endfunction

1778376823582.webp


Is this a good XP script? I made it to manage XP more efficiently instead of having to tweak a thousand different things
 
I would replace that code:
JASS:
function CustomXPSystem_Actions takes nothing returns nothing
    local unit killer = GetKillingUnit()
    local unit victim = GetDyingUnit()
    local player killerOwner = GetOwningPlayer(killer)

with this one:
JASS:
function CustomXPSystem_Actions takes nothing returns nothing
    local unit killer = GetKillingUnit()
    local unit victim = GetDyingUnit()

    if killer == null then
        set victim = null
        return
    endif

    local player killerOwner = GetOwningPlayer(killer)
if the killer were a trigger, the environment, or something else.
But maybe someone smarter can suggest something else to you :thumbs_up:
 
Heroes are often permanent and limited units, which means that they're easy to keep track of yourself. With that in mind, it's more efficient to use an existing Unit Group that will rarely change than creating a new one whenever a unit dies. So here's some ways of handling this...

1. Create a Unit Group array for each Team (Force) and use each Team's number as the index.
2. Enumerate over every unit at the start of the map, find each pre-placed Hero, and add them to their respective Unit Group.
  • Loop - Actions
    • Unit Group - Add (Picked unit) to Hero_Group[(Team number of (Owner of (Picked unit))]
3. Detect when a Hero enters the map (optimize by using "Trained/Sold" events) and add them to their respective Unit Group.
4. Remove heroes from the group when they die (add back upon revival) OR simply check for living status whenever you reference a group.
5. Whenever a unit dies, enumerate over opposing groups and do distance checks between each Hero and the dying unit.
6. Add XP to any Hero within range of the dying unit. If you need to divide the XP then you can loop over the group twice and temporarily track "in range" heroes.

You could also use one big Unit Group and filter unwanted units as needed.

To further optimize things, each Player (or Team) could have their own Unit Group array which tracks all of their enemy heroes. Something like this:
  • Events
    • Unit - A unit Dies
  • Conditions
    • (Killing unit) Not equal to No Unit
  • Actions
    • Set Variable DU1 = (Triggering unit)
    • Set Variable DP1 = (Position of DU1)
    • Set Variable DPN = (Player number of (Owner of DU1))
    • Unit Group - Pick every unit in Enemy_Hero_Group[DPN] and do Actions
      • Loop - Actions
        • Set Variable DU2 = (Picked unit)
        • Set Variable DP2 = (Position of DU2)
        • If (Distance between DP1 and DP2) Less than or equal to 1000.00 then Add exp to DU2...
        • Custom script: call RemoveLocation(udg_DP2)
    • Custom script: call RemoveLocation(udg_DP1)
Add an early return if the Unit Group is empty. You can track this using an Integer array for a highly efficient lookup. By doing so, you can avoid running most of the logic when no eligible Heroes exist.
 
Last edited:
Back
Top