• 🏆 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!

[Snippet] MUI DummyCasters

Level 29
Joined
Mar 10, 2009
Messages
5,016
MUIDummyCasters

Intro

Code

Demo

Changelogs

Bugs


- You may find the GUI version of this in >>> [thread=235236]MUI DummyCaster[GUI] [/thread]
- Creates only 1 dummy for instant spells like firebolt, carrion swarm, banish, etc
- Supports channel spells by creating another dummy to channel the spell like flamestrike, starfall, blizzard, etc
- You may control the point where the dummy is created and cast to a target or location where you can see the projectile is traveling

JASS:
library MUIDummyCasters /* v4.4a */ initializer Init /*
*************************************************************************************
*
*   by mckill2009
*
*************************************************************************************
*
*   Creates only 1 dummy for instant spells like firebolt, carrion swarm, banish, etc
*   Supports channel spells by creating another dummy to channel the spell like flamestrike, starfall, blizzard, etc
*   You may control the point where the dummy is created and cast to a target or location where you can see the projectile is traveling
*
*************************************************************************************
*
*   Credits
*
*       Bribe
*       -----------------------
*           For info about the 'Amov' ability remove
*
*       Nesthaurus
*       -----------------------
*           For his Comment header
*
*************************************************************************************
*
*   */ uses /*
*   
*       */ Table        /* [url]http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/[/url]
*
************************************************************************************
*   
*   Installation
*       - Copy ALL that is in the "MUIDummyCasters" folder to your map.
*       - Copy the Table library to your map.
*       - Replace the rawID of the DUMMY_CASTER if needed, you may view it via pressing CTRL+D in the object editor.  
*
************************************************************************************
*
*   GLOBALS
*
*/
globals
    /*
    *   Change the DUMMY_CASTER raw code if needed
    */
    private constant integer     DUMMY_CASTER = 'h000'
    /*
    *   Constant globals, never touch these!
    */
    private Table c
    private constant player             OWNER = Player(15)
    private group                   DummyTake = CreateGroup()
    private unit                        Dummy = null   
    /*
    *   TEST is for testing duh!, set to true to display how many
    *   dummies are created, set to false to disable it
    */
    private constant boolean TEST = true
    private integer DummyCount = 0
endglobals

/*
************************************************************************************
*
*   Functions
*   
*       function MDCCast takes player owningPlayer, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
*       function MDCCastToPoint takes player owningPlayer, real xDummy, real yDummy, real xTarget, real yTarget, integer abilityID, integer orderID, integer level returns nothing
*       function MDCCastToTarget takes  player owningPlayer, unit target, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
*
*   Function Parameter Description
*       player owningPlayer
*           - the owner of the casting unit
*
*       unit target (unit)
*           - the spelltarget, used only by MDCCastToTarget
*
*       real xDummy and yDummy
*           - the point/coordinate where the dummy will be placed
*
*       real xTarget and yTarget
*           - the point/coordinate where the dummy cast the spell
*           - used only by MDCCastToPoint
*
*       integer abilityID
*           - the raw code of of the ability being cast by the dummt caster
*
*       integer orderID
*           - the orderID of the abilityID
*
*       integer level
*           - the level of the abilityID
*
*
*/
//! textmacro ISSUE takes order
    local unit dum = GetDummyUnit()
    call UnitAddAbility(dum, abilityID)
    call SetUnitAbilityLevel(dum, abilityID, level)
    call SetUnitOwner(dum, owningPlayer, false)
    call SetUnitX(dum, xDummy)
    call SetUnitY(dum, yDummy)
    call Issue$order$
    set c[GetHandleId(dum)] = abilityID
    /*
    *   This is just a fail safe, in case the target is invulnerable, invisible or cannot be casted
    */
    if GetUnitCurrentOrder(dum)==0 then   
        call RefreshDummyUnit(dum)
    endif
    set dum = null
//! endtextmacro

