Weep
Godspeed to the sound of the pounding
- Reaction score
- 400
It may be a lot to ask, since this is a pretty long bit of code, but I'm looking to optimize the snot out of this. It's a spell I originally made in GUI (yes, really) and it's pretty damaging to the FPS (though it's about 40% faster than in GUI already).
So far I've done what I know about how to streamline things - linked lists, mostly.
Would someone be willing to look through and suggest performance improvements? Also, if there are faults, feel free to point those out, too.
I've omitted the globals/configuration since they are mostly boring or self-explanatory. Bear in mind it requires a unit auto-indexer.
So far I've done what I know about how to streamline things - linked lists, mostly.
Would someone be willing to look through and suggest performance improvements? Also, if there are faults, feel free to point those out, too.
I've omitted the globals/configuration since they are mostly boring or self-explanatory. Bear in mind it requires a unit auto-indexer.
JASS:
//====================================================================
private struct UnitData
real X
real Y
real facing
real height
debug static integer count = 0
thistype prev = 0
thistype next = 0
method update takes unit u returns thistype
if GetUnitTypeId(u) != 0 or GetUnitAbilityLevel(u, 039;Aloc039;) > 0 then
set .X = GetUnitX(u)
set .Y = GetUnitY(u)
set .facing = GetUnitFacing(u)
set .height = GetUnitFlyHeight(u)
endif
return this
endmethod
debug static method create takes nothing returns thistype
debug set count=count+1
debug return thistype.allocate()
debug endmethod
debug private method onDestroy takes nothing returns nothing
debug set count=count-1
debug endmethod
endstruct
private keyword Stream
//===================================================================
// Struct with information and methods pertaining to each missile.
private struct Missile extends array
private unit fx
private effect sfx
private real time
private UnitData headData
private UnitData tailData
thistype prev
thistype next
Stream parent
static method moveHead takes thistype this returns nothing
local real tempReal = 0.
local real tempFactor = 0.5
local real xf = 0.
local real yf = 0.
local real dist = SquareRoot((.parent.targetData.X - .headData.X)*(.parent.targetData.X - .headData.X) + (.parent.targetData.Y - .headData.Y)*(.parent.targetData.Y - .headData.Y))
if dist <= SPEED[.parent.level] and .time >= TIME_MIN[.parent.level] then
// call .damage()
call UnitApplyTimedLife(.fx, 039;BTLF039;, 1.)
call GroupAddUnit(.parent.dead, .fx)
if .parent.headMissile.prev > 0 then
set .prev.next = 0
set .parent.headMissile = .prev
call thistype.moveHead(.parent.headMissile)
endif
else
set .tailData = .tailData.next
call .tailData.prev.destroy()
set .headData.next = UnitData.create().update(.fx)
set .headData.next.prev = .headData
set .headData = .headData.next
if .time >= TIME_MIN[.parent.level] then
if IsUnitType(.parent.target, UNIT_TYPE_FLYING) then
set tempReal = .parent.targetData.height - 100. + HEIGHT_DIFF[.parent.streamType]
else
set tempReal = .parent.targetData.height + HEIGHT_DIFF[.parent.streamType]
endif
call SetUnitFlyHeight(.fx, .headData.height + (SPEED[.parent.level]/(HEIGHT_ADJUSTMENT_RATE*dist))*(tempReal - .headData.height), 0.)
endif
if .time >= 10000. or (dist <= PROXIMITY[.parent.level] and .time >= TIME_MIN[.parent.level]) then
if .time < 10000. then
set .time = 10000.
endif
set tempReal = Atan2(.parent.targetData.Y - .headData.Y, .parent.targetData.X - .headData.X)
set xf = (1. - tempFactor)*(.headData.X + RMinBJ(SPEED[.parent.level], dist) * Cos(tempReal)) + tempFactor*(.headData.X + RMinBJ(SPEED[.parent.level], dist) * Cos(.headData.facing*bj_DEGTORAD))
set yf = (1. - tempFactor)*(.headData.Y + RMinBJ(SPEED[.parent.level], dist) * Sin(tempReal)) + tempFactor*(.headData.Y + RMinBJ(SPEED[.parent.level], dist) * Sin(.headData.facing*bj_DEGTORAD))
call SetUnitFacing(.fx, bj_RADTODEG*Atan2(.parent.targetData.Y - yf, .parent.targetData.X - xf))
else
set xf = .headData.X + SPEED[.parent.level] * Cos(.headData.facing*bj_DEGTORAD)
set yf = .headData.Y + SPEED[.parent.level] * Sin(.headData.facing*bj_DEGTORAD)
set tempReal = Atan2(.parent.targetData.Y - yf, .parent.targetData.X - xf)
if .time >= TIME_MAX[.parent.level] then
call SetUnitFacing(.fx, tempReal*bj_RADTODEG)
else
call SetUnitFacing(.fx, GetRandomReal(-1.*WANDER[.parent.level], WANDER[.parent.level]) + tempReal*bj_RADTODEG)
endif
endif
call SetUnitX(.fx, xf)
call SetUnitY(.fx, yf)
endif
set .time = .time + MOVEMENT_PERIOD
endmethod
method moveTail takes nothing returns nothing
local real tempReal = 0.
local real xf = 0.
local real yf = 0.
set .tailData = .tailData.next
call .tailData.prev.destroy()
set .headData.next = UnitData.create().update(.fx)
set .headData.next.prev = .headData
set .headData = .headData.next
call SetUnitX(.fx, .next.tailData.X)
call SetUnitY(.fx, .next.tailData.Y)
call SetUnitFacing(.fx, .next.tailData.facing)
if .time >= TIME_MIN[.parent.level] then
call SetUnitFlyHeight(.fx, .next.tailData.height, 0.)
endif
set .time = .time + MOVEMENT_PERIOD
endmethod
static method moveDead takes nothing returns nothing
local thistype this = thistype[GetUnitId(GetEnumUnit())]
local real tempReal = 0.
local real tempFactor = 0.5
local real dist = SquareRoot((.parent.targetData.X - .headData.X)*(.parent.targetData.X - .headData.X) + (.parent.targetData.Y - .headData.Y)*(.parent.targetData.Y - .headData.Y))
if dist <= SPEED[.parent.level] then
call SetUnitX(.fx, .parent.targetData.X)
call SetUnitY(.fx, .parent.targetData.Y)
if IsUnitType(.parent.target, UNIT_TYPE_FLYING) then
set tempReal = .parent.targetData.height - 100. + HEIGHT_DIFF[.parent.streamType]
else
set tempReal = .parent.targetData.height + HEIGHT_DIFF[.parent.streamType]
endif
call SetUnitFlyHeight(.fx, tempReal, 0.)
endif
endmethod
static method create takes Stream parent returns thistype
local integer i = 1
local UnitData prevData = 0
local thistype this = 0
local unit u = CreateUnit(GetOwningPlayer(parent.caster), FX_DUMMY_UNIT_ID(IsUnitType(parent.caster, UNIT_TYPE_FLYING)), parent.casterData.X, parent.casterData.Y, parent.angle)
set this = thistype[GetUnitId(u)]
set .fx = u
set .parent = parent
set .time = 0
set .prev = 0
set .next = 0
if IsUnitType(.parent.caster, UNIT_TYPE_FLYING) then
call SetUnitFlyHeight(.fx, .parent.casterData.height - 100 + HEIGHT_DIFF[.parent.streamType], 0)
if not IsUnitType(.parent.target, UNIT_TYPE_FLYING) then
call IssueImmediateOrder(.fx, "ravenform")
endif
else
if IsUnitType(.parent.target, UNIT_TYPE_FLYING) then
call IssueImmediateOrder(.fx, "ravenform")
else
call SetUnitFlyHeight(.fx, .parent.casterData.height + HEIGHT_DIFF[.parent.streamType], 0)
endif
endif
call SetUnitX(.fx, .parent.casterData.X)
call SetUnitY(.fx, .parent.casterData.Y)
//FX
call SetUnitScale(.fx, MISSILE_SCALE[.parent.streamType], 1., 1.)
set .sfx = AddSpecialEffectTarget(MISSILE_FX[.parent.streamType], .fx, "overhead")
//Data
set prevData = UnitData.create().update(.fx)
set .headData = prevData
loop
exitwhen i >= RATIO
set .tailData = UnitData.create().update(.fx)
set prevData.prev = .tailData
set .tailData.next = prevData
set prevData = .tailData
set i = i+1
endloop
return this
endmethod
private static method onDeath takes nothing returns boolean
local thistype this = thistype(GetUnitId(GetTriggerUnit()))
local integer i = 0
local UnitData previous = 0
if IsUnitInGroup(.fx, .parent.dead) then
loop
exitwhen i >= RATIO
set previous = .headData.prev
call .headData.destroy()
set .headData = previous
set i = i+1
endloop
debug call BJDebugMsg(I2S(UnitData.count))
call ShowUnit(.fx, false)
call GroupRemoveUnit(.parent.dead, .fx)
call DestroyEffect(.sfx)
call RemoveUnit(.fx)
if this == .parent.tailMissile then
call .parent.destroy()
endif
endif
return false
endmethod
private static method endMorph takes nothing returns boolean
local thistype this = thistype[GetUnitId(GetTriggerUnit())]
if GetSpellAbilityId() == FX_DUMMY_MORPH_ABILITY and GetTriggerUnit() == .fx then
call SetUnitScale(.fx, MISSILE_SCALE[.parent.streamType], 1., 1.)
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_FINISH)
call TriggerAddCondition(t, Condition(function thistype.endMorph))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function thistype.onDeath))
endmethod
endstruct
//===================================================================
// Struct with information and methods pertaining to each stream of missiles.
private struct Stream
unit caster
UnitData casterData
integer level
real angle
unit target
UnitData targetData
integer streamType
group dead
private static boolean array deadInit
Missile headMissile = 0
Missile tailMissile = 0
private timer movement
thistype prev
private static method moveAll takes nothing returns nothing
local thistype this = thistype(GetTimerData(GetExpiredTimer()))
local Missile m = 0
call .casterData.update(.caster)
call .targetData.update(.target)
if .headMissile > 0 then
call Missile.moveHead(.headMissile)
set m = .headMissile.prev
loop
exitwhen m == 0
if m.next == 0 then
endif
call m.moveTail()
set m = m.prev
endloop
endif
call ForGroup(.dead, function Missile.moveDead)
endmethod
method fire takes nothing returns Missile
set .tailMissile.prev = Missile.create(this)
set .tailMissile.prev.next = .tailMissile
set .tailMissile = .tailMissile.prev
return .tailMissile
endmethod
static method create takes unit caster, integer level, unit target, real angle, thistype prev returns thistype
local thistype this = thistype.allocate()
set .caster = caster
set .casterData = UnitData.create().update(caster)
set .level = level
set .target = target
set .targetData = UnitData.create().update(target)
set .angle = angle
set .prev = prev
if not deadInit[this] then
set dead = CreateGroup()
set deadInit[this] = true
endif
set .streamType = STREAM_TYPE(level)
set .headMissile = .fire()
set .movement = NewTimer()
call SetTimerData(.movement, this)
call TimerStart(.movement, MOVEMENT_PERIOD, true, function thistype.moveAll)
return this
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.movement)
call GroupClear(.dead)
call .casterData.destroy()
call .targetData.destroy()
endmethod
endstruct
//===================================================================
// Struct with information and methods pertaining to the caster itself.
private struct Cast extends array
Stream lastStream
private timer firing
private static method fireAll takes nothing returns nothing
local Stream this = thistype(GetTimerData(GetExpiredTimer())).lastStream
loop
exitwhen this == 0
call this.fire()
set this = this.prev
endloop
endmethod
private static method start takes unit caster, unit target returns nothing
local integer i = 1
local thistype this = thistype[GetUnitId(caster)]
local Stream prev = 0
local real X = GetUnitX(caster)
local real Y = GetUnitY(caster)
local integer level = GetUnitAbilityLevel(caster, BASE_ABILITY)
local real angle = bj_RADTODEG*Atan2(GetUnitY(target) - Y, GetUnitX(target) - X)
loop
exitwhen i > STREAM_COUNT[level]
set .lastStream = Stream.create(caster, level, target, (ANGLES[2*level - 1]*I2R(i - 1) + ANGLES[2*level]*I2R(STREAM_COUNT[level] - i))/I2R(STREAM_COUNT[level] - 1) + angle, prev)
set prev = .lastStream
set i = i+1
endloop
set .firing = NewTimer()
call SetTimerData(.firing, this)
call TimerStart(.firing, EMISSION_RATE, true, function thistype.fireAll)
//FX
endmethod
private method stop takes nothing returns nothing
call ReleaseTimer(.firing)
endmethod
private static method onEnd takes nothing returns boolean
if GetSpellAbilityId() == BASE_ABILITY then
call thistype[GetUnitId(GetTriggerUnit())].stop()
endif
return false
endmethod
private static method onCast takes nothing returns boolean
if GetSpellAbilityId() == BASE_ABILITY then
call thistype.start(GetTriggerUnit(), GetSpellTargetUnit())
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.onCast))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function thistype.onEnd))
endmethod
endstruct