• 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.

[System] CustomHero

I think this is way too simple to actually justify a system resource.

Anyone with enough JASS knowledge to use structs can write something like that customized for his map needs on his own.


It also lacks basic functionality like attribute modifiers (linear and multipliers).

If I'd have to manually keep track of all temporary attribute modifiers, I don't see the point in using an external library in the first place.

Every custom attribute should have a base value, linear bonus and multiplier factor all seperate from each other.
Then a GetAttribute call returns (base value + linear bonus) * multiplier.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
I think this is way too simple to actually justify a system resource.

Anyone with enough JASS knowledge to use structs can write something like that customized for his map needs on his own.


It also lacks basic functionality like attribute modifiers (linear and multipliers).

If I'd have to manually keep track of all temporary attribute modifiers, I don't see the point in using an external library in the first place.

Every custom attribute should have a base value, linear bonus and multiplier factor all seperate from each other.
Then a GetAttribute call returns (base value + linear bonus) * multiplier.
that's all can be included in the extension (I will write them all later), well, yeah, it's simple indeed, but custom exp can't be better than this, custom attribute is just addition (in the extension perhaps you can give'em name) I add it just because the library name is custom hero not custom exp, so they must have attributes. and there is only one accepted custom exp system in the spell section and imo it lacks improvements, this library only provides everything that is very basic for custom hero..
 
That's because almost every map maker wants to make their own XP system tailored to their needs. Which makes sense.
Also, there's nothing wrong with the XP system used by the game itself, so most people use that for simplification.

It makes no sense to put attributes in this when you can't dynamically apply bonuses to them. If a feature is not completely developed, don't include it.


I'd say don't go all Nestharus by writing extensions and modules for every single feature. Systems should include all the useful features right from the get go and not just be a giant WIP sandbox.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
That's because almost every map maker wants to make their own XP system tailored to their needs.
but this library is very basic so that it can "tailors to every needs", even if you want to completely create your own custom combat system. so copy pasteing this library is more reasonable than re-writing them all with less efficiency. But about custom attribute, let me think about it, maybe you are right and I will remove it..
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
I don't see why anyone would ever use this.
perhaps, but except me. xD

well, if only custom exp in the spell section is good I won't upload this, and there is three things that you can configure there.. :)
The only real functionality this system provides is the XP part
that's right but actually this library also provide the best possible xp calculation, that's the main point. and I will remove custom attribute btw..

okay, I see your point, but I will request to gy this if only you are not the only guy who is disagree about this system to bother uploaded here..

EDIT:
Okay, I have read it once more and it's indeed super simple, but that's the good part of this system, it looks simple bcs it's coded efficiently. also instead of using complicatedly inefficent GUI version better you just use the more simple, more flexible, and more efficient one. meh, why not?
anyway, I will change the name to CustomExp, you better read this
A good resource is a resource that has a clear purpose and performs only that. The less things it does, the better. This may sound silly to you, but it's great to do so because modular design allows you to debug things far more quickly.

Updated name changed to CustomXp and deleted some functions so it's now even more simple which give zwei more reason to hate this library :>
 
Last edited:
you better read this
Simplicity != Serves no purpose.

And your library serves no real purpose. It just replicates the way XP is calculated in WC3 and that's it. And there's snippets that do exactly that already without all the useless wrappers.

And seriously: RegisterVariableEvent to fire events? Since when do we do this?


EDIT:
As a custom XP system, this actually is an OK resource, even if it doesn't really do anything special. But remove the RegisterVariableEvent thing. That's not how we do custom events nowadays.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
And your library serves no real purpose. It just replicates the way XP is calculated in WC3 and that's it. And there's snippets that do exactly that already without all the useless wrappers.
it does serve, what mostly I can think about custom xp are just level up, storing and adding them, what else?
And there's snippets that do exactly that already without all the useless wrappers.
where? gimme the link, I will request to gy if so
But remove the RegisterVariableEvent thing. That's not how we do custom events nowadays.
okay, but why not using RegisterVariableEvent ? can you prove that it's "bad"? no one has ever complained about my "way" to fire the event except you :) (EDIT: actually I found this method after reading Bribe's GUI indexer, he was using the same way with mine, but the difference is that I use elegant wrapper.)
without all the useless wrappers.
really? prove that all of my wrappers are "useless"
 
