| 03-23-2008, 06:52 PM | #1 |
This spell lags horribly when it knocks back enemies, and I want to fix that. Charge:scope Charge globals private constant integer spellid = 'A04T' private constant string spellart = "" private constant string areaart = "" private group array g endglobals private function cond takes nothing returns boolean return GetSpellAbilityId() == spellid endfunction private function filter takes nothing returns boolean return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(bj_groupRandomCurrentPick)) and (IsUnitInGroup(GetFilterUnit(),g[GetUnitIndex(bj_groupRandomCurrentPick)]) == false) endfunction private function forgroup takes nothing returns nothing local unit u = bj_groupRandomCurrentPick local unit t = GetEnumUnit() local integer pui = GetUnitIndex(u) call UnitDamageTarget(u,t,(I2R(GetHeroStr(u,true)))/5.,true,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_FORCE,WEAPON_TYPE_WHOKNOWS) call KS_KnockbackUnit(t,200.,Atan2(GetUnitY(t)-GetUnitY(u),GetUnitX(t)-GetUnitX(u)),KS_GetUnitMass(u)) call GroupAddUnit(g[pui],u) set u = null set t = null endfunction private function check takes nothing returns nothing local unit u = GetTriggerUnit() local real m = GetUnitState(u,UNIT_STATE_MAX_MANA) * .1 local real a = GetUnitState(u,UNIT_STATE_MANA) if m > a then call SimError(GetOwningPlayer(u),"Not enough mana.") call PauseUnit(u,true) call IssueImmediateOrder(u,"stop") call PauseUnit(u,false) endif set u = null endfunction private function actions takes nothing returns nothing local unit u = GetTriggerUnit() local real x = GetUnitX(u) local real y = GetUnitY(u) local unit t = GetSpellTargetUnit() local real tx = GetUnitX(t) local real ty = GetUnitY(t) local real d = SquareRoot(((tx-x)*(tx-x))+((ty-y)*(ty-y))) local real a = Atan2(tx-x,ty-y) local integer pui = GetUnitIndex(u) set g[pui] = NewGroup() call SetUnitTimeScale(u,3.) // Doesn't work!? call SetUnitAnimation(u,"walk") // Doesn't work either... call PauseUnit(u,true) // I've tried putting this over the top two call SlideToPointEx(u,tx,ty,.5,function forgroup,Condition(function filter)) call SmartWait(.5) call SetUnitAnimation(u,"stand") call PauseUnit(u,false) call SetUnitTimeScale(u,1.) call ReleaseGroup(g[pui]) set u = null set t = null endfunction function InitTrig_Charge takes nothing returns nothing local trigger tr = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(tr,EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(tr,Condition(function cond)) call TriggerAddAction(tr,function actions) set tr = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(tr,EVENT_PLAYER_UNIT_SPELL_CAST) call TriggerAddCondition(tr,Condition(function cond)) call TriggerAddAction(tr,function check) set tr = null call Preload(spellart) call Preload(areaart) endfunction endscope I went ballsy and used the handle bug on code vars. If this is the problem then I'll have to find some way around it... here is Jazradel's "Jump" template, changed quite a bit (this is only a small part of it that doesn't include flyheights and such) Thanks to him for making the sample. MoveUnit:library MoveUnit uses TT private struct data unit u real s real ss real b real a real z real dis real ang group g integer func boolexpr filt method onDestroy takes nothing returns nothing set .u = null if .g != null then call ReleaseGroup(.g) set .g = null endif if .filt != null then call DestroyBoolExpr(.filt) set .filt = null endif endmethod endstruct private function C2I takes code c returns integer return c return 0 endfunction private function I2C takes integer i returns code return i return null endfunction private function slide takes nothing returns boolean local data d = TT_GetData() local code f = I2C(d.func) call SetUnitX(d.u,GetUnitX(d.u)+d.dis*Cos(d.ang)) call SetUnitY(d.u,GetUnitY(d.u)+d.dis*Sin(d.ang)) if f != null then set d.g = NewGroup() set bj_groupRandomCurrentPick = d.u call GroupEnumUnitsInRange(d.g,GetUnitX(d.u),GetUnitY(d.u),200.,d.filt) call ForGroup(d.g,f) // Lags horribly... may have something to do with storing the code? call ReleaseGroup(d.g) set d.g = null endif if (d.s <= 1.) then set d.b = 1. endif if (d.b==0.) then set d.s = d.s - 1. else set d.s = d.s + 1. endif if ( d.s > d.ss) then call d.destroy() return true endif return false endfunction function SlideToPointEx takes unit u,real x,real y,real duration,code forgroup,boolexpr filt returns nothing local real tx = 0. local real ty = 0. local real s = 0. local data d = data.create() if duration == 0 then call SetUnitX(u,x) call SetUnitY(u,y) else if x > GetRectMaxX(bj_mapInitialPlayableArea) - 50 then set x = GetRectMaxX(bj_mapInitialPlayableArea) - 50 elseif x < GetRectMinX(bj_mapInitialPlayableArea) + 50 then set x = GetRectMinX(bj_mapInitialPlayableArea) + 50 endif set tx = x - GetUnitX(u) if y > GetRectMaxY(bj_mapInitialPlayableArea) - 50 then set y = GetRectMaxY(bj_mapInitialPlayableArea) - 50 elseif y < GetRectMinY(bj_mapInitialPlayableArea) + 50 then set y = GetRectMinY(bj_mapInitialPlayableArea) + 50 endif set ty = y - GetUnitY(u) set s = (duration/0.035)/2 set d.u = u set d.b = 0. set d.s = s set d.ss = s set d.dis = SquareRoot(tx*tx+ty*ty)/s/2 set d.ang = Atan2(ty,tx) set d.func = C2I(forgroup) set d.filt = filt call TT_Start(function slide,d) endif endfunction endlibrary I'm using a modified version of emjlr3's knockback system for my knockback in the forgroup... Thanks to him for letting me move units fluidly. Also to Cohadar for making the system possible. :) KnockbackSystem:library KS initializer Init, needs PUI // Configuration Options: globals private constant real Friction = .7 // Friction between sliding units and the ground (>0.) private constant real Gravity = 9.8 // Gravity in your WE world (distance/s, >0.) private constant real Stop_Velocity = .1 // Speed/interval at which units stop sliding private constant real Interval = .03 // Interval length for periodic timer private constant boolean Fight = false // Whether units can fight against slides private constant string Sfx = "SlideDust.mdx" // Effect created on sliding units endglobals // NO TOUCHING PAST THIS POINT, UNLESS YOU PAIN FOR DEATH !!! // I pained for death, and made a few changes: Doesn't destroy destructables // (I use pathing blockers and custom cliffs), doesn't use custom BJs, different // order of functions, stores dieing unit masses in a side var for later recovery // Global struct to pass updated data to users public struct Dat real cos = 0. real sin = 0. real vel = 0. endstruct // Needed struct private struct data unit u = null real vel = 0. real accel = 0. real ang = 0. real cos = 0. real sin = 0. effect sfx = null endstruct globals // Stores units mass and whether they are sliding public real array Mass public boolean array Has_Data // Timer callbacks ran every second private real Runs = 1./Interval // Data for looping through active sliding units private timer Timer = CreateTimer() private integer Total = 0 private data array Data private real array DMass endglobals // Set the mass of a unit, affects collision effects public function SetUnitMass takes unit u, real mass returns nothing local integer pui = GetUnitIndex(u) debug if u==null then debug call BJDebugMsg("KS Warning: No unit was provided to set the mass of.") debug return debug endif set Mass[pui] = mass endfunction // Get the mass of a unit, default mass = 100. public function GetUnitMass takes unit u returns real local integer pui = GetUnitIndex(u) debug if u==null then debug call BJDebugMsg("KS Warning: No unit was provided to get the mass of.") debug return 0. debug endif if Mass[pui]==0. then if DMass[pui] == 0. then if IsUnitType(u,UNIT_TYPE_DEAD) == true then return 0. endif return 100. else return DMass[pui] endif endif return Mass[pui] endfunction private function Die takes nothing returns nothing local integer pui = GetUnitIndex(GetTriggerUnit()) set DMass[pui] = GetUnitMass(GetTriggerUnit()) set Mass[pui] = 0. endfunction private function Init takes nothing returns nothing local trigger trig = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_DEATH) call TriggerAddAction(trig,function Die) endfunction // Timer callback, which loops through active sliding units private function KnockbackUnit_Child takes nothing returns nothing local data d local integer i = 1 local integer pui local real x local real y loop exitwhen i > Total set d = Data[i] if d.vel>Stop_Velocity then // Still moving fast enough to continue sliding // Slide to new x and y spots set x = GetUnitX(d.u)+(d.vel/Runs)*d.cos set y = GetUnitY(d.u)+(d.vel/Runs)*d.sin if Fight then call SetUnitX(d.u,x) call SetUnitY(d.u,y) else call SetUnitPosition(d.u,x,y) endif // Vo - a = Vnew set d.vel = d.vel - d.accel else // Its all over for this unit call DestroyEffect(d.sfx) set pui = GetUnitIndex(d.u) set Has_Data[pui] = false call d.destroy() set Data[i] = Data[Total] set Total = Total - 1 if Total==0 then call PauseTimer(Timer) endif set i = i - 1 endif set i = i + 1 endloop endfunction private function Remove_Struct takes nothing returns boolean local Dat dd = ABCT_GetData() call dd.destroy() return true endfunction // Start a knockback for a unit, angle is in radians, mass/speed>0. public function KnockbackUnit takes unit u, real speed, real angle, real mass returns Dat local data d local Dat dd local integer pui = GetUnitIndex(u) local boolean b = Has_Data[pui] local real m = GetUnitMass(u) local integer i = 1 local real theta local real velx1 local real vely1 local real velx2 local real vely2 local real new_ang local real new_vel debug if u==null then debug call BJDebugMsg("KS Warning: No unit was provided to knockback.") debug return 0 debug elseif mass<0. then debug call BJDebugMsg("KS Warning: A negative mass was provided to knockback.") debug return 0 debug elseif speed<0. then debug call BJDebugMsg("KS Warning: A negative speed was provided to knockback.") debug return 0 debug endif if b==false then set d = data.create() set d.u = u // Conservation of momentum, m1*v1 = m2*v2 set d.vel = (mass*speed) / m // Fnet/m = a, the net force is the force of friction on the unit set d.accel = (m*9.8*Friction) / m set d.ang = angle set d.cos = Cos(angle) set d.sin = Sin(angle) set d.sfx = AddSpecialEffectTarget(Sfx,u,"origin") if Total==0 then call TimerStart(Timer, Interval, true, function KnockbackUnit_Child) endif set Total = Total + 1 set Data[Total] = d set Has_Data[pui] = true return 0 else loop set d = Data[i] exitwhen d.u==u or i>Total endloop debug if i>Total then debug call BJDebugMsg("KS Warning: This unit should be sliding but it is not, please report this error to emjlr3, with the replay.") debug return 0 debug endif // X and Y components of velocities set theta = ModuloReal(angle*bj_RADTODEG,90.) set vely1 = Sin(angle)*speed set velx1 = Cos(angle)*speed set theta = ModuloReal(angle*bj_RADTODEG,90.) set vely2 = Sin(angle)*d.vel set velx2 = Cos(angle)*d.vel // Conservation of momentum set velx1 = ((mass*velx1)+(m*velx2))/(mass+m) set vely1 = ((mass*vely1)+(m*vely2))/(mass+m) // Determine new velocity and angle from X and Y components set new_vel = SquareRoot(velx1*velx1 + vely1*vely1) set new_ang = Atan(vely1/velx1) // Store new values to the sliding units struct set d.ang = new_ang set d.cos = Cos(d.ang) set d.sin = Sin(d.ang) set d.vel = new_vel // Return said values to the user so he can use it at his end set dd = Dat.create() set dd.cos = d.cos set dd.sin = d.sin set dd.vel = d.vel call ABCT_Start(function Remove_Struct,dd,.1) return dd endif endfunction endlibrary I guess I pain for death...? Perhaps there is a reason this is lagging that isn't what I'm thinking (with the stored code value)? What the spell is supposed to do is Charge toward the target unit, knocking away and damaging units along the way. (Eventually it'll have a neat finale, but that is for later.) However, I only want units to be knocked/damaged once, so I stored them in a Cohadar-friendly-PUI-using global group array. Realizing that this enabled me to exclude damaged units using the boolexpr itself, everything seemed in tip-top shape until I tested it. Now, everything works correctly, but it lags up pretty bad when the units start getting knocked away. I have other knockback spells and effects in my map using that system and none of them lag. EDIT: Apparently, I can't make a damn spell without using at least two systems and at least 400 lines of code... |
| 03-24-2008, 09:36 PM | #2 |
bump Any ideas at all as to why it would lag/how to fix it? |
| 03-25-2008, 12:28 AM | #3 |
JASS:private function C2I takes code c returns integer return c return 0 endfunction private function I2C takes integer i returns code return i return null endfunction I'm not 100% sure, but I don't think you can treat code handles like you treat another handles like units, timers, ... |
| 03-25-2008, 03:31 AM | #4 |
The functions work fine, and all calls are made correctly, but it just lags is all. |
| 03-25-2008, 05:06 AM | #5 |
how many enemies? |
| 03-25-2008, 03:56 PM | #6 |
Uh.. I just tested it again, and I don't remember making any changes, but... the hero would forever follow and knockback one unit; when the unit is done being knocked, the hero would take it all on its own and charge again, knocking the same unit back again, in a forever loop of annoyance. I'll check my code again... EARLIER, when it was working properly but laggily, it would lag up even if one unit was pushed. I've had 100 units being knocked back on this map without lag (on my computer) so something is fishy about it. EDIT: Alright, it isn't lagging now, but the Warrior is still forever pushing until I order him to stop. I guess I'll just add a stop order to the bottom of my script, but I don't understand why its doing that. Ok, the problem was that A: JASS:
if f != null then
set d.g = NewGroup()
set bj_groupRandomCurrentPick = d.u
call GroupEnumUnitsInRange(d.g,GetUnitX(d.u),GetUnitY(d.u),200.,d.filt)
call ForGroup(d.g,f) // Lags horribly... may have something to do with storing the code?
call ReleaseGroup(d.g)
set d.g = null
endif
JASS:local unit e // // if f != null then set bj_groupRandomCurrentPick = d.u call GroupEnumUnitsInRange(d.g,GetUnitX(d.u),GetUnitY(d.u),200.,d.filt) call ForGroup(d.g,f) loop set e = FirstOfGroup(d.g) exitwhen e == null call GroupRemoveUnit(d.g,e) endloop endif However, it still goes on forever, as if the guy is recasting the spell all on his own. So... B: The warrior was trying to knockback already knocked units several times before, and would stop from something other than my orders. The many, many knockbacks (must have been over 100, or perhaps emjlr's "inelastic collisions" are a bit laggier than normal knocks) lagged the game up unbearably. |
