Spell Mojo Magic

Nexor

...
Reaction score
74
Mojo Magic v1.31 - reworked


Import Difficulty: Easy

Requirements: JASS NewGen, T32 (map has it)

Allowed Targets: Enemies/Allies/Not Self/Organic

Leakless?: Check yourself

GUI/JASS?: vJASS

Lagless?: Yes

MUI?: Of course :)

Spell Description: Binds the target to the Witch Doctor using dark magic. If the linked units' distance is more than 600 range or the duration ends, the link will break and damage or heal the linked units.
The Witch Doctor recieves only the half of the damage if casted on enemy unit.
Level 1 - 60 damage/heal
Level 2 - 120 damage/heal
Level 3 - 180 damage/heal

Screenshots:

At cast:

Link breaks:


Triggers:
JASS:
scope MojoMagic

globals
  constant integer SPELL_ID  = 'A000'   //Spell ID (Ctrl+D at Object Editor)
  constant real DAMAGE_MULTI = 60.      //Damage multiplier
  constant real CASTER_MULTI = 0.5      //Multiplier of damage on caster
  constant integer DURATION  = 5       //Multiplier of duration
  constant string FX_STRING  = "Abilities\\Spells\\Undead\\DeathPact\\DeathPactTarget.mdl" //Spell SFX
  constant real RED          = 0.75  //*******************************
  constant real GREEN        = 0.2   //
  constant real BLUE         = 0.8   //These are the colors and the alpha value of the lightning
  constant real ALPHA        = 0.8   //*******************************
  constant real DISTANCE     = 600.0 //The maximal range of the link
  constant real HEIGHT       = 50.    // Height of lightning effect
  constant string LIGHT_TYPE = "SPLK" // Type of lightning (STLK is Spirit Link)
  constant boolean DMG_ON_END= true   //This will set whether the damage part shall run when the chain is broken
  private location MoveLoc   = Location (0, 0)
endglobals

function GetCoordZ takes real x, real y returns real
  call MoveLocation (MoveLoc, x, y)
  return GetLocationZ (MoveLoc)
endfunction
    
    
private function Damage takes integer level returns real
    return DAMAGE_MULTI * level
endfunction

private function Duration takes integer level returns integer
    return DURATION * level
endfunction    

//! textmacro DAMAGE_PART
    if IsUnitEnemy(.target, GetOwningPlayer(.caster)) == true then
        call UnitDamageTarget( .caster, .target, DAMAGE_MULTI, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS ) //Damage Target
        call UnitDamageTarget( .caster, .caster, DAMAGE_MULTI*CASTER_MULTI, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS ) //Damage Caster
    else
        call SetUnitState( .target, UNIT_STATE_LIFE, ( GetWidgetLife( .target) + DAMAGE_MULTI ) ) //Heal Target
        call SetUnitState( .caster, UNIT_STATE_LIFE, ( GetWidgetLife( .caster) + DAMAGE_MULTI ) ) //Heal Caster
    endif
//! endtextmacro


private struct mojo
    unit caster
    unit target
    lightning light
    real damage
    integer ticks
    private method periodic takes nothing returns boolean
            local real loc1x = GetUnitX(.caster)
            local real loc1y = GetUnitY(.caster)
            local real loc1z = GetCoordZ(loc1x,loc1y) + GetUnitFlyHeight(.caster) + HEIGHT
            local real loc2x = GetUnitX(.target)
            local real loc2y = GetUnitY(.target)
            local real loc2z = GetCoordZ(loc2x,loc2y) + GetUnitFlyHeight(.target) + HEIGHT
            local real disx = loc2x - loc1x
            local real disy = loc2y - loc1y
            
            local real distance = disx * disx + disy * disy
            
            call MoveLightningEx( .light, true, loc1x, loc1y, loc1z, loc2x, loc2y, loc2z)
            
            set .ticks = .ticks - 1
            
            if .ticks == 0 and DMG_ON_END then
                //! runtextmacro DAMAGE_PART()
                    
                call DestroyEffect( AddSpecialEffect( FX_STRING, loc1x, loc1y))
                call DestroyEffect( AddSpecialEffect( FX_STRING, loc2x, loc2y))
                call DestroyLightning(.light)
                
                return true
            endif
            
            if distance >= DISTANCE*DISTANCE or GetWidgetLife(.target) <.405 or GetWidgetLife(.caster) <.405 then
                
                //! runtextmacro DAMAGE_PART()
                call DestroyEffect( AddSpecialEffect( FX_STRING, loc1x, loc1y))
                call DestroyEffect( AddSpecialEffect( FX_STRING, loc2x, loc2y))
                call DestroyLightning(.light)
                
                return true
            endif
            
            return false
    endmethod
    implement T32
endstruct

private function Trig_Mojo_Magic_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

