/* 4 Packages included.. Projectile superclass TimedBlasts (main system) Kamehameha (2d example of TimedBlasts System) AngryKame (3d example of TimedBlasts System

TimedBlasts attempts to give a system to create various "timed blasts" that can be customized to create tons of different spells that follow the general layout

User begins channeling animation, creating the projectile near them.. after a certain size is reached, the projectile is launched to the target point inflicting damage.

If two "clashable" blasts hit each other, the two casters will be in a struggle until one runs out of mana, and the winning blast will change target to its opposing caster unit.

visual example of what this code attempts to achieve in the case of a 3d clashable blast https://www.youtube.com/watch?v=M7i5Z3C5ZOk *note clashable and 3d hasn't been tested/made yet

    protected real impactAoe - size of aoe for damage to be done
    protected real damage - damage to be done
    protected real endScale - final scale of the blast before launching
    protected boolean trailOn - creates a trail following the blast as it flies to the target 
    protected boolean dustOn - creates a dust effect around the caster as he fires
    protected boolean pulseOn - makes the missile pulsate as it flies to the targeted area
    protected boolean launchOn - creates a launch effect as the missile is launched
    protected boolean dropOn - if the missile is fired from an aerial position, the missile is lowered to the targetted area
    protected boolean flightOn - if the missile will be fired from an aerial position
    protected string trailSFX - specify what SFX will be used for the trail
    protected string pulseSFX - specify what SFX will be used for the pulse
protected string launchSFX - " "
protected string finalSFX - " "
protected string dustSFX - " "
protected real trailSize - size of trail created
protected real pulseSize - size of pulses
protected real launchSize - size of launch
protected colorA missileCol - color of missile
protected colorA trailCol = color of trail
protected colorA pulseCol = color of pulse
protected real speed - speed of missile during travel
protected int channelIndex - animation index to channel
protected int launchIndex - animation index to launch
protected real offsetDistance = 50 - small offset from caster
protected real realDropSpeed = -15 if there is a drop, reasonable dropspeed, can be changed
private real timeSinceLastTrail = 0 - used to create trail
private real timeSinceLastPulse = 0 - used to create pulses
private real timeSinceLastChannel = 0 - uses to create channel
protected real timeDistance - how long trails and pulses should last, depends on speed, and missile sizes, should be configured to each spell accordingly
protected sound throwSound = generic missile size
protected real lowerRate = 150. speed that a missile will lower if it is a 3d missile
protected real destroyExplode = 1.5 how long the destruction sfx will be played
protected boolean clashing - setting if the missile is ready to clash
protected boolean clashable - saves true value for clashable until channeling is complete 
protected boolean clashed - if a missile has clashed, prevents further clashs
protected boolean channeling = if a missile is still channeling, it cannot clash yet

*/

package Projectile import public Entity

public class Projectile extends FxEntity // Angles protected angle xyAngle private vec3 startpos

private boolean timed = false
private boolean ranged = false

// Moving Speed
private real speed = 0
private real acc = 1.0
protected real dist = 0
private real maxDist = 0
protected real lifespan = 0
protected real dropSpeed = 0



construct( vec3 pos, real radius, player owner, angle xyAngle, string fxpath )
    super(pos, radius, owner, xyAngle, fxpath)
    startpos = pos
    setXYAngle(xyAngle)
    active = false

function setRanged(real maxDistance)
    this.maxDist = maxDistance*maxDistance
    ranged = true

function setTimed(real lifespan)
    this.lifespan = lifespan
    timed = true

function setSpeed(real speed)
    this.speed = speed
    //angle.direction(real distance)
    setVel(vec3(xyAngle.cos()*speed, xyAngle.sin()*speed, dropSpeed))

function setSpeed(real speed, real drop)
    this.speed = speed
    //angle.direction(real distance)
    setVel(vec3(xyAngle.cos()*speed, xyAngle.sin()*speed, drop))

function setAcc(real factor)
    this.acc = factor

function getSpeed() returns real
    return speed

function setXYAngle(angle xyA)
    this.xyAngle = xyA
    fx.setXYAngle(xyA)

