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

[Spell] Missile Heart Curve Motion

Status
Not open for further replies.
Level 19
Joined
Oct 17, 2012
Messages
860
I am trying to replicate the movement of the projectiles in the GIF attached, but I don't know how to get started.

ShanaW2.gif
 
Last edited:
Level 19
Joined
Oct 17, 2012
Messages
860
Thank you for the suggestion. So, I played around with the system for a bit and got the following:
test.gif
At the moment, the distance between two missiles is too wide, and the heart shape is not really clearly shown.
Wurst:
package ShanaW
import Shana
import Missiles
import RegisterEvents
import ClosureTimers
import EffectUtils
import AttachmentPoints
import SoundUtils
//import HeartCurve2
@configurable constant EFFECT = "Hero\\Shana\\Spells\\W\\OrbOfFire.mdx"
@configurable constant GROUND_EFFECT = "Hero\\Shana\\Spells\\W\\NewGroundEX.mdx"
@configurable constant SOUND_EFFECT = new SoundDefinition("Hero/Shana/Spells/W/XN-W.mp3", false, true)
@compiletime function gen()
    new AbilityDefinitionArchMageBlizzard(SHAHA_W_SPELL)
    //..setAnimationNames("attack,slam")
    ..setAnimationNames("")
    ..setBuildingReduction(1, 0)
    ..setDamage(1, 0)
    ..setMaximumDamageperWave(1, 0)
    ..setNumberofShards(1, 1)
    ..setNumberofShards(1, 1)
    ..setCastRange(1, 1400)
    ..setCastingTime(1, 0)
    ..setCooldown(1, 0) // 15 is the cooldown for the original spell
    ..setLevels(1)
    ..setHeroAbility(false)
    ..setManaCost(1, 0)
    ..setEffects(1, "")
    ..setHotkeyNormal("R")
    ..setName("Dharma: Flying Flames")
    ..setTooltipNormalExtended(1, "|cffffff00Convert the flames of the Scarlet Lotus into 6 flame bombs that are fired at the target area. " +
                                  "Each flame bomb deals the target enemies within the area of effect damage "+
                                  "equal to the hero's agility value multiplied by 3 and burns them for 5 seconds. "+
                                  "|r|cffff6600(Deals 250 damage per second and slows movement speed by 25%)|r"+
                                  "|n|cff00ff00Skill type: AOE"+
                                  "|nMagic Level: LV1|nCast Range: 1400"+
                                  "|nEffect Range: 250|nCooldown: 15s|r")
function createMissile(unit owner, vec3 startPos, vec3 targetPos, real curveAngle, bool curveRight, real distance, real angle, real speed)
    // Create a missile instance
    Missiles missile
    // Set the model for the missile (replace "FX_Model_Path" with the actual path to your missile model)
    if curveRight
        if distance > 0
            let npos = startPos.toVec2().polarOffset(owner.getFacingAngle() + (angle*DEGTORAD).asAngleRadians(), distance).toVec3()
            missile = new Missiles(startPos, npos)
        else
            missile = new Missiles(startPos, targetPos)
        missile.setCurve((curveAngle*DEGTORAD).asAngleRadians())
    else
        if distance > 0
            let npos = startPos.toVec2().polarOffset(owner.getFacingAngle() - (angle*DEGTORAD).asAngleRadians(), distance).toVec3()
            missile = new Missiles(startPos, npos)
        else
            missile = new Missiles(startPos, targetPos)
        missile.setCurve((-curveAngle*DEGTORAD).asAngleRadians())
    missile.setModel("Hero/Shana/Spells/W/OrbOfFire.mdx")
    missile.owner = owner.getOwner()
    missile.collideZ = true
    // Set the speed of the missile
    missile.setSpeed(speed) // You can change the speed as needed
    // Set the duration of the missile's flight
    //missile.setDuration(5) // You can change the duration as needed
    // Set the collision size of the missile
    missile.collision = 45 // You can change the collision size as needed
    // Set an event for when the missile hits a unit
    missile.onHit() (unit u) ->
        // Code to run when missile hits a unit
        // You can customize this based on your requirements
        //print("Missile hit a unit!")
        var b = true
        if u == owner
            b = false // Return true to destroy the missile, false to keep it alive
        else
            u.addEffect("Hero/Shana/Spells/R/shanafire.mdx", AttachmentPoints.origin).destrAfter(5)
            doPeriodicallyCounted(1, 5) (CallbackCounted cb) ->
                owner.damageTarget(u, 250)
        return b
    // Set an event for when the missile reaches its destination
    var i = 0
    let target = targetPos
    let d = distance
    missile.onFinish() ->
        // Code to run when the missile reaches its destination
        // You can customize this based on your requirements
        var b = true
        i++
        if i == 1 and d > 0
            missile.deflect(target.toVec2())
            b = false
        return b
    // Launch the missile
    missile.launch()
