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

[Spell] New model

Status
Not open for further replies.

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Hi!

I am creating a passive ability (evolve) that changes the model of the learning hero forever. The spell will have 3 levels, level 1 will just improve the hero by other means than model, level 2 will change the model and level 3 will change the model one more time (to a different model.).

A bit more detailed:

Level 1 hero (Dalaran Reject)
Learns Evolve @ level 6 (more strength, MS and health (how to add health, btw?))
Learns Evolve @ level 11 (new model, "Sludge Flinger".)
Learns Evolve @ level 16 (new model, "Sludge Monstrosity".)

I figured how to add the strength and MS, but not how to make the spell change models, and how to make it recognise what level of the spell it is being learned.

Hope someone can help me! :)
 
Level 16
Joined
May 1, 2008
Messages
1,605
I think, I would do the replace stuff like this:

1) I would create these 3 units (Normal > next > last).
2) Now I would store in variables, the current exp, the stats and what ever is needed and then a trigger, which replace the current unit with the new one. Now add all those things you stored in the variables you the new unit and tada > new unit

> For the add health - you can add to a unit the health ability from the "Health Book Item" (I don't know the correct name but you should find this)
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Double edit: FIxed the MS after all, thanks for your aid regarding replacing.
 
Level 16
Joined
May 1, 2008
Messages
1,605
Well first I want give you an example how to replace:

JASS:
function ReplaceActions takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local real f = GetUnitFacing(u)
    local real x = GetUnitX(u)
    local real y = GetUnitY(u)
    local unit uu
    local integer str = GetHeroStr(u,false)
    local integer agi = GetHeroAgi(u,false)
    local integer int = GetHeroInt(u,false)
    local integer exp = GetHeroXP(u)
    
    if GetUnitTypeId(GetTriggerUnit()) == 'A000' and GetUnitLevel(GetTriggerUnit()) == 11 then // Id of the first unit
        call RemoveUnit(u) // I know it leaks but it's a hero - expl. doesn't work
        set uu = CreateUnit(GetOwningPlayer(GetTriggerUnit()),'A001',x,y,f) // the new unit
        call AddHeroXP(uu,exp,true)
        call SetHeroStr(uu,str,true)
        call SetHeroAgi(uu,agi,true)
        call SetHeroInt(uu,int,true)
    elseif GetUnitTypeId(GetTriggerUnit()) == 'A001' and GetUnitLevel(GetTriggerUnit()) == 16 then // Id of the new unit
        call RemoveUnit(u) // I know it leaks but it's a hero - expl. doesn't work
        set uu = CreateUnit(GetOwningPlayer(GetTriggerUnit()),'A002',x,y,f) // the last unit
        call AddHeroXP(uu,exp,true)
        call SetHeroStr(uu,str,true)
        call SetHeroAgi(uu,agi,true)
        call SetHeroInt(uu,int,true)
    endif
    set u = null
    set uu = null
endfunction

function InitTrig_Replace takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i = 0
    
    loop
        exitwhen i == 15
        call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_HERO_LEVEL,null)
        set i = i + 1
    endloop
    call TriggerAddAction(t,function ReplaceActions)
    set t = null
endfunction

Hint: Well I set the store values directly, you can init them first and only set them when the current level is reached - I don't know if there is a good increase of performance.

Well about the movementspeed - directly add a permanent bonus is easy for the other you need a simple MUI spell - gimmi some seconds and I give it to you ^^
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Wow, that seems advanced. I don't even know what that is. Is it custom script? or, *shrugs*, Jass?
 
Level 16
Joined
May 1, 2008
Messages
1,605
About the MS increase over time and reset if you default back again:

