HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[OLY] Spell Review

08-17-2008, 01:04 PM#1
saw792
Hi,
This is my first time entering a competition on wc3c, so I'm interested in opinions/improvements on my coding. I've spent a good three or four days working on this, so I hope it isn't too bad:
Collapse JASS:
scope PSphere initializer Init
//=========================================================================================================\\
//==================================================Info===================================================\\
//=========================================================================================================\\
//                                                                                                         \\
//    |Pressure Sphere| by saw792 (TheHelper, wc3campaigns)                                                \\
//    -------------------------                                                                            \\
//    Creates a concentrated ball of air at the location of the caster, briefly stunning all units         \\
//    in the AOE and causing the caster to be propelled backwards through the air.                         \\
//                                                                                                         \\
//    Requires:                                                                                            \\
//    ---------                                                                                            \\
//       - A vJASS preprocessor (Jasshelper, Newgen Pack)                                                  \\
//       - CSData from Caster System (by Vexorian)                                                         \\
//                                                                                                         \\
//    To use in your own map:                                                                              \\
//    -----------------------                                                                              \\
//       - Copy this text into a blank GUI trigger converted to custom text                                \\
//       - Copy the 'Dummy' unit from the object editor into your map                                      \\
//       - Give the ability to a hero, and enjoy                                                           \\
//                                                                                                         \\
//                                                                                                         \\
//=========================================================================================================\\
//=================================================Globals=================================================\\
//=========================================================================================================\\

globals
  private unit caster //Casting unit
  private boolean cancel = false //Don't change. Used in conjunction with ALLOWCANCEL
  private constant real TOTAL_TIME = 1.5 //Total length of spell. Modify this value rather than T
                                         //Must be greater than T at all times.
  private constant real TIMER_INTERVAL = 0.04 //Number of seconds between timer expirations
  private constant integer ABILITY_ID = 'A000' //Rawcode of Pressure Sphere Ability
//SFX config globals
  private constant integer DUMMY_ID = 'h000' //Rawcode of SFX dummy unit
  private constant real T = 1.2 //Knockback time and SFX dummy grow time. Modification not recommended.
  private constant real MAX_SCALE = 8 //Largest unit scale SFX dummy reaches. Initial scale should always be 1.
//Knockback config globals
  private constant real S = 200 //Distance of knockback
  private constant real A = 555.5556 //Rate of deceleration of knockback
  private constant real RADIUS = 250 //AOE of knockback effect from caster
//Jump config globals
  private constant boolean ALLOWCANCEL = true //Set this to true to allow the caster to cancel the slide
  private constant real JUMP_DISTANCE = 600 //Distance travelled in jump in a straight line (no height)
  private constant real MAX_HEIGHT = 300 //Max height reached in the jump
endglobals

//=========================================================================================================\\
//=================================================Structs=================================================\\
//=========================================================================================================\\
private struct Sphere
  //Miscellaneous
  integer tcount
  real exit1 = TOTAL_TIME / TIMER_INTERVAL
  real exit2 = T / TIMER_INTERVAL
  //SFX
  unit dummy
  real x
  real y
  real scale
  real scaleminus = (MAX_SCALE - 1) / (.exit1-.exit2)
  real scaleplus = (MAX_SCALE-1) / .exit2
  //Knockback
  real rs = -S + 400 //Correcting for distance offset
  real ns
  real u 
  real dx 
  real dy 
  real a 
  unit target
  group g = CreateGroup()
  group gcopy = CreateGroup()
  //Jump
  real jx
  real jy
  real jz
  real ja
  real jd
  real cosj
  real sinj
  
  
  static method create takes nothing returns Sphere
    local Sphere data = Sphere.allocate()
    set caster = GetTriggerUnit()
    set data.x = GetUnitX(caster)
    set data.y = GetUnitY(caster)
    set data.scale = 1.00
    set data.dummy = CreateUnit(GetOwningPlayer(caster), DUMMY_ID, data.x, data.y, 0)
    set data.ja = ((GetUnitFacing(caster) * bj_DEGTORAD) - 3.1416) //The opposite direction
    set data.jd = (JUMP_DISTANCE / data.exit1) //Distance per execution
    set data.cosj = Cos(data.ja)
    set data.sinj = Sin(data.ja)
    call UnitAddAbility(caster, 'Arav')
    return data
  endmethod
  
  method onDestroy takes nothing returns nothing
    call RemoveUnit(.dummy)
    call DestroyGroup(.gcopy)
    call SetUnitFlyHeight(caster, 0, 0)
    call UnitRemoveAbility(caster, 'Arav')
    set cancel = false
    set caster = null
  endmethod
  