function Trig_Mojo_Magic_Actions takes nothing returns nothing
    local mojo d = mojo.create()
    local unit caster = GetTriggerUnit()
    local unit target = GetSpellTargetUnit()
    local real loc1x = GetUnitX(caster)
    local real loc1y = GetUnitY(caster)
    local real loc1z = GetCoordZ(loc1x,loc1y) + GetUnitFlyHeight(d.caster) + HEIGHT
    local real loc2x = GetUnitX(target)
    local real loc2y = GetUnitY(target)
    local real loc2z = GetCoordZ(loc2x,loc2y) + GetUnitFlyHeight(d.target) + HEIGHT
    

    set d.light =  AddLightningEx( LIGHT_TYPE, true, loc1x, loc1y, loc1z, loc2x, loc2y, loc2z ) //Set the type of the lightning (in this case, it's Spirit Link)
    call SetLightningColor( d.light, RED, GREEN, BLUE, ALPHA )                                  //Set the color of the lightning
    set d.caster = caster
    set d.target = target
    set d.damage = Damage( GetUnitAbilityLevel(d.caster, SPELL_ID) )
    set d.ticks  = R2I(Duration( GetUnitAbilityLevel(d.caster, SPELL_ID) ) / T32_PERIOD)

    call d.startPeriodic()
    set caster = null
    set target = null
endfunction

//===========================================================================
function InitTrig_Mojo_Magic takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_Mojo_Magic_Conditions ) )
    call TriggerAddAction( t, function Trig_Mojo_Magic_Actions )
    set t = null
endfunction

endscope


I made this spell for fun :)
It can be used to heal and damage either, but it will take effect on the caster too.
Enjoy!
 

Attachments

  • Mojo Magic v1.31.w3x
    51 KB · Views: 258

Vestras

Retired
Reaction score
248
The screenies are so small :(

1)
JASS:
    call SetHandleHandle(tim, "Unit1", caster)
    call SetHandleHandle(tim, "Unit2", target) 
    call SetHandleHandle(tim, "Light", light)


Really slow, use some struct attachment system instead. (vJASS)

2) Use coordinates instead of locations.

3)
JASS:
    call AddLightningLoc( "SPLK", loc1, loc2 ) //Set the type of the lightning (in this case, it's Spirit Link)
    set light =  bj_lastCreatedLightning


Just do
JASS:
set light = AddLightningLoc( "SPLK", loc1, loc2 ) //Set the type of the lightning (in this case, it's Spirit Link)


4)
JASS:
or IsUnitAliveBJ(target) == false or IsUnitAliveBJ(caster) == false then


Ufh... BJs... Use
JASS:
GetWidgetLife(caster) <.405

instead.

5)
JASS:
call SetLightningColorBJ( light, 0.75, 0.20, 0.75, 0.80 ) //Set the color of the lightning


BJ... eew, replace with native plox.

That's all, I think...
 

Flare

Stops copies me!
Reaction score
662
Really slow, use some struct attachment system instead. (vJASS)
And are you ever going to see the difference in speed? Maybe a TIIIIIINY fraction of a second of a difference, but who the f*ck is going to notice that?

And a configuration header would be nice - it may be a fairly short spell code-wise, but it'll make the spell much more usable by others (since they don't have to search through the code to find what to change) - if you're using vJASS
JASS:
//Don't forget to add the other stuff as well, like the lightning colours
globals
  constant integer SPELL_ID = 'a000'
  constant integer DAMAGE_MULTI = 60 //This * Abil Level = Damage dealt
  constant string FX_STRING = "insert\\fx\\string\\here"
  constant real TIMER_INT = 0.03 //0.01 is asking for lag, specially on slower computers
endglobals

otherwise
JASS:
constant function SpellId takes nothing returns integer
  return 'A000'
endfunction

constant function GetDamage takes integer level returns real
   return level*60.
endfunction

constant function FxString takes nothing returns string
  return "someParticularFxString"
endfunction

constant function TimerInt takes nothing returns real
  return 0.03
endfunction
 

Nexor

...
Reaction score
74
Thanks for replies, I will change what I can

The periodic trigger is 0.01 because at 0.03 the lightning moved too slow
and it sets only a real and 2 locations every 0.01 seconds.

I will make the locations to reals, and for the globals thing, do I have to insert it to the top of the trigger? and use their names, like SPELL_ID ?
 

Flare