private function RefreshDummyUnit takes unit d returns nothing        
    call SetUnitOwner(d, OWNER, false)
    call UnitRemoveAbility(d, c[GetHandleId(d)])
    call GroupAddUnit(DummyTake, d)
endfunction
    
private function GetDummyUnit takes nothing returns unit
    if FirstOfGroup(DummyTake)==null then
        set Dummy = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0) 
        call UnitRemoveAbility(Dummy,'Amov')
        static if TEST then
            set DummyCount = DummyCount + 1
            call BJDebugMsg("Dummies Created = "+I2S(DummyCount))
        endif
    else
        /*
        *   if group is not empty then it will pick the first dummy in the group
        *   I've used this above indexing to be.....different XD...
        */
        set Dummy = FirstOfGroup(DummyTake)
        call GroupRemoveUnit(DummyTake, Dummy)    
    endif
    return Dummy
endfunction
    
private function CastEnd takes nothing returns boolean
    /*
    *   Checks the caster if he belongs in this system instead of
    *   checking the unit type (which bugs)
    */
    if c.has(GetHandleId(GetTriggerUnit())) then
        call RefreshDummyUnit(GetTriggerUnit())
    endif
    return false
endfunction
        
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    /*
    *   Preloading only to avoid first time lag
    */
    local unit u = CreateUnit(OWNER, DUMMY_CASTER, 0, 0, 0)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition(t, Condition(function CastEnd))    
    set c = Table.create()
    call KillUnit(u)
    call RemoveUnit(u)
    set t = null
    set u = null
endfunction

/*
*   This the main core system, 3 API you may use
*/
function MDCCast takes player owningPlayer, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
    //! runtextmacro ISSUE("ImmediateOrderById(dum, orderID)")
endfunction

function MDCCastToPoint takes player owningPlayer, real xDummy, real yDummy, real xTarget, real yTarget, integer abilityID, integer orderID, integer level returns nothing
    //! runtextmacro ISSUE("PointOrderById(dum, orderID, xTarget, yTarget)")
endfunction

function MDCCastToTarget takes  player owningPlayer, unit target, real xDummy, real yDummy, integer abilityID, integer orderID, integer level returns nothing
    //! runtextmacro ISSUE("TargetOrderById(dum, orderID, target)")
endfunction

endlibrary

  • MDCCast
    • Events
      • Time - Every 0.10 seconds of game time
    • Conditions
    • Actions
      • Custom script: set udg_a = udg_a + 1.04
      • Custom script: call MDCCast(Player(1), 0+300*Cos(udg_a), 0+300*Sin(udg_a), 'AHtc', 852096, 1)
  • MDCCastToPoint CHANNEL
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Custom script: set bj_wantDestroyGroup=true
      • Unit Group - Pick every unit in (Units within 2000.00 of loc1 matching ((Unit-type of (Matching unit)) Equal to Peasant)) and do (Actions)
        • Loop - Actions
          • Custom script: call MDCCastToPoint(Player(0), 0,0, GetUnitX(GetEnumUnit()), GetUnitY(GetEnumUnit()), 'ACfs', 852488, 1)
JASS:
scope LTest initializer init

private function Actions takes nothing returns nothing
    local player p = Player(0)
    local unit target
    local group g = CreateGroup()
    call GroupEnumUnitsInRange(g, 0,0, 2500, null)    
    loop
        set target=FirstOfGroup(g)
        exitwhen target==null
        if IsUnitEnemy(target,p)then
            call MDCCastToTarget(p, target, 0,0,'ACfb',852231,1)  
        endif
        call GroupRemoveUnit(g,target)
    endloop
    call DestroyGroup(g)
    set g = null
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterTimerEvent(t, 1.00, true)
    call TriggerAddAction(t, function Actions)
    set t = null
endfunction

endscope

v4.4a
- Fixed a bug when checking the unit type if spell is finished.

v4.4
- LUA object merger removed
- Struct API's replaed by functions
- RegisterPlayerPlayerUnit requirement removed
- Readable functions
- Preloading dummy added