endstruct 
  
  
//=========================================================================================================\\
//===============================================Functions=================================================\\
//=========================================================================================================\\

  private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABILITY_ID and ALLOWCANCEL
  endfunction
  
  private function MoveCancelConditions takes nothing returns boolean
    return GetIssuedOrderId() == OrderId("smart")
  endfunction
  
  private function FilterG takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(caster))
  endfunction
  
  private function Unpause takes nothing returns nothing
    call PauseUnit(GetEnumUnit(), false)
  endfunction
  
  private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local Sphere data = GetCSData(t)
    call GroupAddGroup(data.g, data.gcopy) 
    set data.tcount = data.tcount + 1
      //SFX
      if data.tcount > data.exit1 then //When SFX and Knockback end
         call ForGroup(data.g, function Unpause)
         call PauseTimer(t)
         call DestroyTimer(t)
         set data.tcount = 0
         call data.destroy()
      else
      
         if data.tcount >= data.exit2 then
            set data.scale = data.scale - data.scaleminus
            call SetUnitScale(data.dummy, data.scale, data.scale, data.scale)
         else
            set data.scale = data.scale + data.scaleplus
            call SetUnitScale(data.dummy, data.scale, data.scale, data.scale)
         endif
         
         //Knockback
         set data.rs = data.rs - data.ns //Distance remaining to travel
         set data.u = (data.rs - 0.5 * A * T * T) / T //s = ut + 1/2at^2 rearranged for u
         set data.ns = (data.u * TIMER_INTERVAL + 0.5 * A * TIMER_INTERVAL * TIMER_INTERVAL) //Distance to travel this interval. Once again uses
           
           loop                                                                              //s = ut + 1/2at^2
             set data.target = FirstOfGroup(data.gcopy)
             exitwhen data.target == null
             set data.a = (Atan2(data.y - GetUnitY(data.target), data.x - GetUnitX(data.target))) //Angle between caster and target unit
             set data.dx = (data.ns * Cos(data.a) + GetUnitX(data.target)) //Old horizontal position +  horizontal distance to move
             set data.dy = (data.ns * Sin(data.a) + GetUnitY(data.target)) //Old vertical position + vertical distance to move
             call PauseUnit(data.target, true)
             call SetUnitPosition(data.target, data.dx, data.dy)
             call GroupRemoveUnit(data.gcopy, data.target)
           endloop
          //Jump
          set data.jx = (data.tcount * data.jd * data.cosj + data.x) //Another polar projection
          set data.jy = (data.tcount * data.jd * data.sinj + data.y) //Another polar projection
          set data.jz = (-(MAX_HEIGHT / ((data.exit1 / 2) * (data.exit1 / 2))) * data.tcount) * (data.tcount - data.exit1) //Quadratic relationship
          
          if cancel == false then
            call SetUnitPosition(caster, data.jx, data.jy)
          endif
          
          call SetUnitFlyHeight(caster, data.jz, 0)
      endif
  endfunction
  
  private function MoveCancel takes nothing returns nothing
    if caster != null then
      set cancel = true
    endif
  endfunction
  
  private function Actions takes nothing returns nothing
    local Sphere data = Sphere.create()
    local timer t = CreateTimer()
    call GroupEnumUnitsInRange(data.g, data.x, data.y, RADIUS, Condition(function FilterG))
    call SetCSData(t, data)
    call TimerStart(t, 0.04, true, function Callback)
  endfunction
  
//=========================================================================================================\\
//===================================================Init==================================================\\
//=========================================================================================================\\  
  
  private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local trigger t2 = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerAddCondition(t, Condition(function Conditions))
    call TriggerAddCondition(t2, Condition(function MoveCancelConditions))
    call TriggerAddAction(t, function Actions)
    call TriggerAddAction(t2, function MoveCancel)
    set t = null
    set t2 = null
  endfunction
  
endscope

EDIT: Screenshot in Olympics thread. Also, does this comply to JESP?
08-19-2008, 01:05 PM#2
saw792
48hr bump
08-19-2008, 02:31 PM#3
Themerion
I can see no obvious bad things with the code. You could use TimerUtils instead of CSData.
08-20-2008, 01:04 PM#4
Anitarf
Quote:
Originally Posted by saw792
Screenshot in Olympics thread. Also, does this comply to JESP?
No. It doesn't support multiple levels and isn't multiinstanceable.

Also, pausing units is lame, you should be stunning them.
08-20-2008, 01:14 PM#5
saw792
Umm are you sure it isn't multi-instanceable? I have a test map for it that has caused no problems with about 8 simultaneous casts from different players. Can you point out the problem areas?

Levels haven't been implemented yet.

Stun, ok that shall be done.
08-20-2008, 02:37 PM#6
Anitarf
The caster and cancel global variables can only work for one instance at once.
08-21-2008, 06:14 AM#7
saw792
Mm true... fixed now (might update it here later). Thanks Anitarf.