function setZAngle(angle zA)
    fx.setZAngle(zA)

override function update()
    vel *= acc
    pos += vel
    fixPos()
    if timed
        lifespan -= ANIMATION_PERIOD
        if lifespan <= 0
            done = true
            return
    if ranged
        if startpos.distToVecSquared(pos) > maxDist
            done = true

package TimedBlasts import Projectile import LinkedListModule import Fx import Constants import ClosureTimers import Terrain import LinkedList

public class TimedBlasts extends Projectile use LinkedListModule //Editable values protected real impactAoe protected real damage protected real endScale protected boolean trailOn = false protected boolean dustOn = false protected boolean pulseOn = false protected boolean launchOn = false protected boolean dropOn = false protected boolean flightOn = true protected string trailSFX protected string pulseSFX protected string launchSFX protected string finalSFX protected string dustSFX = dustWave protected real trailSize protected real pulseSize protected real launchSize protected colorA missileCol = colorA(255,255,255,255) protected colorA trailCol = colorA(255,255,255,255) protected colorA pulseCol = colorA(255,255,255,255) protected real speed protected int channelIndex protected int launchIndex protected real offsetDistance = 50 protected real realDropSpeed = -15 private real timeSinceLastTrail = 0 private real timeSinceLastPulse = 0 private real timeSinceLastChannel = 0 protected real timeDistance = 1 protected sound throwSound = gg_snd_kamehameha_fire protected real lowerRate = 150. protected real destroyExplode = 1.5 protected boolean clashing = false protected boolean clashable = false protected boolean clashed = false protected boolean channeling = false protected LinkedList<Fx> trailList = new LinkedList<Fx>()

//Instance Variables
private vec3 target
protected unit u
private timer t = getTimer()
protected real scale
protected angle targetAngle
private boolean missileCast = false


construct(unit actor,vec3 pos, player owner, vec3 target, string SFX, real originalScale)
    super(pos.addReals(0, 0, pos.withTerrainZ().z), 0, owner, pos.angleTo2d(target), SFX)
    this.targetAngle = pos.angleTo2d(target)
    this.u = actor
    this.target = target
    this.u.pause()
    this.scale = originalScale



function startMissile()
    this.setSpeed(0)
    this.pos = pos.offset2d(pos.angleTo2d(target), offsetDistance)
    this.pos.z += 50
    this.fixPos()
    this.fx.setColor(missileCol)
    this.fx.setScale(this.scale)

    this.t.setData(this castTo int)
    this.t.startPeriodic(.1, function callExpandMissile)

    //if dust is activated
    if this.dustOn
        this.createDust()



function createDust()
        effect x = AddSpecialEffectTarget(dustSFX, u, "origin")
        DestroyEffectBJ(x)

static function callExpandMissile()
    (GetExpiredTimer().getData() castTo TimedBlasts).expandMissile() 

function expandMissile()
    if this.timeSinceLastChannel == 0
        this.u.setAnimation(channelIndex)
    if this.timeSinceLastChannel >= (.18)
        this.u.setAnimation(channelIndex)
        this.timeSinceLastChannel -= (.17)
    this.timeSinceLastChannel += ANIMATION_PERIOD

    this.scale += .05
    this.fx.setScale(this.scale)
    if this.scale >= endScale
        if this.flightOn
            this.u.setFlyHeight(0, lowerRate)
        if this.launchOn
            this.createLaunchFX()
        if this.dropOn
            this.dropSpeed = realDropSpeed 

        this.setSpeed(speed)

        this.t.release()
        this.u.setAnimation(launchIndex)
        PlaySoundOnUnitBJ(this.throwSound, 70, this.u)
        this.missileCast = true
        if not this.channeling
            this.u.unpause()
        if this.clashing
            this.clashable = true

            //calculate frequency of trail creation


function createLaunchFX()
            Fx launch = new Fx(fx.getPos2(),this.targetAngle, kameLaunch)
            launch.setScale(launchSize)
            doAfter(.5, () -> destroy launch)