v4.3
- Fixed a very large bug that everytime the spell ends, ALL casters will be Player(15)
- Lua object name changed from 'dumy' to 'xcvb' and has a warning message inside the code only

v4.2
- Changed to struct syntax
- Added LUA object creator for dummy
- Added a fail safe cast in case the target is not targetable
- Codes and varibales reduced

v4.1
- Added ForGroup looper instead of Timer

v4.0
- Removed TimerUtils and IsUnitChanneling
- Added Hashtable to save the ability of the dummy then remove it
- Added END CAST code

v3.0a
- Fixed unithandle leak
- API explanations added

- Bugs if the MDCCastToPoint and MDCCastToTarget is unreachable.
- Bugs when checking the unit type if spell is finished (fixed).
 

Attachments

  • MUIDummyCasters.w3x
    31.9 KB · Views: 116
Last edited:
Level 7
Joined
Apr 27, 2011
Messages
272
Level 29
Joined
Mar 10, 2009
Messages
5,016
@jim
no, I want the text to be displayed in debug mode or not, so that the user can see it 100%
if he made the input wrong...

@Alain
as you have said "for instant spells", well this is not ONLY for instant spells but for
multiple instant and non-instant spells, the thing is there are some spells which is not
instant like FlameStrike which has a delay, and btw your looping code doesnt work coz
even though the casting time is 0, loops are faster to execute, not to mention the
turning around of the dummy which takes time...

but I may however put this in 3 API's coz right now I dont like the long code parameters...
 
Level 7
Joined
Apr 27, 2011
Messages
272
my looping code is only for multicasting instant spells. (it works)

btw what i meant earlier was my code only uses one dummy caster when casting MULTIPLE times (for instant spells) and recycles dummy casters when casting MULTIPLE times (for channeled/timed spells). try it. it is faster and more efficient since it doesn't use timers and attaching. i meant my post earlier specifically for my libs "CastOnAreaLvl" function.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016

uses one dummy caster
I dont think so, the 'w' is always 0 at first so if multiple times at first then it will create also multiple times...
The caster also is at the location of the target, what if the user wants it as a missile unit-target-spell, from angle of
caster and target, surely the system doesnt support that...

and again, my system supports instant and non-instant spells alike :)...

anyway v2.0 is out, the thing is, this system is very simple to use with only 3 API's and doesnt use indexing...
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
but it doesnt support missile-like target, from source to destination...

His uses way fewer dummy units to achieve the same thing

some, yes but not all the time...

sample, Blizzard...
  • Channeled
    • Events
      • Time - Every 1.00 seconds of game time
    • Conditions
    • Actions
      • Custom script: set bj_wantDestroyGroup=true
      • Unit Group - Pick every unit in (Units within 2000.00 of loc1 matching ((Unit-type of (Matching unit)) Equal to Peasant)) and do (Actions)
        • Loop - Actions
this creates only 16 units with a 1 sec delay...
  • Custom script: call HitPoint(Player(0), 0,0, GetUnitX(GetEnumUnit()), GetUnitY(GetEnumUnit()), 'AHbz', 852089, 1)
this creates 56 units...
  • call CastOnArea(AllocateCaster(Player(0)), GetUnitX(GetEnumUnit()), GetUnitY(GetEnumUnit()), 10, 'AHbz', 852089, false)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I'll add this to my list of things to test out, I guess. I'd rather have one system for this stuff because it's bad enough having to require a casting system in the first place.

