HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Darkwulfv's EXPERIENCE System

07-26-2007, 08:09 PM#1
darkwulfv
It's really a basic system, but I thought I'd submit it anyways.

What it does:
Reverses Blizzard's EXP system (More heroes = less XP) and flips it to (More heroes = More XP). It also allows you to control the amount of EXP granted, and what units in the radius are checked for and given XP (These work off the same real, but if requested I can make them independent, so that you can check for heroes in, say, 1000, and only grant the XP to heroes in 600)

What else can it do?
It can be reversed back to Blizzard's method (More heroes = less XP) while still retaining the ability to edit the radius of effect and XP amounts granted. It also allows you to give heroes less XP if they're farther away. Also supports XP splitting.

Why I made it:
My map, Wolf Packs, is obviously a pack-orientated game. However, soloing would allow you to level up far faster than packs. So, to counter this, I made this system. Makes sense doesn't it? Also, it was a shitload easier to use than changing Blizzard's constants.

What it can be used for:
Games like mine that emphasize pack/group/team behavior, or RPG's even. If you can find a use for it, go right ahead.

The Code

Collapse JASS:
scope DEXP initializer Init

  //Configuration globals
globals
  private constant real RADIUS = 650. //Radius to consider heroes for exp gain
  private constant real CLOSE = 200. //Determines "Close" for distance effect. Gives full exp
  private constant real MEDIUM = 400. //Determines the "Middle" for distance effect. Gives less exp
  private constant real FAR = 650. //Determines the "Far" for distance effect. Gives even less exp
  //Should probably be the same as Radius.
  private constant real CLOSE_FACTOR = 1. //Determines what % of exp heroes in the "close" range get. (1 == 100%)
  private constant real MEDIUM_FACTOR = .5 //Determines what % of exp heroes in the "medium" range get. (.5 == 50%)
  private constant real FAR_FACTOR = .25  //Determines what % of exp heroes in the "far" range get. (.25 == 25%)
  
  private constant integer UNIT_EXP = 10 //How much exp per level (of the unit) units give.
  private constant integer HERO_EXP = 15 //How much exp per level (of the dying hero) heroes give.
  private constant integer BONUS_EXP = 9 //How much exp is granted per allies hero nearby.
  //If this value is positive, more heroes == more exp each
  //if this value is negative, more heroes == less exp each ('Blizzard' method)
  //If this value is 0, the number of heroes effects nothing.
  
  //This is for the displaying of exp in textag format
  private constant real DURATION = 5. //How long the message will last
  private constant integer SIZE = 12 //Font size
  private constant integer RED = 0 //Red value (of 255) for the text
  private constant integer GREEN = 74 //Green value (of 255) for the text
  private constant integer BLUE = 255 //Blue value (of 255) for the text
  
  private constant boolean SHOW_TEXT = true //Set this to false to stop texttags 
  private constant boolean SPLIT = false //Set to true to divide the final exp by the number of heroes
  private constant boolean DISTANCE_EFFECT = true //Set to false if you do not want the 
  //distance from the kill to determine how much exp the heroes get.
endglobals

private constant function Formula takes integer XPfactor, integer unitlevel, integer heroesinradius, integer bonus returns integer
  return (XPfactor * unitlevel) + (heroesinradius * bonus)
    //This is the function that determines how XP is calculted.
    //Unless you have a different method, don't touch this.
endfunction
   
//This is the filter that picks up heroes in the radius of the killer.
//To modify it, simply add more booleans.
private function filter takes nothing returns boolean
  local unit u = GetFilterUnit()
  local boolean b1 = IsUnitType(u, UNIT_TYPE_DEAD) == false
  local boolean b2 = IsUnitType(u, UNIT_TYPE_HERO) == true
  local boolean b3 = IsUnitAlly(u, GetOwningPlayer(GetKillingUnit())) == true
  
  set u = null
  return b1 and b2 and b3
endfunction

//Use this filter to modify the exp given for certain conditions.
private function ExpFilter takes integer exp, unit u returns integer
   return exp
endfunction

//This applies the XP accordingly.
private function GiveXP takes nothing returns nothing
  local unit u = GetDyingUnit()  
  local unit f
  local unit Killer = GetKillingUnit()
  local player p = GetOwningPlayer(Killer)
  local integer add
  local integer KilledUnitLevel
  local integer unitNum = 0
  local group g = CreateGroup()
  local real x = GetUnitX(Killer)
  local real y = GetUnitY(Killer)
  local real distance  
  set bj_groupCountUnits = 0

  
  call GroupEnumUnitsInRange(g, x, y, RADIUS, Condition(function filter))
  call ForGroup(g, function CountUnitsInGroupEnum)
  set unitNum = bj_groupCountUnits