JASS:
library MSIncrease initializer init requires TimerUtils

    globals
        private constant integer SPELL_ID = 'A001' // the id of the spell
        
        private constant string SFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" // A possible effect each second for ms increase
        
        private constant real MS_INCREASE_DUR = 1. // So every 1 second the ms is increased
        private constant real MS_INCREASE = 5. // How much ms is given each second
        private constant real S_DURATION = 5. // the duration of the whole spell
        private constant real L_TIME = 0.04 // the loop time - don't change this -
    endglobals
    
    private struct MSIncrease
        unit    caster  = null
        real    c       = 0.
        real    t       = 0.
        real    ms      = 0.
        
        private static method Loop takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData(t)
            
            set .c = .c + L_TIME
            set .t = .t + L_TIME
            
            if .c >= MS_INCREASE_DUR then
                set .c = 0
                call SetUnitMoveSpeed(.caster,(GetUnitMoveSpeed(.caster) + MS_INCREASE))
                call DestroyEffect(AddSpecialEffect(SFX,GetUnitX(.caster),GetUnitY(.caster)))
            endif
            if .t >= S_DURATION then
                call SetUnitMoveSpeed(.caster,.ms)
                call .destroy()
                call ReleaseTimer(t)
            endif
            set t = null
        endmethod
       
        static method create takes unit caster returns thistype
            local thistype this = .allocate()
            local timer t = NewTimer()
            
            set .caster = caster
            set .ms = GetUnitMoveSpeed(.caster)
            call SetTimerData(t,this)
            call TimerStart(t,L_TIME,true,function MSIncrease.Loop)
            set t = null
            return this
        endmethod
    endstruct
    
    private function cast takes nothing returns boolean
        if GetSpellAbilityId() == SPELL_ID then
            call MSIncrease.create(GetTriggerUnit())
        endif
        return false
    endfunction

    private function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
                
        loop
            exitwhen i == bj_MAX_PLAYER_SLOTS
            call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
            set i = i + 1
        endloop
        call TriggerAddCondition(t,Condition(function cast))

        call Preload(SFX)
        set t = null
    endfunction
endlibrary

JASS:
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//*  To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//*  To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass)   More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//*  * Attaching
//*  * Recycling (with double-free protection)
//*
//* set t=NewTimer()      : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t)       : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2)     : Attach value 2 to timer
//* GetTimerData(t)       : Get the timer's value.
//*                         You can assume a timer's value is 0
//*                         after NewTimer.
//*
//* Multi-flavor:
//*    Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************

//================================================================
    globals
        //How to tweak timer utils:
        // USE_HASH_TABLE = true  (new blue)
        //  * SAFEST
        //  * SLOWEST (though hash tables are kind of fast)
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true  (orange)
        //  * kinda safe (except there is a limit in the number of timers)
        //  * ALMOST FAST
        //
        // USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
        //  * THE FASTEST (though is only  faster than the previous method
        //                  after using the optimizer on the map)
        //  * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
        //                     work)
        //
        private constant boolean USE_HASH_TABLE      = true
        private constant boolean USE_FLEXIBLE_OFFSET = false

        private constant integer OFFSET     = 0x100000
        private          integer VOFFSET    = OFFSET
              
        //Timers to preload at map init:
        private constant integer QUANTITY   = 256
        
        //Changing this  to something big will allow you to keep recycling
        // timers even when there are already AN INCREDIBLE AMOUNT of timers in
        // the stack. But it will make things far slower so that's probably a bad idea...
        private constant integer ARRAY_SIZE = 8190

    endglobals

    //==================================================================================================
    globals
        private integer array data[ARRAY_SIZE]
        private hashtable     ht
    endglobals

    //It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
    function SetTimerData takes timer t, integer value returns nothing
        static if(USE_HASH_TABLE) then
            // new blue
            call SaveInteger(ht,0,GetHandleId(t), value)
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-VOFFSET]=value
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            set data[GetHandleId(t)-OFFSET]=value
        endif        
    endfunction

    function GetTimerData takes timer t returns integer
        static if(USE_HASH_TABLE) then
            // new blue
            return LoadInteger(ht,0,GetHandleId(t) )
            
        elseif (USE_FLEXIBLE_OFFSET) then
            // orange
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-VOFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-VOFFSET]
        else
            // new red
            static if (DEBUG_MODE) then
                if(GetHandleId(t)-OFFSET<0) then
                    call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
                endif
            endif
            return data[GetHandleId(t)-OFFSET]
        endif        
    endfunction

    //==========================================================================================
    globals
        private timer array tT[ARRAY_SIZE]
        private integer tN = 0
        private constant integer HELD=0x28829022
        //use a totally random number here, the more improbable someone uses it, the better.
    endglobals

    //==========================================================================================
    function NewTimer takes nothing returns timer
        if (tN==0) then
            //If this happens then the QUANTITY rule has already been broken, try to fix the
            // issue, else fail.
            debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
            static if( not USE_HASH_TABLE) then
                debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
                set tT[0]=CreateTimer()
                static if( USE_FLEXIBLE_OFFSET) then
                    if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
                        //all right, couldn't fix it
                        call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                        return null
                    endif
                else
                    if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
                        //all right, couldn't fix it
                        call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
                        return null
                    endif
                endif
            endif
        else
            set tN=tN-1
        endif
        call SetTimerData(tT[tN],0)
     return tT[tN]
    endfunction

    //==========================================================================================
    function ReleaseTimer takes timer t returns nothing
        if(t==null) then
            debug call BJDebugMsg("Warning: attempt to release a null timer")
            return
        endif
        if (tN==ARRAY_SIZE) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")

            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            call PauseTimer(t)
            if(GetTimerData(t)==HELD) then
                debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
                return
            endif
            call SetTimerData(t,HELD)
            set tT[tN]=t
            set tN=tN+1
        endif    
    endfunction

    private function init takes nothing returns nothing
     local integer i=0
     local integer o=-1
     local boolean oops = false
     
        static if( USE_HASH_TABLE ) then
            set ht = InitHashtable()
            loop
                exitwhen(i==QUANTITY)
                set tT[i]=CreateTimer()
                call SetTimerData(tT[i], HELD)
                set i=i+1
            endloop
            set tN = QUANTITY
        else
            loop
                set i=0
                loop
                    exitwhen (i==QUANTITY)
                    set tT[i] = CreateTimer()
                    if(i==0) then
                        set VOFFSET = GetHandleId(tT[i])
                        static if(USE_FLEXIBLE_OFFSET) then
                            set o=VOFFSET
                        else
                            set o=OFFSET
                        endif
                    endif
                    if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
                        exitwhen true
                    endif
                    if (GetHandleId(tT[i])-o>=0)  then
                        set i=i+1
                    endif
                endloop
                set tN = i
                exitwhen(tN == QUANTITY)
                set oops = true
                exitwhen not USE_FLEXIBLE_OFFSET
                debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")               
            endloop
            
            if(oops) then
                static if ( USE_FLEXIBLE_OFFSET) then
                    debug call BJDebugMsg("The problem has been fixed.")
                    //If this message doesn't appear then there is so much
                    //handle id fragmentation that it was impossible to preload
                    //so many timers and the thread crashed! Therefore this
                    //debug message is useful.
                elseif(DEBUG_MODE) then
                    call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
                    call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
                    call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
                    call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
                endif
            endif
        endif

    endfunction