Having support for instant abilities is the main thing, because you add the ability, issue the order, remove the ability, that way the ability will fire from the current thread allowing you to issue multiple casts from a single dummy within one thread.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
having ONE dummy for all instant spell isnt gonna work, I've tested it like this,
and the dummy unit wont fire, even if the cooldown is negative, the spell
will take time to fire, not to mention the 'turning' around of the dummy unit,
loops to remove the spell takes almost instant, unless the spell is faster than loops to cast...
JASS:
function ActionsL takes nothing returns nothing
    local unit dummy = CreateUnit(Player(0), 'h000', 0,0,0)
    local unit target
    local group g = CreateGroup()
    call GroupEnumUnitsInRange(g, 0,0, 2500, null)    
    loop
        set target=FirstOfGroup(g)
        exitwhen target==null
        call GroupRemoveUnit(g,target)
        if IsUnitEnemy(target,Player(0))then
            call BJDebugMsg("not working!")
            call UnitAddAbility(dummy,'ACfb')
            call IssueTargetOrderById(dummy,OrderId("Firebolt"),target)
            call UnitRemoveAbility(dummy,'ACfb')
        endif
    endloop
    call DestroyGroup(g)
    set g = null
    set dummy = null
endfunction

function InitTrig_LoopTestFAIL takes nothing returns nothing
    set gg_trg_LoopTestFAIL = CreateTrigger()
    call TriggerRegisterTimerEvent(gg_trg_LoopTestFAIL, 1.00, false)
    call TriggerAddAction( gg_trg_LoopTestFAIL, function ActionsL)
endfunction
 
Level 7
Joined
Oct 11, 2008
Messages
304
JASS:
API:
call HitTarget(.........
call HitPoint(.........
call HitNone(.........
- Really self explanatory, see the code below.

Really?

'.........' is not a 'self explanatory', also, people will not read your code just to know the API, this is just stupid when I just want to know what is the arguments of your functions.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
@Laiev
its not really a big deal to scroll down the code to read unless you're lazy or worst cant read :)...

this and the others are really readable, as Im not reffering each arguments merely by one or two letters...
function HitTarget takes player owningplayer, unit target, real xSource, real ySource, integer abilityID, integer orderID, integer level returns nothing
 
I think you will be the one deemed as the lazy one if you don't put it on the header... It's a convention that the API of public systems here are written completely at the header/documentation... If ur not writing them completely, don't write the API at all... Not to say that it can lessen the risk of accidental modifications to the system code when some non-coder looks at it...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
The Lua code is unacceptable in its current state as it is unsafe. If you want to do object creation with Lua, you need to use the Lua framework as it supports safe object creation.

If it is to remain in its current state, I recommend deleting it and sticking with the map that has the object in it.

edit
Now that I take a peek at the actual content of the system, the code looks like madness atm... someone is going to have to thoroughly go through it. If all you need is regular dummies, then using a dummy recycler and adding the abilities to those dummies would be a much better/smarter option. The algorithms they use are much better than the attempts at recycling you make in this resource. However, at that point, using an mui spellcaster would be altogether pointless as the simplicity in using a dummy system wouldn't necessitate the need of an entire resource dedicated to MUI dummy spellcasters. Perhaps if you did a small snippet of a few helper functions, that might be smarter ^_^.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
The Lua code is unacceptable in its current state as it is unsafe
Define and explain 'unsafe'?...

But, I dont think so as I've tested it many times and I didnt saw an 'unsafe' situation...Im not planning to use other's
dummy recycler coz I want this to get the dummy not by indexing but by using the FirstOfGroup, if all spells
used are instant cast it only creates 1 dummy, else it will get another one, in all, nothing is wrong with this...
If you want me to replace the LUA with just making a preset dummy, well sorry I wont :)...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
FirstOfGroup is much slower. Furthermore, dummies may need to face in a certain direction for effects.

Your Lua is unsafe as it may overwrite existing objects, so change it or delete it, that has been the standard policy since Lua file header was written. Nobody, including Purge, has been exempt from this, and there is no reason for you to suddenly be exempt from it. Keep in mind that Purge wrote an entire tutorial on object generation with Lua.

If you'd prefer the resource be gy'd, that's fine too.

Choice 1: delete the Lua
Choice 2: go to preset dummy in map
Choice 3: change the Lua
Choice 4: gy

Pick one =), they are not negotiable.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Except you're not a moderator.
mckill2009 i suppose you should just use a more random rawcode, like 'd&1%' (if it's a valid one i don't care to check), 'dumy' is likely to be already used.

uh huh, those have been the standards on THW and no exceptions have been made except to those old resources made by now inactive members that were already approved like Earth-Fury's thing.

This should be using a counter + unit array (array stack) for its recycling anyways... much smarter than using a group : \.

edit
This should be in a module
JASS:
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(t, function thistype.instantCastEnd) 
        set t = null
    endmethod
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I still don't see the problem if he choices a more "random" id, it's just one rawcode for god sake ...
And we are not on wc3c.net with lame coding standards (yes i consider it lame, or at very least really overkill for a such simple thing)
Anyway, nothing forbids him to provide a map where there is this script disabled and already the ability.

That should be a part of the submission rule btw, i mean include a demo map all the times.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Anyway, nothing forbids him to provide a map where there is this script and already the ability.

I told him to do exactly that... lol

Choice 1: delete the Lua
Choice 2: go to preset dummy in map
Choice 3: change the Lua
Choice 4: gy

edit
Unsafe Lua code overwrites objects with no warning, which is why it hasn't been allowed and why I spent all that effort doing the LUA_FILE_HEADER thing.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
You were not clear about the script, for me with or without a map his lua script was a no-no, but it's fine if you're ok with it (even if you're a simple peon like me)

oh no, with or without the map, the Lua script is a no-no for the reason above your post. Perhaps if he added a warning This Script May Overwrite Object(s) (units, items, abilities, doodads, destructables, upgrades, tech) From Your Map Without Warning, Use At Your Own Risk. I'd be fine with it.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Can i laugh about the "unsafe" part ?
As usual you are over exaggerating an unlikely "problem" which can be fixed really quickly and easily.

Anyway i'm not a moderator neither, so let's Mag and Bribe decide about it.

I would just be sad if there is so "high" coding standard here.
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
'dumy' is used by 1 of Jesus4Lyf's resources and one of Bribe's resources, so it's a no-no.
Seriously though, just upload a map with the object in it.
I really dont see any problem with that except merely to accidentally overwrite certain objects in OE,
this is the same as copying objects from one map to the other coz the code will surely bug by sharing
the same ID if you input the 'non-unique' rawID...

How bout 'qwer' or 'ftwh' or 'jfk0', is it used?...what about if I put a warning?

This should be using a counter + unit array (array stack) for its recycling anyways... much smarter than using a group : \.
And again, Im NOT using indexing about this part, it maybe slower but no way Im gonna change this to indexing...

Anyway, nothing forbids him to provide a map where there is this script and already the ability.
I already did that in the past versions, but decided to make it in LUA :)...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
what about if I put a warning?

We said a warning would be fine and even gave suggested warnings ^)^.