function createTrail()
    Fx trail = new Fx(fx.getPos3d(), this.targetAngle, trailSFX)
    ..setScale(trailSize)
    ..setColor(trailCol)
    if flightOn == false
        trail.setZ(100)
//  doAfter(timeDistance, () -> trail.hiddenDestroy())
    this.trailList.add(trail)



function createPulse()
        Fx pulse = new Fx(fx.getPos3d(), this.targetAngle, pulseSFX)
        ..setScale(pulseSize)
        ..setZAngle(-5200)
        ..setColor(pulseCol)
        if this.flightOn == false
            pulse.setZ(100)
        // pulse.getDummy().setAnimation(0)
        doAfter(timeDistance, () -> pulse.hiddenDestroy()) 

override function update()
    super.update()

//should be speed dependent at some level. if pos.toVec2().distToVecSquared(target.toVec2()) < 120120 //timeDistance = 0.15 if this.getSpeed() > 0 if this.trailOn if this.timeSinceLastTrail > (2/speed) this.createTrail() this.timeSinceLastTrail -= (2/speed) this.timeSinceLastTrail += ANIMATION_PERIOD if this.pulseOn if this.timeSinceLastPulse > (2/speed) this.createPulse() this.timeSinceLastPulse -= (2/speed) this.timeSinceLastPulse += ANIMATION_PERIOD if not this.clashed if this.clashable Entity e = Entity.getFirst() while e != null if e!= this and this.owner != e.owner if this.owner.isAllyOf(e.owner) TimedBlasts blast2 = e castTo TimedBlasts if blast2.clashable and not blast2.clashed if this.pos.distToVecSquared(e.pos) < 40000. print(this.pos.distToVecSquared(e.pos).toString()) if this.pos.distToVecSquared(e.pos) < (120120) //print("hi6!") this.onHit(blast2) e=e.next

    if ((this.u.getPos().distToVecSquared(target.toVec2()) &lt; 150*150) or (pos.toVec2().distToVecSquared(target.toVec2()) &lt; 64*64)) and missileCast
        for unit enemy from ENUM_GROUP..enumUnitsInRange(pos.toVec2(), impactAoe)
            if enemy.isEnemy(owner)
                this.u.damageTarget(enemy,  damage)
        done = true
        terminate()


function onHit(TimedBlasts blast2)
    boolean winner1 = false
    boolean winner2 = false
    //print("hi7!")
    this.clashed = true
    blast2.clashed = true
    blast2.setSpeed(0)
    this.setSpeed(0)
    blast2.active = false 
    this.active = false
    //this.fx.setScale(endScale+ 1)
    //blast2.fx.setScale(endScale+ 1)
    doPeriodically(.25, (CallbackPeriodic cb) -> begin
        Fx effect1 = new Fx(fx.getPos2(),this.targetAngle, iceNova)
        ..setScale(1.8)
        ..setZ(100)
        ..setZAngle(-5200)
        Fx effect2 = new Fx(blast2.fx.getPos2(),this.targetAngle, iceNova)
        ..setScale(1.8)
        ..setZ(100)
        ..setZAngle(-5200)
        Fx effect5 = new Fx(((fx.getPos2() + blast2.fx.getPos2()) *.5).toVec3().addReals(0, 0, 50), this.targetAngle, sparklyExplosion)
        ..setScale(5)
        doAfter(.25, () -> begin
            destroy(effect1)
            destroy(effect2)
            destroy(effect5)
        end)

        if this.u.getMana() == 0
            winner2 = true
        if blast2.u.getMana() == 0
            winner1 = true
        this.u.setMana(u.getMana()-1)
        blast2.u.setMana(blast2.u.getMana()-2)
        if winner1
            print(this.u.getName())
            blast2.terminate()
            this.target = blast2.u.getPos3()
            this.setSpeed(this.speed)
            this.active = true
            destroy cb
        if winner2 and not winner1
            print(blast2.u.getName())
            terminate()
            blast2.target = this.u.getPos3()
            blast2.setSpeed(blast2.speed)
            blast2.active = true
            destroy cb
    end)