Hmm... it's not my decision to decide on approval of a resource anyway. I already said this is OK as a custom XP system, though as a snippet.
okay, but why not using RegisterVariableEvent ? can you prove that it's "bad"? no one has ever complained about my "way" to fire the event except you :) (EDIT: actually I found this method after reading Bribe's GUI indexer, he was using the same way with mine, but the difference is that I use elegant wrapper.)
It's used in GUI indexer because this is the only custom event that can be accessed and used in GUI.
Your snippet uses JASS, it's not designed with the GUIer in mind.
And besides, if you want this to be a GUI compatible event, then you maybe shouldn't make the EventTrigger variable private so that they can actually *use* the event in GUI?

We don't use the variable event because it's not even needed here and adds a lot of useless overhead.
Just use a trigger evaluation as everyone else does when implementing custom events.
 
can you prove that using TriggerEvaluate is any better? if yes it is, perhaps the difference wont be more than 0.0000001 milisecond, problem for you? and it's not any overhead :> deal with it.
I don't need to prove anything, as this event is totally redundant here.

Instead of setting TriggerEvent to -1 and back to 0, you can just call TriggerEvaluate(Trigger).
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
I don't need to prove anything, as this event is totally redundant here.

Instead of setting TriggerEvent to -1 and back to 0, you can just call TriggerEvaluate(Trigger).

still, I don't have any reason to keep that event fire method :p so here is an update!

Q: Is that okay to let the current exp & level become negative?

EDIT:
wait, you can't do enumerations (adding Xp to a group of unit as example) to these custom heroes D: I will add that functionality at the next update ;)
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
as Zwiebelchen said, this is basically a simple wrapper just for the "nice" struct syntax.

Also this code generates useless TriggerEvaulations, because functions SetCustomHeroXp amd SetCustomHeroLevel are defined before the function you try to call from them(Hero.addXp and Hero.addLevel respectively)

Also currently this system is really basic, because you dont even give the oppoturnity to set up different XpTargets for different units


So far I can do everything your system can with less overhead, because I dont calculate anything, but the engine does by calling the natives on real heroes

edit: Sorry Zwiebelchen, I got your name wrong
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
I'm sorry but I don't know how you thiink about this snippet, but myself, I love this so much, I can see many uses of this library, in other word: this snippet is useful in so many cases. I think simple != useless
Also this code generates useless TriggerEvaulations, because functions SetCustomHeroXp amd SetCustomHeroLevel are defined before the function you try to call from them(Hero.addXp and Hero.addLevel respectively)
I'm sorry?

Also currently this system is really basic, because you dont even give the oppoturnity to set up different XpTargets for different units
different xp target for each unit? sounds imbalance

So far I can do everything your system can with less overhead, because I dont calculate anything, but the engine does by calling the natives on real heroes
simple calculation really doesn't hurt anything
but the engine does by calling the natives on real heroes
I dont get that part
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
ok, why would I call your fat functions, when I can just as much call SetHeroXP or AddHeroXP. The only difference is that your system works for units.

The TriggerExecution may occur when you create code like:

JASS:
function aa takes A a returns nothing
    call a.b()
endfunction

struct A
    method b takes nothing returns nothing
    endmethod
endstruct

this may under certain conditions create code like this:

JASS:
function aa takes integer a returns nothing
    set SomeGlobalHolder = a
    call TriggerExecute(someJHGeneratedTrig[someInteger])
endfunction

function A__b takes integer this returns nothing
endfunction

function JH_GENERATED_SHIT takes nothing returns nothing
    local integer a = SomeGlobalHolder
    call A__b(a)
endfunction

function TheFunctionThatIsAtTheBottom takes nothing returns nothing

//random shit
call TriggerAddAction(someJHGeneratedTrig[someInteger])

//other random shit

endfunction

you should therefor never call stuff that is defined below the place where you call it.

And yes I know it may create copy of the function at the top and call it directly, and in this case it could, but it entirely depends on the A.b's body.

edit: this is not exact code, its wrote from top of my head, since I dont have world editor on this computer, so I cant place here exact output of JH

edit: Everyone loves their own snippets, and I didnt say that every simple thing is useless, but this is kind of iffy, cause this is so general topic and as Zwieb said, if you are making a map with custom XP system, you will write your own that fills your needs the most
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
The only difference is that your system works for units.
uhm, helloo, this system is indeed intended for normal units, so SetHeroXP or AddHeroXP are obviously not working.
you should therefor never call stuff that is defined below the place where you call it.
I don't :) I just evaluating trigger, and you can add the condition anytime, what's wrong with that? TH is doing the same thing (link)
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
the difference is he only returns variables, which are no problem, because globals are placed to the top of the map, but your system tries to call function that has body under the place it is called from.

This is how the code looks when compiled:

