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

[Snippet] Particle

JASS:
library Particle /* v2.0.2.2
*************************************************************************************
*
*   Unit for spell effects
*
*       Thanks to Vexorian for dummy.mdx
*       Thanks to Magtheridon96 for updates
*
*************************************************************************************
*
*   */uses/*
*
*       */ UnitIndexer          /*  hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*       */ GetUnitCollision     /*  hiveworkshop.com/forums/jass-resources-412/snippet-getunitcollision-180495/
*
************************************************************************************
*
*   SETTINGS
*/
globals
    constant integer PARTICLE_ID = 'h000'
endglobals
/*
************************************************************************************
*
*   function GetUnitParticle takes UnitIndex whichUnit returns Particle
*
************************************************************************************
*
*   struct Particle extends array
*
*       real x
*       real y
*       real z
*
*       real xyAngle
*       real zAngle
*
*       real scale
*
*       player owner
*
*       string model
*
*       integer red
*       integer green
*       integer blue
*       integer alpha
*
*       boolean hidden
*
*       readonly real collision
*
*       readonly unit unit
*
*       static method create takes player owner, real x, real y, real xyAngle, string model, real collision returns Particle
*       static method createEx takes player owner, real x, real y, real z, real xyAngle, real zAngle, real scale, integer red, integer green, integer blue, integer alpha, string model, real collision returns Particle
*       static method createFromUnit takes unit whichUnit returns Particle
*       method destroy takes nothing returns nothing
*
*       method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
*
************************************************************************************/
    globals
        private integer array unitParticle
    endglobals
    
    struct Particle extends array
        private static integer instanceCount = 0
        private static integer array recycler
        
        private unit particle
        private unit trueParticle
        private real scl
        private real tlt
        private integer red1
        private integer blue1
        private integer green1
        private integer alpha1
        private string mdl
        private effect mdle
        private real xp
        private real yp
        private real zp
        private real ap
        private real collision_p
        private static location zloc = Location(0,0)
        
        method operator unit takes nothing returns unit
            return particle
        endmethod
        method operator collision takes nothing returns real
            return collision_p
        endmethod
        method operator x takes nothing returns real
            return xp
        endmethod
        method operator x= takes real v returns nothing
            set xp = v
            call SetUnitX(particle, v)
        endmethod
        method operator y takes nothing returns real
            return yp
        endmethod
        method operator y= takes real v returns nothing
            set yp = v
            call SetUnitY(particle, v)
        endmethod
        method operator z takes nothing returns real
            return zp
        endmethod
        method operator z= takes real v returns nothing
            set zp = v
            call MoveLocation(zloc, xp, yp)
            call SetUnitFlyHeight(particle, v - GetLocationZ(zloc), 0)
        endmethod
        method operator xyAngle takes nothing returns real
            return ap
        endmethod
        method operator xyAngle= takes real v returns nothing
            set ap = v
            call SetUnitFacing(particle,v*57.2957795)
        endmethod
        method operator zAngle takes nothing returns real
            return tlt
        endmethod
        method operator zAngle= takes real v returns nothing
            local integer i = R2I(v*57.2957795+90.5)
            set tlt = v
            if (179 < i) then
                set i = 179
            elseif (0 > i) then
                set i = 0
            endif
            call SetUnitAnimationByIndex(particle, i)
        endmethod
        method operator hidden takes nothing returns boolean
            return IsUnitHidden(particle)
        endmethod
        method operator hidden= takes boolean v returns nothing
            call ShowUnit(particle, not v)
        endmethod
        method operator scale takes nothing returns real
            return scl
        endmethod
        method operator scale= takes real v returns nothing
            set scl = v
            call SetUnitScale(particle, v, 0, 0)
        endmethod
        method operator owner takes nothing returns player
            return GetOwningPlayer(particle)
        endmethod
        method operator owner= takes player v returns nothing
            call SetUnitOwner(particle, v, true)
        endmethod
        method operator model takes nothing returns string
            return mdl
        endmethod
        method operator model= takes string s returns nothing
            call DestroyEffect(mdle)
            if (null != s and "" != s) then
                set mdle = AddSpecialEffectTarget(s, particle, "origin")
            else
                set mdle = null
            endif
            set mdl = s
        endmethod
        method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
            set this.red1 = red
            set this.green1 = green
            set this.blue1 = blue
            set this.alpha1 = alpha
            call SetUnitVertexColor(particle, red, green, blue, alpha)
        endmethod
        method operator red takes nothing returns integer
            return red1
        endmethod
        method operator blue takes nothing returns integer
            return blue1
        endmethod
        method operator green takes nothing returns integer
            return green1
        endmethod
        method operator alpha takes nothing returns integer
            return alpha1
        endmethod
        
        method operator red= takes integer v returns nothing
            set red1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator blue= takes integer v returns nothing
            set blue1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator green= takes integer v returns nothing
            set green1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator alpha= takes integer v returns nothing
            set alpha1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        
        static method createEx takes player p, real x, real y, real z, real xyAngle, real zAngle, real scale, integer red, integer green, integer blue, integer alpha, string model, real collision returns thistype
            local thistype this
            local unit u
            
            if (0 == recycler[0]) then
                set this = instanceCount + 1
                set instanceCount = this
            else
                set this = recycler[0]
                set recycler[0] = recycler[this]
            endif
            
            if (null == trueParticle) then
                set trueParticle = CreateUnit(p, PARTICLE_ID, x, y, xyAngle * 57.2957795)
                set unitParticle[GetUnitUserData(trueParticle)] = this
            else
                call SetUnitX(trueParticle, x)
                call SetUnitY(trueParticle, y)
                call SetUnitFacing(trueParticle, xyAngle * 57.2957795)
                call SetUnitOwner(trueParticle, p, false)
            endif
            
            set xp = x
            set yp = y
            set zp = z
            set ap = xyAngle
            
            set particle = trueParticle
            
            call UnitRemoveAbility(trueParticle, 'Amov')
            
            call UnitAddAbility(trueParticle, 'Amrf')
            call UnitRemoveAbility(trueParticle, 'Amrf')
            
            call SetUnitFlyHeight(trueParticle, z, 0)
            set mdl = model
            set mdle = AddSpecialEffectTarget(model, trueParticle, "origin")
            set this.zAngle = zAngle
            call setColor(red, green, blue, alpha)
            
            set this.scale = scale
            
            set collision_p = collision
            
            return this
        endmethod
        
        static method create takes player p, real x, real y, real xyAngle, string model, real collision returns thistype
            return createEx(p, x, y, 0, xyAngle, 0, 1, 255, 255, 255, 255, model, collision)
        endmethod
        
        static method createFromUnit takes unit u returns thistype
            local thistype this
            
            if (0 == recycler[0]) then
                set this = instanceCount + 1
                set instanceCount = this
            else
                set this = recycler[0]
                set recycler[0] = recycler[this]
            endif
            
            set particle = u
            
            set xp = GetUnitX(u)
            set yp = GetUnitY(u)
            set zp = GetUnitFlyHeight(u)
            set ap = GetUnitFacing(u)*bj_DEGTORAD
            
            set collision_p = GetUnitCollision(u)
            
            set unitParticle[GetUnitUserData(u)] = this
            
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            if (particle == trueParticle) then
                set hidden = false
                
                call DestroyEffect(mdle)
                set mdle = null
                
                call SetUnitX(trueParticle, WorldBounds.maxX)
                call SetUnitY(trueParticle, WorldBounds.maxY)
            else
                set particle = null
            endif
            
            set recycler[this] = recycler[0]
            set recycler[0] = this
        endmethod
    endstruct
    
    function GetUnitParticle takes UnitIndex whichUnit returns Particle
        return unitParticle[whichUnit]
    endfunction