And again, Im NOT using indexing about this part, it maybe slower but no way Im gonna change this to indexing...

And again, Im NOT using indexing about this part, it maybe slower but no way Im gonna change this to indexing...

Where did I say to use indexing? I said to use an array stack over a group.

JASS:
integer count = 0
unit array units

function Push takes unit u returns nothing
    set units[count] = u
    set count = count + 1
endfunction
function Pop takes nothing returns unit
    set count = count - 1
    return units[count]
endfunction

edit
Ok, looked at some more of the code and have another fix for you -> http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/

Code to be fixed using RegisterPlayerUnitEvent
JASS:
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(t, function thistype.instantCastEnd) 
        set t = null

And no, not even I have gotten away with not using that resource for my own submissions. Simply not using it has made some of my resources sit in submissions until I did use it. Bribe has been very firm about using it.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I called 'count' as "indexing" same as stack :)...

And why are you determined to use a group when it's slower and using an array stack is literally 2 extra lines of code >.>.

It's a better approach to the problem as it saves a handle and it's faster.

edit
also be sure to look at my posts edits, because you still have the problems outlined in that post + your messed up initializer (needs to be in a module).

edit
Now that I see that you are using a hashtable, you have to use your own unit indexing scheme and pass integers around instead of units as handling integers would save the hashtable, save the group, reduce variables, and increase speed.
 
Top