init
    registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT) ->
        if EventData.getSpellAbilityId() == SHAHA_W_SPELL
            SOUND_EFFECT.play(1, 127)
            let caster = EventData.getTriggerUnit()
            let pos = caster.getPos().polarOffset(caster.getFacingAngle(), 100)
            let target = EventData.getSpellTargetPos()
            nullTimer() ->
                caster.setAnimation(4)
                //CreateHeartCurveProjectile(pos.x, pos.y, target.x, target.y, 10)
                //createHeartCurve(pos, target, 600)
                //createProjectile(pos, target, "Hero/Shana/Spells/W/OrbOfFire.mdx", 600, 100)
            doAfter(0.3) ->
                //createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, true, 0, 0, 900)
                //createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, false, 0, 0, 900)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, true, 600, 100, 900)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, false, 600, 100, 900)
            doAfter(0.5) ->
                //createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 10, true, 0, 0, 1100)
                //createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 10, false, 0, 0, 1100)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, true, 600, 90, 1100)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, false, 600, 90, 1100)
            doAfter(0.7) ->
                // createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 5, true, 0, 0, 1300)
                // createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 5, false, 0, 0, 1300)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, true, 600, 80, 1300)
                createMissile(caster, pos.withTerrainZ().add(0, 0, 100), target.withTerrainZ(), 20, false, 600, 80, 1300)

So, I went and chatted with AI and came up with this:
test.gif
I finally got the heart shape! Only issue with the AI solution is that the heart shape is always pointing in one direction, not the angle between start and target location.
Wurst:
package HeartCurve
import ClosureTimers
function heartCurve(real t, real leftX, real topY, real rightX, real centerX, real centerY) returns vec2
    let a = (rightX - leftX)/2
    let b = topY - centerY
    let x = leftX + a + a * Sin(t) * Sin(t) * Sin(t)
    let y = centerY + b * (13 * Cos(t) - 5 * Cos(2*t) - 2 * Cos(3*t) - Cos(4*t))
    return vec2(x, y)
public function createProjectile(vec2 start, vec2 target, string projectileArt, real speed, real max_height)
    let centerX = start.x
    let centerY = start.y + max_height/4
    let dx = target.x - start.x
    let dy = target.y - start.y
    let distance = SquareRoot(dx*dx + dy*dy)
    let angle = Atan2(dy, dx)
    let max_width = 2 * max_height
    let leftX = centerX - distance/2 - max_width/2
    let topY = start.y
    let rightX = centerX + distance/2 + max_width/2
    real t = 0
    doPeriodically(1/10) (CallbackPeriodic cb) ->
          
        if (t < 2*PI)
            let pos = heartCurve(t*(speed/distance)*2*PI, leftX, topY, rightX, centerX, centerY)
            let proj = addEffect(projectileArt, pos)
            BlzSetSpecialEffectScale(proj, 1.5)
            BlzSetSpecialEffectOrientation(proj, angle, 0, 0)
            BlzSetSpecialEffectYaw(proj, angle)
            BlzSetSpecialEffectPitch(proj, -90.0)
            t = t + 0.1
        else
            destroy cb
    //-- Optionally, you can add additional logic to deal damage to units within a certain radius of the target point

UPDATE: I consulted with AI again and came with a new heart curve function that takes an angle. The problem was that the heart shape was pointing perpendicularly to the angle between start and target location. So, I simply subtracted 90 degrees from the angle before passing it to the heart curve function. Voilà! I got the heart that I always wanted.
test.gif
Wurst:
package HeartCurve
import ClosureTimers
function heartCurve(real t, real leftX, real topY, real rightX, real centerX, real centerY, real angle) returns vec2
    let a = (rightX - leftX)/2
    let b = topY - centerY
    let x = leftX + a + a * Sin(t) * Sin(t) * Sin(t)
    let y = centerY + b * (13 * Cos(t) - 5 * Cos(2*t) - 2 * Cos(3*t) - Cos(4*t))
    let dx = Cos(angle)
    let dy = Sin(angle)
    let rotatedX = (x - centerX) * dx - (y - centerY) * dy + centerX
    let rotatedY = (x - centerX) * dy + (y - centerY) * dx + centerY
    return vec2(rotatedX, rotatedY)
public function createProjectile(vec2 start, vec2 target, string projectileArt, real speed, real max_height)
    let centerX = start.x
    let centerY = start.y + max_height/4
    let dx = target.x - start.x
    let dy = target.y - start.y
    let distance = SquareRoot(dx*dx + dy*dy)
    let angle = Atan2(dy, dx) - 90*DEGTORAD
    let max_width = 2 * max_height
    let leftX = centerX - distance/2 - max_width/2
    let topY = start.y
    let rightX = centerX + distance/2 + max_width/2
    real t = 0
    doPeriodically(1/10) (CallbackPeriodic cb) ->
           
        if (t < 2*PI)
            let pos = heartCurve(t*(speed/distance)*2*PI, leftX, topY, rightX, centerX, centerY, angle)
            let proj = addEffect(projectileArt, pos)
            BlzSetSpecialEffectScale(proj, 1.5)
            BlzSetSpecialEffectOrientation(proj, angle, 0, 0)
            BlzSetSpecialEffectYaw(proj, angle)
            BlzSetSpecialEffectPitch(proj, -90.0)
            t = t + 0.1
        else
            destroy cb
    //-- Optionally, you can add additional logic to deal damage to units within a certain radius of the target point
 
Last edited:
Status
Not open for further replies.
Top