//============================================================
  if IsUnitAlly(u, p) == false then    
    if IsUnitType(u, UNIT_TYPE_HERO) == true then
      set KilledUnitLevel = GetHeroLevel(u)
      set add = Formula(HERO_EXP, KilledUnitLevel, unitNum, BONUS_EXP) 
    else
      set KilledUnitLevel = GetUnitLevel(u)
      set add = Formula(UNIT_EXP, KilledUnitLevel, unitNum, BONUS_EXP)
    endif  
  endif
    
  if SPLIT then
    set add = R2I(add / unitNum)
  endif
//============================================================
         
  if DISTANCE_EFFECT then

    set add = ExpFilter(add, u)
    loop
    set f = FirstOfGroup(g)
      exitwhen f == null
      set distance = ((GetUnitX(Killer)-GetUnitX(f)) * (GetUnitX(Killer)-GetUnitX(f))) + ((GetUnitY(Killer)-GetUnitY(f)) * (GetUnitY(Killer)-GetUnitY(f)))
      //Far Away Units
      if distance > MEDIUM*MEDIUM and distance <= FAR*FAR then 
        call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
        set add = R2I(add * FAR_FACTOR)
        call AddHeroXP(f, add, true)
        call SetPlayerHandicapXP(GetOwningPlayer(f), 0.00)
      //Medium Distance Units  
      elseif distance > CLOSE*CLOSE and distance <= MEDIUM*MEDIUM then
        call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
        set add = R2I(add * MEDIUM_FACTOR)
        call AddHeroXP(f, add, true)
        call SetPlayerHandicapXP(GetOwningPlayer(f), 0.00)
      //Close Range Units
      elseif distance <= CLOSE*CLOSE then
        call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
        set add = R2I(add * CLOSE_FACTOR)
        call AddHeroXP(f, add, true)
        call SetPlayerHandicapXP(GetOwningPlayer(f), 0.00)
      endif
        if SHOW_TEXT and add > 0 then
        call CreateTextTagEX("+ " + I2S(add), SIZE, f, DURATION, RED, GREEN, BLUE)
      endif
        call GroupRemoveUnit(g, f)
    endloop
    
  else
    set add = ExpFilter(add, u) 
    loop
    set f = FirstOfGroup(g) 
      exitwhen f == null   
      if SHOW_TEXT and add > 0 then
        call CreateTextTagEX("+ " + I2S(add), SIZE, f, DURATION, RED, GREEN, BLUE)
      endif
      call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
      call AddHeroXP(f, add, true)
      call SetPlayerHandicapXP(GetOwningPlayer(f), 0.00)
      call GroupRemoveUnit(g, f)
    endloop           
  endif

  call DestroyGroup(g)
  set g = null
  set u = null
  set Killer = null
  set f = null                          
endfunction


//This trigger stops players from gaining XP when they shouldn't be.
private function Cut_XP_Gain takes nothing returns nothing
   local integer i = 0
   loop
     exitwhen i > 12
     call SetPlayerHandicapXP(Player(i), 0.00)
     set i = i + 1
   endloop
   call DestroyTimer(GetExpiredTimer())
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH )
    call TriggerAddAction(t, function GiveXP )
    call TimerStart(CreateTimer(), .04, false, function Cut_XP_Gain)
    set t = null
endfunction
endscope

The Demo Map:Let your heroes kill. Look for the following:
  • The solo hero will have less XP than the non-solo hero. (Primary heroes == Non-"Random Lazy Heroes")
  • The "Random Lazy Heroes" will have different XP amounts in accordance to their position.

Implementing The System:
All instructions for doing so are inside the demo map.

System Requirements:
The ability to read and understand basic instructions
The ability to change numbers and words
NOTE: This requires JassNewGenPack to use the vJASS version. It's nothing different except vJASSified. Old version still in map with all the new features.
No extra stuff (NewGenJASS, grimoire, WEU, CScache, etc.) are needed. But NewGen will make your life easier =D