ondestroy
    if this.channeling
        this.u.unpause()
    Fx final = new Fx(fx.getPos3d(), this.targetAngle, finalSFX)
    final.setScale(2)
    doAfter(destroyExplode, () -> final.hiddenDestroy())
    for i = 0 to (this.trailList.getSize() -1)
        this.trailList.get(i).hiddenDestroy()
    this.done = true

package Kamehameha import Constants import TimedBlasts

constant real originalScale = .2 public class Kamehameha extends TimedBlasts

construct(unit actor,vec3 pos, player owner, vec3 target, int level)
    super(actor,pos.addReals(0, 0, 50),owner,target,bigKamehamehaBall,originalScale)

    //if (actor.getTypeId() == 'H001')
    this.impactAoe = 500
    this.damage = 150*level.toReal() + (GetHeroInt(actor, true)*10)
    this.trailOn = true
    this.dustOn = true
    this.pulseOn = true
    this.launchOn = true
    int actorID = actor.getTypeId()
    if actorID == 'H000'
        this.channelIndex = 11
        this.launchIndex = 12
        this.throwSound = gg_snd_Kamehameha_Voice
    if actorID == 'H005' or actorID == 'H009'
        this.channelIndex = 20
        this.launchIndex = 18
    if actorID == 'H00A'
        this.channelIndex = 5
        this.launchIndex = 4
    if actorID == 'H001' or actorID == 'H008'
        this.channelIndex = 7
        this.launchIndex = 8
        this.throwSound = gg_snd_GohanKame
    if actorID == 'H00D' or actorID == 'H00E' or actorID == 'H00F'
        this.channelIndex = 8
        this.launchIndex = 12
    if actorID == 'H007'
        this.channelIndex = 9
        this.launchIndex = 13
    if actorID == 'H00G'
        this.channelIndex = 12
        this.launchIndex = 11
    this.speed = 32
    this.endScale = .6
    this.trailSFX = bigKamehamehaBall 
    this.trailSize = .3
    this.pulseSFX = bluePulse
    this.launchSFX = kameLaunch
    this.launchSize = 5
    this.finalSFX = bigBlueExplosion
    this.pulseSize = 1
    this.trailCol = colorA(255,255,255,255)
    this.clashing = true
    this.channeling = true
    startMissile()

package AngryKame import Constants import TimedBlasts import ClosureTimers

constant real originalScale = .2 public class AngryKame extends TimedBlasts

construct(unit actor,vec3 pos, player owner, vec3 target, int level)
    super(actor,pos.addReals(0, 0, 700),owner,target,dummy2,originalScale)
    this.impactAoe = 500
    actor.setFlyHeight(700, 700.)
    this.flightOn = true
    this.dropOn = true
    this.offsetDistance = 0
    this.missileCol = colorA(150,150,0,255)
    this.speed = 25
    this.endScale = 1.5
    this.dustSFX = dustWave
    this.damage = 5000*level.toReal() + (GetHeroInt(actor, true)*20)
    this.finalSFX = nuclearExplosion
    this.trailOn = true
    this.dustOn = true
    this.pulseOn = true
    this.launchOn = false
    this.channelIndex = 20
    this.launchIndex = 10
    this.trailSFX = pinkBall
    this.trailSize = .6
    this.launchSFX = kameLaunch
    this.launchSize = 5
    this.trailCol = colorA(150,150,0,255)
    this.lowerRate = 300.
    this.realDropSpeed = -25
    this.destroyExplode = 3
    this.pulseSFX = windBlue
    this.pulseSize = 1.2
    this.pulseCol = colorA(150,150,0,255)
    effect x = AddSpecialEffectTarget(kamelightning, u, "origin")
    doAfter(1.5, () -> begin
        doAfter(1, () -> begin
            PlaySoundOnUnitBJ(gg_snd_angrykame, 70, this.u)
        end)
        startMissile()
        fx.setFx(pinkBall)
    end)
    doAfter(12, () -> begin
        DestroyEffectBJ(x)
    end)

goto line:
Compare with:
text copy window edit this code post new code