endlibrary

Well, yes it's jass and the second one is vJass - better then GUI =)
( Well I have to say, that I don't know anymore how to create a GUI + MUI trigger - I only learn it a little because I directly go to jass ^^ )

Greetings and Peace
Dr. Boom
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Holy mother of crap, that's a wall of text! :|

What is the advantage of using that contra using gui? I used that, and got it to work in 3 trigger lines (actions), but I am not sure if it leaks or not.
 
Level 16
Joined
May 1, 2008
Messages
1,605
Well, Jass/vJass is faster, better performance, free from shit bj's, you need no GUI variables, you can create everything in one trigger (spells), you have much more function available, Jass is better for configuration,
you can write jass faster as create all the action with mouseclick in GUI and so on - and so on ...^^. More important, you can use extra libraries for jass, so more function are available, like the TimerUtils:
It gives such function like "NewTimer()","SetTimerData(t,this)","GetTimerData(t)","ReleaseTimer(t)" which aren't available in the normal editor.

Well, if you want create a spell, which have a loop time, you need always 2 GUI triggers to make it MUI.
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Okay, but how do I even apply that? Is it done in another client than worldedit? I'm talking about the replace thingy now. And what ... lines, should I edit with my own data to make it fit the scenario?
 
but you need a base model with the right animations but is invisible (so that only the attachments would make it visible), else it wont move...

and its pretty tedious to do... as apart from the base model that has the animations, you would also need to separate each part of the supposed to be appearance into several parts...

which is totally unnecessary (and would take a larger files size I guess) as you could just always create a new unit on the OE and make it use a diff model...
 
Hint: If you want to use vJass or Jass, go learn Java and C++.
Have fun with C++, though. It's a very useful language, but extremely difficult to learn.
You can create entire games with C++.
Java is just.. well.. Java.
All computer languages (generally) use the same style.
The code is used for everything you need, and is raw.
GUI is the only thing I know, but I kinda wish I knew these languages.
With GUI you gotta create new triggers.. name them for organization.. create new events.. assign the events specific functions.. select those functions in a huge ass list.. etc etc etc.
Where as with languages like that it's easier and quicker to get stuff done.
I know this only because I know some basic HTML and I tried to use the dreamweaver program.
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
I suppose you are right. But what other use than WCIII does Jass have? Is it a general programming language? I thought it was made by blizzard:eek:
 

Esa

Esa

Level 4
Joined
Nov 10, 2010
Messages
79
Ah. Well not really, I'm only interested (for the time being) in learning how to create better wcIII maps. :)
 
Status
Not open for further replies.
Top