Code:
Changelog
-v1.9. Lied again! Fixed a problem with the test map not working, as well as made the texttags show up even when using range-determined exp. Thanks Zaraf for discovering the bug.
-v1.8: I lied, new version. Now has a filter for the dying unit (to modify exp), texttag support, and some tweaks. Plus a vJASS version.
-v1.7: Probably the final version. Merged the 2 triggers and tweaked minor details. Thanks for the help Moyack!
-v1.6: Optimized and tweaked the code a bit.
-v1.5: Added even more features and optimized the code.
-v1.4: Added 2 new features and fixed any leftover indentation slop.
-v1.3: Indented the code the conformist way. (Looks no different to me, but whatever)
-v1.2: Optimized the code more. (thanks PipeDream and Pyrogasm!)
-v1.1: Changed some tiny stuff involving my loops and if's (thanks Ammorth and Pyrogasm!)
-v1.0: I made the system. Nothing special.

Credits:
Darkwulfv (me) - I made the system. W00t.
Pyrogasm - He read my code, checked it, and made the sexy image you see down below
Rising_Dusk - He gave me the idea to do the system, and answered some questions I had.
PipeDream - He forced me to indent my code, so he gets credit for some reason.
The_Elite - He suggested hero XP splitting according to distance from the kill. (Which i used)
Moyack - He merged my 2 triggers and did other minor touch-ups. Thanks!
Zaraf - Discovered that the test map doesn't work (minor errors on my part).


-Enjoy! If you feel I need to add more information, let me know and I'll get right on it.
Attached Images
File type: jpgEXP Icon.jpg (42.3 KB)
Attached Files
File type: w3xDarkwulfv's EXP System 1.9.w3x (25.3 KB)
07-27-2007, 07:01 AM#3
Ammorth
Few things.

Change:
Collapse JASS:
set f = FirstOfGroup(allies) 
   loop
   exitwhen f == null   
       call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
              call AddHeroXP(f, add, true)
       call SetPlayerHandicapXP(p, 0.00)
       call GroupRemoveUnit(allies, f)
   set f = FirstOfGroup(allies)
   endloop
to
Collapse JASS:
   loop
       set f = FirstOfGroup(allies) 
       exitwhen f == null   
       call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
              call AddHeroXP(f, add, true)
       call SetPlayerHandicapXP(p, 0.00)
       call GroupRemoveUnit(allies, f)
   endloop                       
endfunction
(and same with the other loop).

Don't use count units in group. Just add 1 to the integer variable as you add the units to the group.

Otherwise nice system; simple and effective.
07-27-2007, 07:08 AM#4
darkwulfv
Thanks for spotting that. I'm still not perfect with JASS, but I think I did a good job. I'll replace all the code accordingly and update the map.

Quote:
Otherwise nice system; simple and effective.
Thank you =) I was afraid I was gonna get a comment along the lines of "This system is too basic and limited, SystemX [insert link here] does thisthisthisthisthis and this, whereas yours only does this and this."
07-27-2007, 07:20 AM#5
PipeDream
  • http://www.wc3campaigns.net/showthread.php?t=90186
  • darkwulfv = toadcop? Please indent your code sanely =)
  • Try refactoring your code, I think you could make it shorter and simpler. If there are things you do because you are unsure of difficult to test bugs, please ask.
07-27-2007, 07:34 AM#6
darkwulfv
Quote:
Originally Posted by PipeDream
  • http://www.wc3campaigns.net/showthread.php?t=90186
  • darkwulfv = toadcop? Please indent your code sanely =)
  • Try refactoring your code, I think you could make it shorter and simpler. If there are things you do because you are unsure of difficult to test bugs, please ask.
  • It shouldn't EVER run as often as that thread was tested for. If your unit's are set to kill something more than 15 times per second, then your map is psycho. I don't think the GroupEnum's will make much, if any, difference in the memory usage. I played a 45 minute game with this system with no sign of major memory increase.
  • That's how I indent my code. I've always done it like that and always will unless something forces me to do otherwise. Force of habits ftw.
  • Try re-whattering my code? I shortened the code down some, but if you spot anywhere to fix it up, don't hesitate to point it out. Thanks!
  • I think I see where some stuff can be changed. There's some stuff left over from stuff I'd put in the in case I would need it and never used it. I'll get to changing it in a moment.
Psst... Did you try the system though? I'm proud of my work and want to show it off, even though it's not that glorious =p
07-27-2007, 07:40 AM#7
Alvatar
Seems most of these can be done only with game constants
07-27-2007, 07:55 AM#8
darkwulfv
True, but game constants are a hassle to sort through. Also, this gives you more control and better handling.