JASS:
globals
//globals from CustomXp:
constant boolean LIBRARY_CustomXp=true
        // Initial Xp target when a custom hero is created/registered
constant integer CustomXp___INITIAL_XP_TARGET= 500
        // Constant Xp target addition when level up. Added after the new target has been factored
constant integer CustomXp___NEXT_LEVEL_CONSTANT= 0
        // Factor increment of Xp target when level up
constant real CustomXp___NEXT_LEVEL_FACTOR= 0.25
        // Furthermore, edit them with your own risk
trigger CustomXp___Trigger= CreateTrigger()
unit CustomXp___EventUnit= null
//endglobals from CustomXp
    // Generated
trigger gg_trg_Melee_Initialization= null


//JASSHelper struct globals:
constant integer si__Hero=1
integer si__Hero_F=0
integer si__Hero_I=0
integer array si__Hero_V
integer array s__Hero_XpTarget
integer array s__Hero_XpCurrent
integer array s__Hero_Level
unit array s__Hero_Unit
trigger st__Hero_addXp
trigger st__Hero_addLevel
integer f__arg_integer1
integer f__arg_this
integer f__result_integer

endglobals


//Generated method caller for Hero.addXp
function sc__Hero_addXp takes integer this,integer amount returns integer
    set f__arg_this=this
    set f__arg_integer1=amount
    call TriggerEvaluate(st__Hero_addXp)
 return f__result_integer
endfunction

//Generated method caller for Hero.addLevel
function sc__Hero_addLevel takes integer this,integer amount returns integer
       
            local unit u
       
            loop
                exitwhen amount == 0
                set s__Hero_XpTarget[this]=s__Hero_XpTarget[this] + R2I(I2R(s__Hero_XpTarget[this]) * CustomXp___NEXT_LEVEL_FACTOR) + CustomXp___NEXT_LEVEL_CONSTANT
                set s__Hero_Level[this]=s__Hero_Level[this] + 1
                set u=CustomXp___EventUnit
                set CustomXp___EventUnit=s__Hero_Unit[this]
                call TriggerEvaluate(CustomXp___Trigger)
                set CustomXp___EventUnit=u
                set amount=amount - 1
            endloop
            set u=null
           
            return s__Hero_Level[this]
endfunction

//Generated allocator of Hero
function s__Hero__allocate takes nothing returns integer
 local integer this=si__Hero_F
    if (this!=0) then
        set si__Hero_F=si__Hero_V[this]
    else
        set si__Hero_I=si__Hero_I+1
        set this=si__Hero_I
    endif
    if (this>8190) then
        return 0
    endif

    set si__Hero_V[this]=-1
 return this
endfunction

//Generated destructor of Hero
function s__Hero_deallocate takes integer this returns nothing
    if this==null then
        return
    elseif (si__Hero_V[this]!=-1) then
        return
    endif
    set si__Hero_V[this]=si__Hero_F
    set si__Hero_F=this
endfunction

