• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Dummy Unit - Submission

Status
Not open for further replies.
Part 4:
JASS:
function GroupCon takes nothing returns boolean
   return IsUnitEnemy(GetFilterUnit(), bj_forceRandomCurrentPick)
endfunction

function TimerExpires takes nothing returns nothing
   local timer t = GetExpiredTimer ()
   local integer timerID =  GetHandleId( t )
   local unit u = LoadUnitHandle(udg_Hash, timerID, 0)
   local unit source = LoadUnitHandle(udg_Hash, timerID, 5)
   local unit first
   local real angle = LoadReal(udg_Hash, timerID, 1)
   local real speed = LoadReal(udg_Hash, timerID, 2)
   local integer timerExecutions = LoadInteger(udg_Hash, timerID, 3)
   local real damage = LoadReal(udg_Hash, timerID, 4)
   local real x = GetUnitX(u) + speed * Cos(angle)
   local real y = GetUnitY(u) + speed * Sin(angle)
   local group targets = CreateGroup()
   local boolexpr filter = Condition (function GroupCon)
   set bj_forceRandomCurrentPick = GetOwningPlayer(source)
   call SetUnitPosition(u, x, y)
   call GroupEnumUnitsInRange(targets, x, y, 200, filter)
   loop
       set first = FirstOfGroup (targets)
       exitwhen (first == null)
       call GroupRemoveUnit(targets, first)
       call UnitDamageTarget(source, first, damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
   endloop
   //Not Last Intervall?
   if timerExecutions != 0 then
       call SaveInteger(udg_Hash, timerID, 3, timerExecutions - 1)
   else
       call KillUnit(u)
       call FlushChildHashtable(udg_Hash, timerID)
       call PauseTimer(t)
       call DestroyTimer(t)
   endif
   set source = null
   set t = null
   set u = null
   call DestroyGroup(targets)
   call DestroyBoolExpr(filter)
   set filter = null
   set targets = null
endfunction

function PressESC takes nothing returns nothing
   local timer t
   local integer timerID
   local unit u
   local integer loopA = 0
   local real angle =  0
   local real angleInc = bj_PI/4
   local real speed = 1050 * 0.03125
   local real damage = 100 * 0.03125
   local integer timerExecutions = R2I (1000 / speed)
   local real x = GetUnitX(udg_Taure)
   local real y = GetUnitY(udg_Taure)
   loop
       exitwhen loopA > 7
       set t = CreateTimer()
       set timerID =  GetHandleId(t)
       set u = CreateUnit(Player(0), 'h000' , x, y, angle * bj_RADTODEG)
       call SaveUnitHandle(udg_Hash, timerID, 0, u)
       call SaveReal(udg_Hash, timerID, 1, angle)
       call SaveReal(udg_Hash, timerID, 2, speed)
       call SaveInteger(udg_Hash, timerID, 3, timerExecutions)
       call SaveReal(udg_Hash, timerID, 4, damage)
       call SaveUnitHandle(udg_Hash, timerID, 5, udg_Taure)
       call SaveEffectHandle(udg_Hash, timerID, 6,null)
       call TimerStart(t, 0.03125, true, function TimerExpires)
       set loopA = loopA + 1
       set angle = angle + angleInc
   endloop
   set t = null
   set u = null
endfunction

//===========================================================================
function InitTrig_DummyUnit takes nothing returns nothing
   local trigger ESC = CreateTrigger(  )
   set udg_Hash = InitHashtable()
   set udg_Taure = CreateUnit(Player(0), 'Otch' , 0, 0, 270)
   call TriggerRegisterPlayerEventEndCinematic( ESC, Player(0) )
   call TriggerAddAction( ESC, function PressESC )
   set ESC = null
endfunction
Submitting Dummy - Unit
Edit: First upload is Part 4

Part 2:
JASS:
function PressESC takes nothing returns nothing
   local integer loopA = 0
   local real angle = 0
   local real x
   local real y
   call SetUnitPosition(udg_Dummy, GetUnitX(udg_Taure), GetUnitY(udg_Taure))
   loop
       exitwhen loopA > 7
       set x = GetUnitX(udg_Taure) + Cos(angle * bj_DEGTORAD)
       set y = GetUnitY(udg_Taure) + Sin(angle * bj_DEGTORAD)
       call SetUnitFacing(udg_Dummy, angle)
       call IssuePointOrder(udg_Dummy, "shockwave", x, y)
       set loopA = loopA + 1
       set angle = angle + 45
       call TriggerSleepAction( 0.02 )
   endloop 
 
endfunction

//===========================================================================
function InitTrig_DummyUnit takes nothing returns nothing
    local trigger ESC = CreateTrigger(  )
    set udg_Taure = CreateUnit(Player(0), 'Otch' , 0, 0, 270) 
   set udg_Dummy = CreateUnit(Player(0), 'h000' , 0, 0, 270)
   call UnitAddAbility(udg_Dummy, 'A000')
    call TriggerRegisterPlayerEventEndCinematic( ESC, Player(0) )
    call TriggerAddAction( ESC, function PressESC )
endfunction
 

Attachments

  • Jass Class 4 - Dummy Unit.w3x
    19.1 KB · Views: 52
  • Jass Class 4 - Dummy Unit - Part 2.w3x
    18 KB · Views: 60
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
JASS:
local boolexpr filter = Condition (function GroupCon)
The value of this boolexpr is constant. Would it be better to make it a global variable that is initialized only once?
Then you wouldn't have to create and destroy it every time.

I think it would be better to use distance instead of timerExecutions, so you can better set the maximum distance.
Your solution works as well, but I think distance would be more intuitive.
 
Thanks for your Feedback, its nice to discuss and analyze different apporaches.
I think it would be better to use distance instead of timerExecutions, so you can better set the maximum distance.
Your solution works as well, but I think distance would be more intuitive.
I like countdowns they perform good, cause of the leighter checks.

intuitive: That's a question how you see the whole situation.
Yours: Walk x m / Beeing Pushed until x m are reached.
Mine: You get Pushed x times​

With distances you could code it more precise, to be more precise with distance and constant speed you would have to compare speed and remaining distance each timeout or, if you skip the (speed > remaining Distance) you missle will fly to far in the current siutation: Rest ( distance / speed ) != 0 as in this code. Mine does fly to short in most cases: Inaccuraty in current Speed = [0 -> 32,8125].
Distances logic witout (constant speed > remaining Distance) check has a inaccuracy of Rest (distance/Speed) too, but as too much.

Therefore 3 Options: inaccuracy in - (with Countdown), in + (with simple Distance) or additional Checks.

The value of this boolexpr is constant. Would it be better to make it a global variable that is initialized only once?
It would be, but i use simple Jass and basic World editor therefore no access to global block and boolexpr is not createable with variable Editor. The only option i know is Hashtable with 2 constant indexes, I could do that if you like.
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
intuitive: That's a question how you see the whole situation.
Yours: Walk x m / Beeing Pushed until x m are reached.
Mine: You get Pushed x times
Exactly. It comes down to personal preference.
With distances you could code it more precise, to be more precise with distance and constant speed you would have to compare speed and remaining distance each timeout or, if you skip the (speed > remaining Distance) you missle will fly to far in the current siutation: Rest ( distance / speed ) != 0 as in this code. Mine does fly to short in most cases: Inaccuraty in current Speed = [0 -> 32,8125].
Distances logic witout (constant speed > remaining Distance) check has a inaccuracy of Rest (distance/Speed) too, but as too much.

Therefore 3 Options: inaccuracy in - (with Countdown), in + (with simple Distance) or additional Checks.
In most cases distance should not be important to be 100% accurate, so I guess it's better to avoid additional checks, unless the system must be perfectly accurate.

It would be, but i use simple Jass and basic World editor therefore no access to global block and boolexpr is not createable with variable Editor. The only option i know is Hashtable with 2 constant indexes, I could do that if you like.
It was just an idea. I don't think it makes much of a difference and storing it in a hashtable seemes somewhat weird.
Which program do you use to write your code? The basic world editor has a pretty bad text editor in my opinion.
 
The timeouts in Part 2 aren't wanted, try to find a solution!

==
The boolexpr creation/destruction is not really needed, just write it out as direct value.
I'm also more a fan of direct calculation with travaled distance, but your counter works here, too.
One global group could be used which is never destroyed, instead of local creation and recycling. But that's only a little optimization.
Instead of using an extra filter func, you could just filter with a custom function where you give parameters, or write an extra if-case inside the loop, then also the global bj player variable would not be needed.
call TriggerRegisterPlayerEventEndCinematic( ESC, Player(0) )
^The bj feels not needed.
 
Updated Part 4 and 2.

Part 4: does not use boolexp anymore, uses a global Group, the bj Event was replaced.
JASS:
function TimerExpires takes nothing returns nothing
   local timer t = GetExpiredTimer ()
   local integer timerID =  GetHandleId( t )
   local unit u = LoadUnitHandle(udg_Hash, timerID, 0)
   local unit source = LoadUnitHandle(udg_Hash, timerID, 5)
   local unit first
   local real angle = LoadReal(udg_Hash, timerID, 1)
   local real speed = LoadReal(udg_Hash, timerID, 2)
   local integer timerExecutions = LoadInteger(udg_Hash, timerID, 3)
   local real damage = LoadReal(udg_Hash, timerID, 4)
   local real x = GetUnitX(u) + speed * Cos(angle)
   local real y = GetUnitY(u) + speed * Sin(angle)
   local player  owner = GetOwningPlayer(source)
   call SetUnitPosition(u, x, y)
   call GroupClear( udg_Targets )
   call GroupEnumUnitsInRange(udg_Targets, x, y, 200, null)
   loop
       set first = FirstOfGroup (udg_Targets)
       exitwhen (first == null)
       call GroupRemoveUnit(udg_Targets, first)
       if IsUnitEnemy(first, owner) and GetWidgetLife(first) >= 0.45 then
           call UnitDamageTarget(source, first, damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
       endif
   endloop
   //Not Last Intervall?
   if timerExecutions != 0 then
       call SaveInteger(udg_Hash, timerID, 3, timerExecutions - 1)
   else
       call KillUnit(u)
       call FlushChildHashtable(udg_Hash, timerID)
       call PauseTimer(t)
       call DestroyTimer(t)
   endif
   set source = null
   set t = null
   set u = null
   set owner = null
endfunction

function PressESC takes nothing returns nothing
   local timer t
   local integer timerID
   local unit u
   local integer loopA = 0
   local real angle =  0
   local real angleInc = bj_PI/4
   local real speed = 1050 * 0.03125
   local real damage = 100 * 0.03125
   local integer timerExecutions = R2I (1000 / speed)
   local real x = GetUnitX(udg_Taure)
   local real y = GetUnitY(udg_Taure)
   loop
       exitwhen loopA > 7
       set t = CreateTimer()
       set timerID =  GetHandleId(t)
       set u = CreateUnit(Player(0), 'h000' , x, y, angle * bj_RADTODEG)
       call SaveUnitHandle(udg_Hash, timerID, 0, u)
       call SaveReal(udg_Hash, timerID, 1, angle)
       call SaveReal(udg_Hash, timerID, 2, speed)
       call SaveInteger(udg_Hash, timerID, 3, timerExecutions)
       call SaveReal(udg_Hash, timerID, 4, damage)
       call SaveUnitHandle(udg_Hash, timerID, 5, udg_Taure)
       call SaveEffectHandle(udg_Hash, timerID, 6,null)
       call TimerStart(t, 0.03125, true, function TimerExpires)
       set loopA = loopA + 1
       set angle = angle + angleInc
   endloop
   set t = null
   set u = null
endfunction

//===========================================================================
function InitTrig_DummyUnit takes nothing returns nothing
   local trigger ESC = CreateTrigger(  )
   set udg_Hash = InitHashtable()
   set udg_Taure = CreateUnit(Player(0), 'Otch' , 0, 0, 270)
   call TriggerRegisterPlayerEvent(ESC, Player(0), EVENT_PLAYER_END_CINEMATIC)
   call TriggerAddAction( ESC, function PressESC )
   set ESC = null
endfunction

JASS:
function PressESC takes nothing returns nothing
   local integer loopA = 0
   local real angle = 0
   local real x
   local real y
   local real xTaure =GetUnitX(udg_Taure)
   local real yTaure =GetUnitY(udg_Taure)
   call SetUnitPosition(udg_Dummy, xTaure, yTaure)
   loop
       exitwhen loopA > 7
       set x = xTaure + 50 * Cos(angle * bj_DEGTORAD)
       set y = yTaure + 50 * Sin(angle * bj_DEGTORAD)
       call IssuePointOrder(udg_Dummy, "shockwave", x, y)
       set loopA = loopA + 1
       set angle = angle + 45
   endloop
endfunction

//===========================================================================
function InitTrig_DummyUnit takes nothing returns nothing
   local trigger ESC = CreateTrigger(  )
   set udg_Taure = CreateUnit(Player(0), 'Otch' , 0, 0, 270)
   set udg_Dummy = CreateUnit(Player(0), 'h000' , 0, 0, 270)
   call UnitAddAbility(udg_Dummy, 'A000')
   call TriggerRegisterPlayerEvent(ESC, Player(0), EVENT_PLAYER_END_CINEMATIC)
   call TriggerAddAction( ESC, function PressESC )
endfunction

Seems like warcraft didn't see a difference in "x = xTaure + Cos(angle * bj_DEGTORAD)
and xtaure". Therefore all 8 shockwaves were shot in the same direction. Now that i use 50 times cos and sin it works.
Edit: I tested this values and it starts working with 21 * cos | 21 * sin, below shots all waves in one direction (Only integer angle multipliers were tested).

Map Files will be soon updated too.
Edit: Decided to reupload them in this post.
 

Attachments

  • Jass Class 4 - Dummy Unit - Part 2.w3x
    17.8 KB · Views: 55
  • Jass Class 4 - Dummy Unit.w3x
    19.1 KB · Views: 62
Last edited:
Instead of increasing angle with 45 + converting rad to deg each time, + bj_PI / 4 could be used to increase angle.
(I then mentioned you did it in the latter code, so probably just haven't changed it)

Pretty solid, good.

full
 
Status
Not open for further replies.
Top