Lemme ask you. Which would you rather do? Sort through all of Blizzard's hero experience constants and change each and every one to your needs, or change ~4 values in a couple keystrokes? If you read, you'd also know it's reversable, so you can go make it like Blizzard's system, but with more control.

PipeDream, I have rewhatevered my system, it's more optimized now. The initial function (the action for the core) was there because of an earlier method I was using, before I discovered it could be changed. The code is now fixed and ready for another review. Thanks =D
07-27-2007, 04:20 PM#9
Game_Slave
There are many exp related constants that make manually editing Blizzard's exp system very difficult, so I can see the reason behind this system, however, I have no use for it.
07-27-2007, 05:15 PM#10
Rising_Dusk
Quote:
darkwulfv = toadcop? Please indent your code sanely =)
I've told him that dozens of times.
He stubbornly refuses, pretending somehow that his method is the best or whatever. -_-;
07-27-2007, 05:20 PM#11
Toadcop
Quote:
I've told him that dozens of times.
He stubbornly refuses, pretending somehow that his method is the best or whatever. -_-;
yes it's the best. for him. <_<
07-27-2007, 05:31 PM#12
Rising_Dusk
But when you submit it as a resource, it no longer matters what is best for the submitter, but for those who might use it.
That's the whole point, because this level of indenting is terrible for what now matters.
07-27-2007, 05:39 PM#13
darkwulfv
Rawr to all those who hate my indenting. It makes sense to me at least.

If you're all so against it, someone's gonna have to tell me how the hell to "fix" it, because nobody ever told me the "standard" method. I taught myself to indent. If someone tells me, then maybe I'll be a little more willing to fix it, rather than everyone just saying "Your indent sucks fixfixfix". If I knew the quote-unquote "standard" method, do you think I'd still be doing it like this? (Even though it makes perfect sense to me).

Quote:
pretending somehow that his method is the best or whatever
Dont'chu be putting words in my mouth Dusker. I said my indenting method was the way I learned and it was best for ME, because I learned it that way. You never told me the "proper" method, just said it sucked, you hated it, it was a bad habit, and should be fixed.

You know the old phrase "You can lead a horse to water but you can't make it drink?" It's the same concept here, except you're all assuming I know the way to the water and you're dunking my head in it. I'm only semi-defending my indentation methods since that's how I learned and have always done it. But if someone shows me the "standards", by all means I'll fix it.
07-27-2007, 06:45 PM#14
PitzerMike
It's rather simple. Everything inside the same block is indented one level deeper than stuff outside the block.
With blocks i mean stuff between function - endfunction, loop - endloop, globals - endglobals, if/else - endif
That especially means that locals, set and call are not at different levels of indentation when they`re in the same block.

Fix:
Collapse JASS:
//This applies the XP accordingly.
function DES_GiveXP takes nothing returns nothing
  local unit u = GetDyingUnit()
  // etc...

  loop
    set f = FirstOfGroup(g)
    exitwhen f == null
    if IsUnitAlly(f, p) and IsUnitType(f, UNIT_TYPE_HERO) == true then 
      call GroupAddUnit(allies, f)
      set unitNum = unitNum + 1
    endif                  
    call GroupRemoveUnit(g, f)      
  endloop
   
  // etc. ....
      
  loop
    set f = FirstOfGroup(allies) 
    exitwhen f == null   
    call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
    call AddHeroXP(f, add, true)
    call SetPlayerHandicapXP(p, 0.00)
    call GroupRemoveUnit(allies, f)
  endloop     

  call DestroyGroup(g)
  call DestroyGroup(allies)
  set u = null
  set Killer = null
  set f = null                          
endfunction

I mean, how does the following make sense?
Collapse JASS:
set f = FirstOfGroup(allies) 
   loop
   exitwhen f == null   
       call SetPlayerHandicapXP(GetOwningPlayer(f), 1.00)
              call AddHeroXP(f, add, true)

The number of spaces per level of indentation doesn't really matter, I've used 2 in the example but it should be equal for all levels.

And I agree with everyone else in the thread that sane indentation is a vital part of jass systems.
07-27-2007, 07:26 PM#15
Rising_Dusk
I generally use 4 between levels for my code, but it doesn't really matter as long as it's consistent.
Otherwise I agree with everything Pitzer mentioned.