//library CustomXp:

   
    function SetCustomHeroXp takes integer whichHero,integer newValue returns nothing
        call sc__Hero_addXp(whichHero,newValue + ( newValue - s__Hero_XpCurrent[whichHero] ))
    endfunction
   
    function SetCustomHeroLevel takes integer whichHero,integer newValue returns nothing
        call sc__Hero_addLevel(whichHero,newValue + ( newValue - s__Hero_Level[whichHero] ))
    endfunction
   
    function RegisterCustomHeroLevelUpEvent takes code func returns triggercondition
        return TriggerAddCondition(CustomXp___Trigger, Condition(func))
    endfunction
   
    function RemoveCustomHeroLevelUpEvent takes triggercondition tc returns nothing
        call TriggerRemoveCondition(CustomXp___Trigger, tc)
    endfunction
   
    function GetLevelingCustomHero takes nothing returns unit
        return CustomXp___EventUnit
    endfunction

       
   
        function s__Hero_create takes player p,integer id,real x,real y,real face returns integer
       
            local integer this= s__Hero__allocate()
           
            set s__Hero_Unit[this]=CreateUnit(p, id, x, y, face)
            set s__Hero_XpCurrent[this]=0
            set s__Hero_XpTarget[this]=CustomXp___INITIAL_XP_TARGET
            set s__Hero_Level[this]=1
           
            return this
        endfunction
   
        function s__Hero_add takes unit whichUnit returns integer
       
            local integer this= s__Hero__allocate()
           
            set s__Hero_Unit[this]=whichUnit
            set s__Hero_XpCurrent[this]=0
            set s__Hero_XpTarget[this]=CustomXp___INITIAL_XP_TARGET
            set s__Hero_Level[this]=1
           
            return this
        endfunction
   
        function s__Hero_remove takes integer this returns nothing
            call s__Hero_deallocate(this)
        endfunction
       
        function s__Hero_addXp takes integer this,integer amount returns integer
       
            set s__Hero_XpCurrent[this]=s__Hero_XpCurrent[this] + amount
            loop
                exitwhen s__Hero_XpCurrent[this] < s__Hero_XpTarget[this]
                set s__Hero_XpCurrent[this]=s__Hero_XpCurrent[this] - s__Hero_XpTarget[this]
                call sc__Hero_addLevel(this,1)
            endloop
           
            return s__Hero_XpCurrent[this]
        endfunction
       
        function s__Hero_addLevel takes integer this,integer amount returns integer
       
            local unit u
       
            loop
                exitwhen amount == 0
                set s__Hero_XpTarget[this]=s__Hero_XpTarget[this] + R2I(I2R(s__Hero_XpTarget[this]) * CustomXp___NEXT_LEVEL_FACTOR) + CustomXp___NEXT_LEVEL_CONSTANT
                set s__Hero_Level[this]=s__Hero_Level[this] + 1
                set u=CustomXp___EventUnit
                set CustomXp___EventUnit=s__Hero_Unit[this]
                call TriggerEvaluate(CustomXp___Trigger)
                set CustomXp___EventUnit=u
                set amount=amount - 1
            endloop
            set u=null
           
            return s__Hero_Level[this]
        endfunction

function main takes nothing returns nothing
    call SetCameraBounds(- 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), - 3328.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 3072.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 3328.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), - 3584.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM))
    call SetDayNightModels("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTerrain\\DNCLordaeronTerrain.mdl", "Environment\\DNC\\DNCLordaeron\\DNCLordaeronUnit\\DNCLordaeronUnit.mdl")
    call NewSoundEnvironment("Default")
    call SetAmbientDaySound("LordaeronSummerDay")
    call SetAmbientNightSound("LordaeronSummerNight")
    call SetMapMusic("Music", true, 0)
    call InitBlizzard()

call ExecuteFunc("jasshelper__initstructs12467755")

    call InitGlobals()
    call InitCustomTriggers()
    call RunInitializationTriggers()

endfunction

//Struct method generated initializers/callers:
function sa__Hero_addXp takes nothing returns boolean
local integer this=f__arg_this
local integer amount=f__arg_integer1
            set s__Hero_XpCurrent[this]=s__Hero_XpCurrent[this] + amount
            loop
                exitwhen s__Hero_XpCurrent[this] < s__Hero_XpTarget[this]
                set s__Hero_XpCurrent[this]=s__Hero_XpCurrent[this] - s__Hero_XpTarget[this]
                call sc__Hero_addLevel(this,1)
            endloop
set f__result_integer= s__Hero_XpCurrent[this]
   return true
endfunction
function sa__Hero_addLevel takes nothing returns boolean
local integer this=f__arg_this
local integer amount=f__arg_integer1
            local unit u
            loop
                exitwhen amount == 0
                set s__Hero_XpTarget[this]=s__Hero_XpTarget[this] + R2I(I2R(s__Hero_XpTarget[this]) * CustomXp___NEXT_LEVEL_FACTOR) + CustomXp___NEXT_LEVEL_CONSTANT
                set s__Hero_Level[this]=s__Hero_Level[this] + 1
                set u=CustomXp___EventUnit
                set CustomXp___EventUnit=s__Hero_Unit[this]
                call TriggerEvaluate(CustomXp___Trigger)
                set CustomXp___EventUnit=u
                set amount=amount - 1
            endloop
            set u=null
set f__result_integer= s__Hero_Level[this]
   return true
endfunction

function jasshelper__initstructs12467755 takes nothing returns nothing
    set st__Hero_addXp=CreateTrigger()
    call TriggerAddCondition(st__Hero_addXp,Condition( function sa__Hero_addXp))
    set st__Hero_addLevel=CreateTrigger()
    call TriggerAddCondition(st__Hero_addLevel,Condition( function sa__Hero_addLevel))


endfunction

as you may noticed, the function SetCustomHeroLevel calls sc__Hero_addLevel, which is placed on top, and function SetCustomHeroXp calls sc__Hero_addXp. and the function has a TriggerEvaluate in it, because it is defined under the body. So effectively you turned simple call to function to a heavy TriggerEvaluation
 
Top