1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. From the gates of hell, the 5th Special Effect Contest Results have emerged.
    Dismiss Notice
  4. Rubbed the right way, the genie is out of its lamp! The 12th Concept Art Contest Results have been announced.
    Dismiss Notice
  5. Race against the odds and Reforge, Don't Refund. The 14th Techtree Contest has begun!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Snippet] [Needs work] Projectile Utils

Discussion in 'Graveyard' started by Bribe, Aug 24, 2010.

  1. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Code (vJASS):

    //---------------------------------------------------------------------------
    // Projectile Utilities by Bribe, for use with Berb's Projectiles Library.
    //---------------------------------------------------------------------------
    //
    // ProjectileUtils makes launching projectiles between units a user-friendly,
    // automatic process. The main functions are listed under Users' API below.
    //
    //---------------------------------------------------------------------------
    // Thanks
    //---------------------------------------------------------------------------
    //
    // Berb for Custom Projectiles - the library that inspired me to learn vJass.
    // &
    // Vexorian for JassHelper and the dummy.mdx model.
    // &
    // Nestharus for inspiring ideas that add to the dynamics of this library.
    //
    //---------------------------------------------------------------------------
    // Requirements
    //---------------------------------------------------------------------------
    //
    // Projectile and ProjectileArt by Berb
    //   - http://www.hiveworkshop.com/forums/showthread.php?t=162121
    //
    // Table by Bribe
    //   - http://www.hiveworkshop.com/forums/showthread.php?t=188084
    //
    // xebasic by Vexorian
    //   - http://www.wc3c.net/showthread.php?t=101150
    //
    //---------------------------------------------------------------------------
    // Dummy-unit settings to produce accurate and optimal projectile simulations
    //---------------------------------------------------------------------------
    //
    // Art - Model File
    //   - Dummy.mdx
    //
    // Art - Special (unit explodes)
    //   - None
    //
    // Animation - Blend Time
    //   - 0.00
    //
    // Pitch/Roll - Maximum Rotation
    //   - 0.00
    //
    // Abilities
    //   - 'Aloc' (Locust)
    //
    //---------------------------------------------------------------------------
    // Users' API
    //---------------------------------------------------------------------------
    //
    // FireProjectile(unit, unit, real, real, real, boolean, string) -> projectile
    //
    //   unit source,
    //   :  The unit firing the projectile.
    //
    //   unit target,
    //   :  The target unit of the projectile.
    //
    //   real damage,
    //   :  The amount of damage the source deals to the target upon on-impact.
    //
    //   real speed,
    //   :  Projectile's speed.
    //
    //   real arc,
    //   :  Projectile's arc.
    //
    //   boolean homing,
    //   :  The projectile will home in on the target unit.
    //
    //   string modelpath,
    //   :  The file path of what you want the projectile to look like.
    //
    //   returns projectile,
    //   :  A new projectile each time the function is called.
    //
    //---------------------------------------------------------------------------
    //
    // FireProjectileXY(unit, real, real, real, real, string) -> projectile
    //
    //   unit source,
    //   :  Same as FireProjectile.
    //  
    //   real x, y,
    //   :  Coodinates to fire the missile towards.
    //  
    //   real speed, arc, string modelpath, returns projectile
    //   :  Same as FireProjectile.
    //
    //---------------------------------------------------------------------------
    //
    // SetProjectileVisualsById(integer, real, integer, integer, integer, integer)
    //
    //   integer unitid,
    //   :   units of this id will have projectiles with these visual settings.
    //
    //   real scale,
    //   :   adjusts the projectile's visual size. 1.0 == 100% (default).
    //
    //   integer alpha, red, green, blue,
    //   :  adjusts projectiles' vertex colors. 255 is 100% (default). 0 alpha is
    //      invisible.
    //
    //---------------------------------------------------------------------------
    //
    // SetProjectileImpactEvent(projectile, function)
    //
    //   projectile proj,
    //   :  This parameter can be easily filled using GetLastCreatedProjectile().
    //
    //   function <name>,
    //   :  The name of the function you want to call when the projectile impacts
    //      with the target unit. The function should take a projectile instance
    //      and return nothing.
    //          '''
    //          function exampleFunc takes projectile proj returns nothing
    //              call UnitDamageTarget(proj.source, proj.target, GetProjectileDamage(proj), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
    //              call BJDebugMsg(GetUnitName(proj.target) + " was hit by " + GetUnitName(proj.source) + " for " + I2S(R2I(GetProjectileDamage(proj))) + " damage!")
    //          endfunction
    //          '''
    //      That example-function would be called like this:
    //          '''
    //          call SetProjectileImpactEvent(GetLastCreatedProjectile(), exampleFunc)
    //          '''
    //      WARNING: This function overrides the normal damaging method used by
    //      the internal system. You can use this to your advantage if you want
    //      an area-of-effect attack or if you want to have your own, customized
    //      attack-type, damage type or the like.
    //
    //---------------------------------------------------------------------------
    //
    // GetLastCreatedProjectile() -> projectile
    //
    //   returns projectile,
    //   :  the last projectile-instance created by the FireProjectile function.
    //
    //---------------------------------------------------------------------------
    //
    // GetProjectileDamage(projectile) -> real
    //
    //   projectile proj,
    //   :  The projectile instance to retrieve damage from.
    //  
    //   returns real,
    //   :  the damage specified by FireProjectile, else the damage set by...
    //
    //---------------------------------------------------------------------------
    //
    // SetProjectileDamage(projectile, real)
    //
    //   projectile proj,
    //   :  The projectile instance to be assigned the value of...
    //
    //   real damage,
    //   :  Can be set for a projectile instance of any type, retrievable via
    //      GetProjectileDamage.
    //
    //---------------------------------------------------------------------------
    //
    // SetProjectileOffsetsById(integer, real, real, real, real)
    //
    //   integer unitid,
    //   :  units of this unit-type id will use these offsets for FireProjectile.
    //
    //   real distance,
    //   :  project an offset from the source unit's position to create & launch
    //      the projectile; works like PolarProjectionBJ, offset by...
    //
    //   real angle,
    //   :  value taken in radians (must be between 0.00 and 2PI (~ 6.2831853)).
    //
    //   real launchz,
    //   :  directly offsets the z-coordinate of launch and is unaffected by the
    //      given angle.
    //
    //   real impactz,
    //   :  projectiles will hit their targets from this height-offset.
    //
    //---------------------------------------------------------------------------
    library ProjectileUtils requires Projectile, ProjectileArt, Table, xebasic
       
       
    globals
        //-----------------------------------------------------------------------
        // Internal-use
        //
       
        // Configurable values
        private constant real       DEFAULT_LAUNCH_DISTANCE = 30.0   // Specify the default distance and angle to launch the projectile. These
        private constant real       DEFAULT_LAUNCH_ANGLE    = 0.00   // values offset the source x/y values and create the projectile unit there.
                                                                     // Angle must be in radians (between 0 and 2 * PI). Multiply by bj_DEGTORAD if
                                                                     // you are unsure how to calculate it.
        private constant real       DEFAULT_LAUNCH_HEIGHT   = 60.0   // Launch-height is not offset by launch-angle.
        private constant real       DEFAULT_IMPACT_HEIGHT   = 60.0   // When the projectile hits its target, it will be offset by this height.
       
        // Readability
        private constant real       PI2 = 6.2831853
       
        // Scalar variables
        private player      s_owner     = Player(15)
        private unit        s_unit      = null
        private integer     s_size      = 0
        private real        s_height    = 0.00
        private vector      s_launchVec
        private vector      s_impactVec
        private projectile  s_proj
        private Table       s_hash
       
        // Arrays
        private boolean array a_offsets
        private real    array a_distance
        private real    array a_angle
        private real    array a_launchz
        private real    array a_impactz
       
        private boolean array a_visuals
        private real    array a_scale
        private integer array a_alpha
        private integer array a_red
        private integer array a_green
        private integer array a_blue
    endglobals
       
       
    //===========================================================================
    // A user-customizable on-impact function.
    //   - Re-set via SetProjectileImpactEvent
    //
    function interface ProjectileImpactFunc takes projectile p returns nothing
       
       
    //===========================================================================
    private module m
        private static method onInit takes nothing returns nothing
            set s_hash=Table.create()
            set s_launchVec=vector.create(0., 0., 0.)
            set s_impactVec=vector.create(0., 0., 0.)
        endmethod
    endmodule
       
       
    //===========================================================================
    private struct object extends projectile
       
       
        // Instance members
        real damage=0.
        ProjectileImpactFunc func=0
       
       
        //=======================================================================
        // This transforms the struct requirement into a user-defined function.
        //
        method onFinish takes nothing returns nothing
            if (this.func != 0) then
                call this.func.evaluate(this)
            endif
        endmethod
       
       
        //=======================================================================
        // The default damaging function used by FireProjectile.
        //
        method damager takes nothing returns nothing
            if (this.damage != 0.) then
                call UnitDamageTarget(this.source, this.target, this.damage, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
            endif
        endmethod
       
       
        //=======================================================================
        // Data processing
        //
        static method operator [] takes integer unitid returns integer
            local integer i=s_hash[unitid]
            if (i==0) then
                set i=s_size+1
                set s_size=i
                set s_hash[unitid]=i
            endif
            return i
        endmethod
       
       
        //=======================================================================
        // Projectile creator...  
        //   - Creates a new projectile instance from a new projectile unit
        //   - Attaches dummy-skin
        //   - Assigns target/impact vectors
        //   - Adjusts for customized offsets
        //  
        static method create takes unit source, real x2, real y2, string modelpath returns thistype
            local integer i=s_hash[GetUnitTypeId(source)]
            local real x=GetUnitX(source)
            local real y=GetUnitY(source)
            local real angle=Atan2(y2 - y, x2 - x)
            local real tempang=DEFAULT_LAUNCH_ANGLE
            local real launchz=DEFAULT_LAUNCH_HEIGHT + GetUnitFlyHeight(source)
            local real launchd=DEFAULT_LAUNCH_DISTANCE
           
            if a_offsets[i] then
                set tempang=a_angle[i]
                set launchz=a_launchz[i]
                set launchd=a_distance[i]
                set s_height=a_impactz[i]
            else
                set s_height=DEFAULT_IMPACT_HEIGHT
            endif
           
            if (tempang != 0.) then
                set angle = angle + tempang
                if (angle > PI2) then
                    set angle = angle - PI2
                elseif (angle < 0.) then
                    set angle = angle + PI2
                endif
            endif
           
            call s_impactVec.getTerrainPoint(x2, y2)
            call s_launchVec.getTerrainPoint(x + launchd * Cos(angle), y + launchd * Sin(angle))
           
            set s_launchVec.z=s_launchVec.z + launchz
           
            set s_unit=CreateUnit(s_owner, XE_DUMMY_UNITID, s_launchVec.x, s_launchVec.y, bj_RADTODEG * angle)
           
            set s_proj=thistype.allocate(s_unit)
            set s_proj.source=source
           
            if (a_visuals[i]) then
                call SetUnitScale(s_unit, a_scale[i], 0., 0.)
                call SetUnitVertexColor(s_unit, a_red[i], a_green[i], a_blue[i], a_alpha[i])
            endif
            call SetUnitExploded(s_unit, true)
            call s_proj.setModel(modelpath)
           
            return s_proj
        endmethod
       
        implement m
       
    endstruct
       
       
       
    //***************************************************************************
    //*
    //* Users' API
    //*
    //***************************************************************************
       
       
        //=======================================================================
        function GetLastCreatedProjectile takes nothing returns projectile
            return s_proj
           
        endfunction
           
           
        //=======================================================================
        function GetProjectileDamage takes object proj returns real
            return proj.damage
           
        endfunction
           
           
        //=======================================================================
        function SetProjectileDamage takes object proj, real damage returns nothing
            set proj.damage=damage
           
        endfunction
           
           
        //=======================================================================
        function SetProjectileImpactEvent takes object proj, ProjectileImpactFunc func returns nothing
            set proj.func=func
           
        endfunction
           
           
        //=======================================================================
        // Streamlined projectile-firing utility. Passes basic arguments to the
        // object.create method and then processes the additional arguments.
        //
        function FireProjectile takes unit source, unit target, real damage, real speed, real arc, boolean homing, string modelpath returns projectile
            set object.create(source, GetUnitX(target), GetUnitY(target), modelpath).target=target
            set s_impactVec.z=s_impactVec.z + GetUnitFlyHeight(target) + s_height
           
            if (homing) and GetUnitAbilityLevel(target, 'Amov')!=0 then
                set s_proj.targetZOffset=s_height
            else
                set s_proj.activeTargetFollow=false
            endif
           
            call SetProjectileDamage(s_proj, damage)
            call SetProjectileImpactEvent(s_proj, object.damager)
            call s_proj.launch(s_launchVec, s_impactVec, speed, arc)
           
            return s_proj
        endfunction
           
           
        //=======================================================================
        // Targetless projectile-firing utility. Passes basic arguments to the
        // object.create method and then processes the additional arguments.
        //
        function FireProjectileXY takes unit source, real x, real y, real speed, real arc, string modelpath returns projectile
            call object.create(source, x, y, modelpath).launch(s_launchVec, s_impactVec, speed, arc)
           
            return s_proj
        endfunction
           
           
        //=======================================================================
        function SetProjectileVisualsById takes integer unitid, real scale, integer alpha, integer red, integer green, integer blue returns nothing
            local integer i=object[unitid]
           
            set a_scale[i]=scale
            set a_alpha[i]=alpha
            set a_red[i]=red
            set a_green[i]=green
            set a_blue[i]=blue
            set a_visuals[i]=true
        endfunction
           
           
        //=======================================================================
        function SetProjectileOffsetsById takes integer unitid, real distance, real angle, real launchz, real impactz returns nothing
            local integer i=object[unitid]
           
            if (angle <= PI2) and (angle >= 0) then
                set a_angle[i]=angle
            endif
           
            set a_distance[i]=distance
            set a_launchz[i]=launchz
            set a_impactz[i]=impactz
            set a_offsets[i]=true
        endfunction
       
       
    endlibrary
     


    Add-ons

    Code (vJASS):

    library SplashingProjectile requires ProjectileUtils
       
       
        globals
            // Configurables
            private constant integer MED_DAMAGE_FACTOR = 60 // deals 60% of normal damage
            private constant integer MIN_DAMAGE_FACTOR = 20 // deals 20% of normal damage
           
            private constant integer MED_DAMAGE_POINT = 25 // med. damage applied for units past 25% of radius
            private constant integer MIN_DAMAGE_POINT = 50 // min. damage applied for units past 50% of radius
           
            // Data storage
            private unit s_source
            private unit s_dummy
            private real s_radius
            private real s_damage
            private real array a_radius
            private string array a_fx
        endglobals
       
       
        //=======================================================================
        // Configurable damage function.
        //
        private function inflict takes real damage returns nothing
            call UnitDamageTarget(s_source, GetFilterUnit(), damage, false, false, null, null, null)
        endfunction
       
       
        //=======================================================================
        private function enum takes nothing returns boolean
           
            if GetWidgetLife(GetFilterUnit()) > 0.405 then
                if (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius * MED_DAMAGE_POINT)) then
                    call inflict(s_damage)
                   
                elseif (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius * MIN_DAMAGE_POINT)) then
                    call inflict(s_damage * MED_DAMAGE_FACTOR)
                   
                elseif (IsUnitInRange(GetFilterUnit(), s_dummy, s_radius)) then
                    call inflict(s_damage * MIN_DAMAGE_FACTOR)
                endif
            endif
           
            return false
        endfunction
       
       
        //=======================================================================
        private function splash takes projectile proj returns nothing
            set s_source=proj.source
            set s_radius=a_radius[proj]
            set s_damage=GetProjectileDamage(proj)
            set s_dummy=proj.toUnit
            call DestroyEffect(AddSpecialEffect(a_fx[proj], proj.x, proj.y))
            call GroupEnumUnitsInRange(bj_lastCreatedGroup, proj.x, proj.y, s_radius + 90.0, Filter(function enum))
        endfunction
       
       
        //=======================================================================
        function SplashingProjectile takes projectile proj, real damage, real radius, string fx returns nothing
            set a_fx[proj]=fx
            set a_radius[proj]=radius
            call SetProjectileDamage(proj, damage)
            call SetProjectileImpactEvent(proj, splash)
        endfunction
       
       
    endlibrary
     


    Example use of SplashingProjectile:

    Code (vJASS):

    library Example initializer init requires SplashingProjectile, SpellEffectEvent
       
        // Example response to fire a missile
        private function OnCast takes nothing returns boolean
           
            // Fire it
            call FireProjectileXY(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), 600.0, 0.20, "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl")
           
            // Make it a splashing projectile
            call SplashingProjectile(GetLastCreatedProjectile(), 100.0, 150.0, "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl")
           
            return false
        endfunction
       
        // Initialize
        private function init takes nothing returns nothing
            call RegisterSpellEffectEvent(Condition(function OnCast), 'A000')
        endfunction
       
    endlibrary
     

     
    Last edited: Mar 7, 2011
  2. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    private constant integer DUMMY_ID = 'n000' // Unit model should be DUMMY.mdx from Vexorian's XE.


    ProjectileArt has dummy stuff ... also if this is an add on for Berb's projectile library, why are you using vex's xe stuff over berb's? Doesn't make sense to me.

    Code (vJASS):

            private real LaunchOffsetFront  = 30.0
            private real LaunchOffsetAngle  = 0.0  // Should be between 0.0 and 2 * PI (radians)
            private real LaunchOffsetHeight = 60.0
           
            private real ImpactOffsetRear   = 30.0
            private real ImpactOffsetAngle  = 0.0  // Should be between 0.0 and 2 * PI (radians)
            private real ImpactOffsetHeight = 60.0
     


    Shouldn't these be specific to each unit/projectile?

    Code (vJASS):

        private module Init
            static method onInit takes nothing returns nothing
                set LaunchVector = vector.create(0.0, 0.0, 0.0)
                if (LaunchOffsetAngle > MAX or LaunchOffsetAngle < 0.0) then
                    set LaunchOffsetAngle = 0.0
                endif
                set ImpactVector = vector.create(0.0, 0.0, 0.0)
                if (ImpactOffsetAngle > MAX or ImpactOffsetAngle < 0.0) then
                    set ImpactOffsetAngle = 0.0
                endif
            endmethod
        endmodule
       
        private struct xyz extends array
            implement Init
        endstruct
     


    While I find the fact that you did an initializer this way rather dumb, I find the initialization code itself useless.


    I think the wrappers in this are pretty spiffy in the way they simplify using Berb's projectile lib though : D.
     
  3. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    > ProjectileArt has dummy stuff ... also if this is an add on for Berb's projectile library, why are you using vex's xe stuff over berb's? Doesn't make sense to me.

    Berb's Projectile library is meant to be used with the XE dummy. Also, ProjectileArt does not have dummy stuff but a simple Special Effect attachment utility.

    > Shouldn't these be specific to each unit/projectile?
    By adding six new parameters to the LaunchProjectile function? If you want it to be different for each, use SetProjectileLaunchOffsets/SetProjectileImpactOffsets.

    > While I find the fact that you did an initializer this way rather dumb, I find the initialization code itself useless.
    Besides the obvious lazy attitude you have, you don't know the way Berb's projectile system works. You have to input two vectors into the 'launch' method, so this saves having to re-create the vectors per-launch.


    > I think the wrappers in this are pretty spiffy in the way they simplify using Berb's projectile lib though : D.
    Indeed.
     
    Last edited: Aug 28, 2010
  4. busterkomo

    busterkomo

    Joined:
    Jun 17, 2007
    Messages:
    1,423
    Resources:
    1
    Tutorials:
    1
    Resources:
    1
    It should require xebasic if it is meant to be used with it.
    --
    ProjectileArt's functionality doesn't even come close to xefx's.
     
  5. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    sigh....

    Code (vJASS):

    ProjectileUtils[unitTypeId].launchOffsetFront =
    ProjectileUtils[unitTypeId].launchOffsetAngle =
    ProjectileUtils[unitTypeId].launchOffsetHeigh =
     


    and impacts should be based upon both the projectile type id and the unit type id... that is unless you think something like a massive Ogre should shoot and have the same impact radius as a sheep.


    And if you didn't get my initialization comment, libraries have initializers and checking the initial values of your vars seems stupid >.>. If a user inputs something that's out of whack (you have the input limits in comments next to the vars), it's their own fault.
     
  6. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    ProjectileArt doesn't require xebasic yet is almost useless without the dummy model from Vexorian's map. But I kind of eye where you are coming from and I will alter the API in the library to support optional xebasic and that cool unit-type id matching Nestharus suggested, sometime today or tomorrow. I am glad this community does not sleep.
     
  7. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Alright, the API has been updated with some great new features; many thanks to Nestharus for the cool idea to match offsets to unit-types. Each of the three functions has been updated with one additional parameter -

    function FireProjectile now takes an additional damage parameter.

    function SetProjectileLaunchOffsets/SetProjectileImpactOffsets take an integer (unit-type-id) as the first parameter.
     
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Much better! You still have one more thing... you forgot about the projectile impact offsets themselves... for example, a bullet as compared to a boulder.

    For example, a boulder impact offset + ogre impact offset = impact offset.

    But yea, much better.
     
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    FireProjectile now has another additional parameter - homing. If the missile should home in on its target or not is up to the user, though the proj.target reference will not return the correct unit if it is a non-homing missile. To make sure you always get the correct target unit, use
    set u = GetProjectileTarget(proj)
    . It safely inlines.

    SetProjectileDisplays is another new function that helps simulate an object-editor missile. This function allows you to control the size of the projectile as well as its alpha, red, green and blue visuals. Another implementation in-place thanks to Nestharus: projectiles with a larger scale will have a greater impact-offset, so larger projectiles need not traverse as much distance to hit their target.

    Optional support for GetUnitCollisionSize has been added as well. If a target-unit has a large collision size, the projectile will impact further away than a target-unit with a smaller collision size.
     
  10. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Awesome

    Good job Bribe ^>^

    I don't have time to read your code now, but I will hopefully get to reading it some time tonight ; D.
     
  11. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    There's no need for you to use another
    unit
    handle for the "victim". If you don't want the projectile to "follow" it's target but you still want reference to the target-unit then you can do:

    Code (vJASS):
    set proj.activeTargetFollow = false
    set proj.target = yourTargetUnit


    This is the block of code that handles target homing:

    Target Homing Block
    Code (vJASS):
                    if (dat.FLAG__active) and (dat.activeTargetFollow) then
                        if (dat.FLAG__followUnit) and (dat.target != null) then
                            set x = GetUnitX(dat.target)
                            set y = GetUnitY(dat.target)
                            call MoveLocation(loc, x, y)
                            set z = GetLocationZ(loc)+GetUnitFlyHeight(dat.target)
                           
                            call dat.setTargetPos(x, y, z)
                        endif
                        if (dat.FLAG__targetFollow) then
                            set vC           = dat.VECTOR__target
                            set x            = Atan2(vC.y-vB.y, vC.x-vB.x)                                      // Direction To Target (radians)
                            set y            = SquareRoot((vC.x-vB.x)*(vC.x-vB.x) + (vC.y-vB.y)*(vC.y-vB.y))    // Distance To Target
                            set z2           = vC.z-vB.z                                                        // Height Difference
                           
                            set vA.x         = dat.speed * Cos(x) * DATA__loopRef
                            set vA.y         = dat.speed * Sin(x) * DATA__loopRef
                            set vB           = dat.VECTOR__start
                            set x            = SquareRoot((vB.x-vC.x)*(vB.x-vC.x) + (vB.y-vC.y)*(vB.y-vC.y))    // Total Distance

                            set dat.VECTOR__acceleration.z  = -8*dat.arc*dat.speed*dat.speed/x                                                  
                            set vA.z                        = -dat.VECTOR__acceleration.z * y/(dat.speed*2) + dat.speed*z2/y
                           
                            set dat.VECTOR__acceleration.z  = dat.VECTOR__acceleration.z   *DATA__loopRef*DATA__loopRef
                            set vA.z                        = vA.z                         *DATA__loopRef
                        endif
                    endif


    If the
    activeTargetFollow
    member is set to false then none of this is executed, regardless of whether or not a target has been set.

    I just found a potential problem with controlling the offset when the target vector is updated automatically - the Z-coordinate of the unit is used in addition to it's fly-height, however the height offset that may have been intended in the first place will no longer be in effect, thus the projectile will home in around the feet of the unit. What you'll want to do in order to help this problem is use
    setTargetPos
    and use the method operators
    targetX
    ,
    targetY
    and
    targetZ+heightOffset
    .

    Code (vJASS):
    call yourProjectile.setTargetPos(yourProjectile.targetX, yourProjectile.targetY, yourProjectile.targetZ + heightOffset)


    The height offset of the initial launch can be handled quite easily when you're giving values to the start/finish vectors.
     
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Dern, Berb beat me to the punch.
     
  13. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    >> If the
    activeTargetFollow
    member is set to false then none of this is executed, regardless of whether or not a target has been set.

    Very good to know, thank you :)

    >> What you'll want to do in order to help this problem is use
    setTargetPos


    If Projectile had an onLoop method, this problem could easily be solved. But I've no way to hook the TimerStart call you do if someone creates a projectile without this system, so whatever timer I use is not reliable to be synced with yours and will cause bad motion.

    Projectile could use another update. I can make the changes myself if you are short on time. It would just be a matter of a user being able to sync a
    TriggerRegisterTimerExpireEvent
    with the core timer.
     
  14. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    OK, I have partially resolved the conflict. I prefer not to use
    proj.target
    for this system because I need activeTargetFollow on but I don't want it to lock in the target-unit, I want to do that myself so that it saves excessive function calls. The target should be referenced as GetProjectileTarget(proj).

    Unless Berb thinks of another way for me to implement the same thing without slowing down the system.

    I'd like to use proj.target as a reference, but to re-iterate, that would add a lot of function calls as I reference GetUnitFlyHeight, GetUnitX/Y and MoveLocation/GetLocationZ, eliminating everything the projectile library does automatically to update the target vector.
     
  15. Berb

    Berb

    Joined:
    Jan 21, 2006
    Messages:
    2,539
    Resources:
    2
    JASS:
    2
    Resources:
    2
    Perhaps I should add a member for target height-offset.
     
  16. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    It does a bit more than just fly-height offset, I have it preserve its offset angle as well as its impact distance offset which factors in collision size;

    Code (vJASS):

                    set thistype.tempUnit = dat.victim
                    set x = GetUnitX(thistype.tempUnit)
                    set y = GetUnitY(thistype.tempUnit)
                    set a = Atan2(dat.y - y, dat.x - x) + dat.offsetang
                    if (a > MAX) then
                        set a = a - MAX
                    elseif (a < 0.0) then
                        set a = a + MAX
                    endif
                    set x = x + dat.offset1 * Cos(a)
                    set y = y + dat.offset1 * Sin(a)
                    call MoveLocation(thistype.aloc, x, y)
                    call dat.setTargetPos(x, y, GetLocationZ(thistype.aloc) + GetUnitFlyHeight(thistype.tempUnit) + dat.offset2)
     
     
  17. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I added a feature so that if the damage argument passed to FireProjectile is a negative number, the target unit will be healed on impact.
     
  18. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    Wait, how is this a feature? That is by default if you deal negative damage.
     
  19. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,158
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    I don't have much experience with UnitDamageTarget, but a negative damage value passed into that will have the source unit heal the target? Do all the attacktypes and damagetypes apply, as well?
     
  20. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    Yes, everything works the same way as having actual positive damage.