endlibrary
 
Last edited:
  • The unit should be accessible as readonly. It often looks very cool to attach two effects to the model. Can't do that with your system.
  • Why removing the 'Amov' ability? You can't set its facing any more if you do that.
  • This doesn't recycle the missile dummies despite the fact that I wrote a recycler for this specific model. The two systems are not at all compatible.
  • "create2" should be named "create" and "create" should be named "createEx".
  • In your single-set operators you should pass the local as a parameter to the function instead of passing the global.
  • SetUnitScale should have 0 in the last two parameters.
  • native IsUnitHidden.
  • I'd never use your zAngle function as any good projectile system does not need the angular safety.
  • This is a rip off of xefx.
 
The unit should be accessible as readonly. It often looks very cool to attach two effects to the model. Can't do that with your system.

How about I add an addEffect method instead?

Why removing the 'Amov' ability? You can't set its facing any more if you do that.

Coulda sworn removing 'Amov' did the instant turn deal ;o.

This doesn't recycle the missile dummies despite the fact that I wrote a recycler for this specific model. The two systems are not at all compatible.

I don't believe in recycling anymore ; P

"create2" should be named "create" and "create" should be named "createEx".

Yea =)

SetUnitScale should have 0 in the last two parameters

Why?

native IsUnitHidden