Stops copies me!
Reaction score
662
The periodic trigger is 0.01 because at 0.03 the lightning moved too slow
0.03 is fine for moving lightning effects (my TimedLightning system runs on a 0.03125s timer and it's just fine)

and for the globals thing, do I have to insert it to the top of the trigger?
They should be above the rest of the code

and use their names, like SPELL_ID ?
Doesn't have to be SPELL_ID, you could name it anything you like as long as it's understandable

Just replace all instances of 'A000' with INSERT_VARIABLE_HERE e.g.
JASS:
globals
  constant integer SID = 'A000'
endglobals

function CondFunc takes notthing returns boolean
  return GetSpellAbilityId () == SID
endfunction


And your lightning ID also needs to be configurable (not everyone wants Spirit Link :p)
 

Nexor

...
Reaction score
74
I made some changes, and for the attachment system, I don't know the other systems, I'm just learning JASS.

Other: I made the changes but my Wc3 crashes on opening, what can it be?
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
You are not nulling your handles at the end of your functions (units, timer, and lightning).

In order for it to be leakless, you would need those to be nulled. You also need to FlushHandleLocals from the timer.

You may want to put vJASS instead of JASS, since you are using a global block, which is only available to those who have Newgen.

One last minor thing, the formatting for your if-thens looks bad when you don't have it indented, it makes it a lot harder to read.
 

Nexor

...
Reaction score
74
Yes I'm using NewGen and I made the changes (grimorie off) but can't test my own map :S

Please write the triggers of the flush thing and the others (I'm new to thehelper and new to JASS, this is my first finished spell using handles)
 

quraji

zap
Reaction score
144
Really slow, use some struct attachment system instead.*

2) Use coordinates instead of locations.**

BJ... eew, replace with native plox.***

*quraji facepalms Vestras. I really want to start a mailing list of anyone who says this, and mail them anthrax :p Please, read:
Non-Gamecache attachment offers a *slight* speed increase. Conversely, Gamecache is *slightly* slower.
"Really slow" applies to neither of these.

**quraji slaps Vestras. As long as you don't misuse locations (read: cause leaks), or constantly convert back and forth between locs and coords, they're fine. Besides...does this code even use locs?

***quraji backhands Vestras. Yes BJs are often wasteful functions. But, they often provide the benefit of readability. One extra function call isn't going to break a code. If it makes a code unreadable to inline a BJ, then I'd rather use the BJ. That said, there are some very stupid BJs people should take an extra second to inline. I'm tired of seeing "Ew BJ gross", "omg a BJ you nub", "your code sucks you have a BJ", etc.


*deep breath* Okay, I'm good now :D

Now, to force this post back on topic. Sounds like a neat spell, but you have to break the line to get the effect? I'll try it and tell you what I think :thup:
 

Romek

Super Moderator
Reaction score
963
quraji:
But a lot of the BJ's he does use are the shitty ones. Which is just an extra function call.

JASS:
  constant integer DAMAGE_MULTI = 60 //This * Abil Level = Damage dealt


I disagree with that (and Flare).
That should be a function.
So you can do a full formula such as "level*10+700" or anything else really.
 

Flare

Stops copies me!
Reaction score
662
That should be a function.
So you can do a full formula such as "level*10+700" or anything else really.

You don't need a function for a formula >_<

JASS:
globals
constant real BASE = 100
constant real MULTI = 60
endglobals

function bla takes nothing returns nothing
  local real dmg = BASE + GetUnitAbilityLevel (whichUnit, SID) * MULTI
...
endfunction


While a function would allow alot more flexibility, it isn't an absolute essential and if someone needs that flexibility, they can modify it themselves or ask Nexor to do it if they don't know how - for the most part, ability stats are associated with the ability's level so it's fine the way it is
 

Romek

Super Moderator
Reaction score
963
You don't need a function for a formula >_<

JASS:
constant real BASE = 100
constant real MULTI = 60

function bla takes nothing returns nothing
  local real dmg = BASE + GetUnitAbilityLevel (whichUnit, SID) * MULTI
...
endfunction

What if I want (level*100) - (Pow(level, 3) + (level * 50) :p (Ignore the fact that this equation sucks)

It's easier to use a function than loads of globals anyway.
 

Nexor

...
Reaction score
74
and without the globals I can open my map... :S

It's more work with the globals to write but the users don't have to know JASS well to change the numbers in the triggers.
 

Flare

Stops copies me!
Reaction score
662
What if I want (level*100) - (Pow(level, 2)
Pow is lame, X * X instead ^_^

It's easier to use a function than loads of globals anyway.
That's opinion - I personally find functions easier to deal with (since I like the flexibility) but others don't, or they may just prefer globals (I believe Tinki3 uses/used BASE + AbilLevel * MULTIPLIER for most of his spells)

Anyway, if they don't like how the author does things, they can deal with it, or just ask nicely for the author to modify it (or do it themselves if they know how) - there's no law saying that anyone must use a function (or globals, for that matter) for calculating a spell's attributes and ,as such, they aren't obliged to, unless they don't mind and/or want to do it anyway

Anyway, enough arguing about the merits of functions and globals for formulas - it's Nexor's spell, and he is the one who is supposed to be making that decision

and without the globals I can open my map... :S
Well, you've got bigger problems there - you said you were using NewGen right? If so, global declarations shouldn't be causing you any problems
 

Romek

Super Moderator
Reaction score
963
and without the globals I can open my map... :S

It's more work with the globals to write but the users don't have to know JASS well to change the numbers in the triggers.

They won't need JASS knowledge for a basic equation they would have to do with globals. Something like level*10.

But if they know how to do basic maths (Or use math functions), then they would be able to.
 

Nexor

...
Reaction score
74
the 90% of the people don't like/know or don't like --> don't know maths really good, this global block is for them :p

I don't know why my map won't open correctly, the syntax check didn't say any syntax errors, but it says the handle things are undeclared functions :S
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top