Oh, I was thinking of IsUnitVisible when I read IsUnitHidden, haha.

I'd never use your zAngle function as any good projectile system does not need the angular safety.

ehh, it's just easier to have that stuff in the method rather than in the code using it : )

This is a rip off of xefx.

I thoroughly disliked how vex did his dummy struct thing and it didn't some of the things I wanted.

edit
unit still turns just fine with 'Amov' removed.. but the turning still isn't instant ; |. What was the trick to it again? ^)^
 
I don't believe in recycling anymore

I don't believe that recycling causes leaks, however CreateUnit is an extremely heavy operation especially with UnitIndexer in the map which makes recycling very valuable.

Coulda sworn removing 'Amov' did the instant turn deal

What it does is remove the unit's need to turn in order to face a target point in order to cast an ability.

How about I add an addEffect method instead?

Fine by me.


Because it is faster and for no other reason. The last two parameters do nothing.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
  • The unit should be accessible as readonly. It often looks very cool to attach two effects to the model. Can't do that with your system.

No, just no. If you need to attach other effects (besides the "main" one) to the dummy, you should do so by adding a dummy item bonus ability (such as item armor bonus), which xefx does have support for (this one, unfortunately, doesn't). Assigning multiple effect attachments to a dummy ability in the Object Editor and adding that single ability is also faster than adding multiple effects at the same time.

Another point, this should seriously wait some time before removing the dummy (just don't tell me that you expect the user to account for that), because otherwise the removal (which ends up being without the "death effect" of the model) looks very, very ugly.
 
JASS:
        method operator red= takes integer v returns nothing
            set red1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator blue= takes integer v returns nothing
            set blue1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator green= takes integer v returns nothing
            set green1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator alpha= takes integer v returns nothing
            set alpha1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod

You have the opportunity to use the integer v in all those functions (which happens to have a much shorter name than all those other variables. Why don't you use it instead?
That's a slight performance gain.

Also, it seems logical to me that this library should be split into 2 seperate libraries:
Widget -> A library that contains a module for all the method operators (For reusability - these methods could be useful anywhere)
Particle -> The methods that have to do with attaching an effect to a unit.

Just my opinion.
 
I'd really prefer access to the dummy via readonly, and adding support for MissileRecycler to this so that you aren't creating a new dummy every time. If Anitarf's benchmarks on wc3c are correct then using MissileRecycler has about 1/5 the overhead of creating and removing a unit.

Readonly unit would be good for this because I would want to set the unit's z without the angular safety, and like I said be able to add multiple effects to the dummy unit.
 
Level 6
Joined
Jun 20, 2011
Messages
249
Nes i would rename create to createEx and have create2 renamed to create, since most of the cases you won't need those extra values.
Another change would be the x operator, instead of retrieving the unit's x you could store the last x it had in a var and retrieve that var (same thing for the y and z values)
Also, are you interested in particle emition? i was toying out testing the greatly useful zAngle tool and i got this working (check demo).

I know it uses T32 and it's terrible, but the important thing is the idea.
 

Attachments

  • Particle.w3x
    29.2 KB · Views: 73
Level 6
Joined
Jun 20, 2011
Messages
249
Nes please fix the following issues:
-The particle's facing should take radians instead of degrees in the create method.
-The particle should have crow form added to it once it's created (fly bugs most get unit z equations. The proper movement type should be hover, ran some tests..)
-Instead of having the x y or z operators return natives, they should return variables that were set when the particle's x y or z was changed by the user.
 
How does this look?

JASS:
library Particle /* v2.0.0.0
*************************************************************************************
*
*   Unit for spell effects
*
*       Thanks to Vexorian for dummy.mdx
*
*************************************************************************************
*
*   */uses/*
*
*       */ optional UnitIndexer /*  hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*
************************************************************************************
*
*   SETTINGS
*/
globals
    constant integer PARTICLE_ID = 'n000'
endglobals
/*
************************************************************************************
*
*   struct Particle extends array
*
*       real x
*       real y
*       real z
*
*       real xyAngle
*       real zAngle
*
*       real scale
*
*       player owner
*
*       string model
*
*       integer red
*       integer green
*       integer blue
*       integer alpha
*
*       boolean hidden
*
*       static method create takes player owner, real x, real y, real xyAngle, string model returns Particle
*       static method createEx takes player owner, real x, real y, real z, real xyAngle, real zAngle, real scale, integer red, integer green, integer blue, integer alpha, string model returns Particle
*       method destroy takes nothing returns nothing
*
*       method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
*
************************************************************************************/
    struct Particle extends array
        static if not LIBRARY_UnitIndexer then
            private static integer instanceCount = 0
            private static integer array recycler
        endif
        
        private unit particle
        private real scl
        private real tlt
        private integer red1
        private integer blue1
        private integer green1
        private integer alpha1
        private string mdl
        private effect mdle
        private real xp
        private real yp
        private real zp
        private real ap
        
        method operator x takes nothing returns real
            return xp
        endmethod
        method operator x= takes real v returns nothing
            set xp = v
            call SetUnitX(particle, v)
        endmethod
        method operator y takes nothing returns real
            return yp
        endmethod
        method operator y= takes real v returns nothing
            set yp = v
            call SetUnitY(particle, v)
        endmethod
        method operator z takes nothing returns real
            return zp
        endmethod
        method operator z= takes real v returns nothing
            set zp = v
            call SetUnitFlyHeight(particle, v, 0)
        endmethod
        method operator xyAngle takes nothing returns real
            return ap
        endmethod
        method operator xyAngle= takes real v returns nothing
            set ap = v
            call SetUnitFacing(particle,v*57.2957795)
        endmethod
        method operator zAngle takes nothing returns real
            return tlt
        endmethod
        method operator zAngle= takes real v returns nothing
            local integer i = R2I(v*57.2957795+90.5)
            set tlt = v
            if (179 < i) then
                set i = 179
            elseif (0 > i) then
                set i = 0
            endif
            call SetUnitAnimationByIndex(particle, i)
        endmethod
        method operator hidden takes nothing returns boolean
            return IsUnitHidden(particle)
        endmethod
        method operator hidden= takes boolean v returns nothing
            call ShowUnit(particle, not v)
        endmethod
        method operator scale takes nothing returns real
            return scl
        endmethod
        method operator scale= takes real v returns nothing
            set scl = v
            call SetUnitScale(particle, v, 0, 0)
        endmethod
        method operator owner takes nothing returns player
            return GetOwningPlayer(particle)
        endmethod
        method operator owner= takes player v returns nothing
            call SetUnitOwner(particle, v, true)
        endmethod
        method operator model takes nothing returns string
            return mdl
        endmethod
        method operator model= takes string s returns nothing
            call DestroyEffect(mdle)
            if (null != s and "" != s) then
                set mdle = AddSpecialEffectTarget(s, particle, "origin")
            else
                set mdle = null
            endif
            set mdl = s
        endmethod
        method setColor takes integer red, integer green, integer blue, integer alpha returns nothing
            set this.red1 = red
            set this.green1 = green
            set this.blue1 = blue
            set this.alpha1 = alpha
            call SetUnitVertexColor(particle, red, green, blue, alpha)
        endmethod
        method operator red takes nothing returns integer
            return red1
        endmethod
        method operator blue takes nothing returns integer
            return blue1
        endmethod
        method operator green takes nothing returns integer
            return green1
        endmethod
        method operator alpha takes nothing returns integer
            return alpha1
        endmethod
        
        method operator red= takes integer v returns nothing
            set red1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator blue= takes integer v returns nothing
            set blue1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator green= takes integer v returns nothing
            set green1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        method operator alpha= takes integer v returns nothing
            set alpha1 = v
            call SetUnitVertexColor(particle, red1, green1, blue1, alpha1)
        endmethod
        
        static method createEx takes player p, real x, real y, real z, real xyAngle, real zAngle, real scale, integer red, integer green, integer blue, integer alpha, string modl returns thistype
            local thistype this
            local unit u
            
            static if not LIBRARY_UnitIndexer then
                if (0 == recycler[0]) then
                    set this = instanceCount + 1
                    set instanceCount = this
                else
                    set this = recycler[0]
                    set recycler[0] = recycler[this]
                endif
            endif
            
            static if LIBRARY_UnitIndexer then
                set u = CreateUnit(p, PARTICLE_ID, x, y, xyAngle * 57.2957795)
                set this = GetUnitUserData(u)
                set particle = u
                set u = null
            else
                set particle = CreateUnit(p, PARTICLE_ID, x, y, xyAngle * 57.2957795)
            endif
            
            set xp = x
            set yp = y
            set zp = z
            set ap = xyAngle
            
            call UnitRemoveAbility(particle, 'Amov')
            
            call UnitAddAbility(particle, 'Amrf')
            call UnitRemoveAbility(particle, 'Amrf')
            
            call SetUnitFlyHeight(particle, z, 0)
            set mdl = modl
            set mdle = AddSpecialEffectTarget(modl, particle, "origin")
            set this.zAngle = zAngle
            call setColor(red, green, blue, alpha)
            
            set this.scale = scale
            
            return this
        endmethod
        
        static method create takes player p, real x, real y, real xyAngle, string modl returns thistype
            return createEx(p, x, y, 0, xyAngle, 0, 1, 255, 255, 255, 255, modl)
        endmethod
        
        method destroy takes nothing returns nothing
            set hidden = false
            
            call DestroyEffect(mdle)
            set mdle = null
            
            call RemoveUnit(particle)
            set particle = null
            
            static if not LIBRARY_UnitIndexer then
                set recycler[this] = recycler[0]
                set recycler[0] = this
            endif
        endmethod
    endstruct
endlibrary

edit

And here's the testmap with the fixed dummy. (Attached)

edit
Fixed a small bug o-o
 

Attachments

  • Particle.w3x
    21.2 KB · Views: 39
Level 16
Joined
Aug 7, 2009
Messages
1,406
There's one thing I don't understand: this supports UnitIndexer by using GetUnitUserData - AIDS is also based on that, and a lot of people are still using it (for example me: I'm lazy to rewrite this huge amount of script, especially because a lot of them is not even mine). Why don't you add an or LIBRARY_AIDS to that sad static if block so that AIDS users could also enjoy their unit indexer system?
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
Yes, there's a UnitIndexer based AIDS, I know about that - but using textmacros that implement modules and make the whole thing look like BJ's is even worse IMO. Plus, I'm not planning on implementing the requirements either - I'll stick to what I'm using right now, they work fine for me.

I even have an effect library of my own, so that was not why I did that suggestion, but I thought adding 20 extra characters wouldn't hurt your keyboard nor your fingers, and some AIDS users that are willing to use this snippet might appreciate it.
 
Luorax... this all gets inlined
JASS:
        method AIDS_addLock takes nothing returns nothing
            call UnitIndex(this).lock()
        endmethod
        
        method AIDS_removeLock takes nothing returns nothing
            call UnitIndex(this).unlock()
        endmethod
        
        private static method filter takes unit u returns boolean
            return thistype.AIDS_filter(u)
        endmethod
        
        private method index takes nothing returns nothing
            call this.AIDS_onCreate()
        endmethod
        
        private method deindex takes nothing returns nothing
            call this.AIDS_onDestroy()
        endmethod

...


I don't like how AIDS operates and I absolutely hate the AIDS API, thus I don't support AIDS.
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
I never said they wasn't inlined lol - stop spamming thos dots. Also, you just said it yourself why I won't use that crap (nor UnitIndexer)

I absolutely hate the AIDS API, thus I don't support AIDS.

And also, it's the customer's duty to do anything the disclaimer requests, and not the opposite.

(I also don't get how is the AIDS API related to GetUnitUserData - but it's fine, it must be some very high level advanced hocus pocus magic trick that such a silly boy like me would never understand)
 
no, I meant I won't support AIDS simply because I dislike the API and how it operates. The API is unrelated to GetUnitUserData, but because I don't like the API, I just refuse to support it.


So you're all for a PUI style API?
call IndexUnit(whichUnit) ?


Btw, the function API is very much like JASS, so apparently you don't like any of the JASS API either >.>. The module API is obviously easier to implement and use than the AIDS macro API, but they both still run through structs, so you can w/ reason say you don't like both. However, it can't get any easier than the module API and it can't get anymore made from scratch than the function API. If you don't like either of them, then perhaps JASS/vJASS just aren't your languages ;p.
 
Level 16
Joined
Aug 7, 2009
Messages
1,406
No, it's not that - I like modules and use them a lot, and I'm fine with the JASS API too (there are certain things that I like more than their C++ solution). It's just that I personally find it silly to implement modules via a textmacro - yes, saves time etc, but still, I don't like it. That's all.

Well, it's up to you wheter you add AIDS support or not - it was just a suggestion.
 
Since we now have Dummy, we don't really need this much.
The only other functionality it gives us is attaching a 'model', but it lacks the facing angle accuracy that Dummy gives us.

Dummy is a much better alternative, as you can easily attach a model using the AddSpecialEffectTarget native.

The only thing that would save this resource is the extension off of Dummy.
 
Top