• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

String Distribution

Status
Not open for further replies.
Level 33
Joined
Mar 27, 2008
Messages
8,035
Well, this is actually more of a request rather than questioning, I wrote it here because I see there's a few people who'd like to help on Request Section :(
Famous spot is this section and the Triggers & Scripts.

Enough with that, let's get down to business

Okay, I'm in need of a spell request actually, its name is String Distribution

Here's the info about the spell:
1. Target Ground

2. When targeted, spawns <object> (you can use the none.mdx dummy unit) and from that object, two sources of lightning (acting as "string") will come out from the object and link itself to 2 random units in an 1000 AOE (max AOE check).

3. The total length of the string depends on the distance from Unit A -> Object -> Unit B. I'll draw diagram for this:

------------------------------------Object
-----------------------------------/-------\
-----------------------(500 range)/---------\(500 range)
---------------------------------/-----------\
------------------------------Unit A--------Unit B

--- = space divider
/ and \ = Lightning/String
Example Situation: From Unit A -> Object -> Unit B = 1000 range of string length.

4. If Unit A moves (Unit B is static), then Unit B is pulled equivalent to the distance of Unit A moves. Example: Unit A moves 200 range, Unit B will be pulled 200 range towards the Object.

5. If any unit moves too far away (Greater than or Equal to MaxStringLength (MaxStringLength = Total Distance from Unit A -> Object -> Unit B, assume it like vector) range), then than unit can't move anymore further than that.

6. If both unit is moving at the same speed, the string will have no movement but if Unit A is moving more than 30 movement speed than Unit B, the string will be moved 30 range per trigger loop Event (I bet it's 0.03).


NOTE: The "<value>" thing is my own decision to make what it is. Like that <object>, I have my own dummy to be used, but in your case, you can replace it with whatever you want including the none.mdx dummy unit. If you have any question, please ask :)

Reward: +3 rep.

(This is due to my big brother's request, I just need to show him that everything can be done with Warcraft III World Editor, are you with me, people ?)
 
Level 33
Joined
Mar 27, 2008
Messages
8,035
So this one affects unit A only? I mean, only unit A can affect the unit B's movement or Unit B can also affect unit A's movement?
Both unit can affect or be affected by the movement

Like, Unit A moves while Unit B is static, therefore Unit B is pulled towards the <object> with the speed of Unit A moving now

If Unit B moves while Unit A is static, therefore Unit A is pulled towards <object> with the speed of Unit B moving now

If both unit is moving and have the same speed, doesn't affect the string and the unit plays its Walk Animation but in the same place

If both unit is moving and Unit A > Unit B in movement speed, Unit B will be pulled equivalent to the difference of speed of Unit A and Unit B (also, Unit A will have its movement speed reduced to the difference of speed between the units)

Example: Unit A moves 300MS/second
Unit B moves 250MS/second
Unit B will be pulled 50MS/second
Unit A will move with 50MS/second

The difference acts as "weight" or "load" for the Unit A to "pull" something that moves.
Moved to requests forum.
I knew this would come :(
 
This is due to my big brother's request, I just need to show him that everything can be done with Warcraft III World Editor

this is not really about warcraft but about math
so here is some math for you:
  • Loop
    • Events
      • Time - Every 0.03 seconds of game time
    • Conditions
    • Actions
      • Custom script: local real z
      • Custom script: local real offset = 50.0
      • Custom script: local real x = GetUnitX(udg_u)
      • Custom script: local real y = GetUnitY(udg_u)
      • Custom script: local real x1 = GetUnitX(udg_u1)
      • Custom script: local real y1 = GetUnitY(udg_u1)
      • Custom script: local real x2 = GetUnitX(udg_u2)
      • Custom script: local real y2 = GetUnitY(udg_u2)
      • Custom script: local real dx1 = x1-x
      • Custom script: local real dy1 = y1-y
      • Custom script: local real dx2 = x2-x
      • Custom script: local real dy2 = y2-y
      • Custom script: local real a1 = Atan2(dy1, dx1)
      • Custom script: local real a2 = Atan2(dy2, dx2)
      • Custom script: local real r = 1000.0
      • Custom script: local real r1 = SquareRoot(dx1*dx1+dy1*dy1)
      • Custom script: local real r2 = SquareRoot(dx2*dx2+dy2*dy2)
      • Custom script: local real sum = r1+r2
      • Custom script: set r1 = r*r1/sum
      • Custom script: set r2 = r*r2/sum
      • Custom script: set x1 = x + Cos(a1)*r1
      • Custom script: set y1 = y + Sin(a1)*r1
      • Custom script: set x2 = x + Cos(a2)*r2
      • Custom script: set y2 = y + Sin(a2)*r2
      • Custom script: call MoveLocation(udg_zLoc, x, y)
      • Custom script: set z = GetLocationZ(udg_zLoc)
      • Custom script: call MoveLocation(udg_zLoc, x1, y1)
      • Custom script: call MoveLightningEx(udg_l1, true, x, y, z, x1, y1, GetLocationZ(udg_zLoc)+offset)
      • Custom script: call MoveLocation(udg_zLoc, x2, y2)
      • Custom script: call MoveLightningEx(udg_l2, true, x, y, z, x2, y2, GetLocationZ(udg_zLoc)+offset)
      • Custom script: call SetUnitX(udg_u1, x1)
      • Custom script: call SetUnitY(udg_u1, y1)
      • Custom script: call SetUnitX(udg_u2, x2)
      • Custom script: call SetUnitY(udg_u2, y2)
depending on the result you want to archive this might not be the best way because it is easier for the pulling unit to pull if it is closer to the <object> but it was the first way I could think of
if you are not happy with it you could try to implement something called "verlet integration" which is usually used for soft body physics though I could not get it to work with bigger objects and it had a few other flaws but it's pretty much what you are looking for and I guess it will work fine for it
here is an article about it:
http://www.gamasutra.com/resource_guide/20030121/jacobson_01.shtml
 

Attachments

  • String.w3x
    13.4 KB · Views: 139
Level 33
Joined
Mar 27, 2008
Messages
8,035
My rep comment to D4RK_G4ND4LF explains everything.

Anyway, anyone got better trigger than his ?

If not, this thread may end up as Solved thread (wait until I implement the system into my map, and see if it works)
If it doesn't, then moderator don't close this thread first because I got some other questions related to this thread (maybe the trigger is not working, etc, etc.)
 
Level 11
Joined
May 31, 2008
Messages
698
Here, i made my version of this
I know it isnt mui or even mpi, but i just wanted to get the basic idea down. It seems to work really well, and changing it to mui probly isnt too difficult


  • StringInit
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to (==) String Distribution
    • Actions
      • Lightning - Destroy lightning1[0]
      • Lightning - Destroy lightning2[0]
      • Set stringBool[0] = True
      • Set tempLoc[0] = (Position of (Triggering unit))
      • Set tempGroup = (Units within 1000.00 of tempLoc[0])
      • Unit Group - Remove (Triggering unit) from tempGroup
      • Unit Group - Pick every unit in tempGroup and do (Actions)
        • Loop - Actions
          • Set targetChoice[1] = (Picked unit)
          • Set tempLoc[1] = (Position of targetChoice[1])
          • Unit Group - Remove targetChoice[1] from tempGroup
          • Unit Group - Pick every unit in tempGroup and do (Actions)
            • Loop - Actions
              • Set targetChoice[2] = (Picked unit)
              • Set tempLoc[2] = (Position of targetChoice[2])
                • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                  • If - Conditions
                    • ((Distance between tempLoc[0] and tempLoc[1]) + (Distance between tempLoc[0] and tempLoc[2])) Less than or equal to (<=) 1000.00
                  • Then - Actions
                    • Set target1[0] = targetChoice[1]
                    • Set target2[0] = targetChoice[2]
                    • Set caster[0] = (Triggering unit)
                  • Else - Actions
              • Custom script: call RemoveLocation(udg_tempLoc[2])
          • Unit Group - Add targetChoice[1] to tempGroup
          • Custom script: call RemoveLocation(udg_tempLoc[1])
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • Multiple ConditionsOr - Any (Conditions) are true
              • Conditions
                • target1[0] Equal to (==) No unit
                • target2[0] Equal to (==) No unit
          • Then - Actions
            • Set stringBool[0] = False
            • Set caster[0] = No unit
          • Else - Actions
            • Set tempLoc[1] = (Position of targetChoice[1])
            • Lightning - Create a Magic Leash lightning effect from source tempLoc[0] to target tempLoc[1]
            • Set lightning1[0] = (Last created lightning effect)
            • Set tempLoc[2] = (Position of targetChoice[2])
            • Lightning - Create a Magic Leash lightning effect from source tempLoc[0] to target tempLoc[2]
            • Set lightning2[0] = (Last created lightning effect)
            • Custom script: call RemoveLocation(udg_tempLoc[1])
            • Custom script: call RemoveLocation(udg_tempLoc[2])
      • Custom script: call RemoveLocation(udg_tempLoc[0])

JASS:
function StringInitCond takes nothing returns boolean
    return udg_stringBool[0]
endfunction

function StringMove takes nothing returns nothing
    local unit caster = udg_caster[0]
    local unit target1 = udg_target1[0]
    local unit target2 = udg_target2[0]
    local real cx = GetUnitX(caster)
    local real cy = GetUnitY(caster)
    local real t1x = GetUnitX(target1)
    local real t1y = GetUnitY(target1)
    local real t2x = GetUnitX(target2)
    local real t2y = GetUnitY(target2)
    local real ms1 = GetUnitMoveSpeed(target1)*0.03
    local real ms2 = GetUnitMoveSpeed(target2)*0.03
    local real t1dx = t1x-cx
    local real t1dy = t1y-cy
    local real t2dx = t2x-cx
    local real t2dy = t2y-cy
    local real radius1 = SquareRoot(t1dx*t1dx + t1dy*t1dy)
    local real radius2 = SquareRoot(t2dx*t2dx + t2dy*t2dy)
    local real y1max
    local real x1max
    local real y2max
    local real x2max
    local real tempReal
    
    if ((radius1 + radius2) >= 1000) then
        set tempReal = ((radius1+radius2)-1000)/2
        set radius1 = radius1 - tempReal
        set radius2 = radius2 - tempReal
    if radius2 < 100 then
        set radius2 = 100
        set radius1 = 900
    endif
    if radius1 < 100 then
        set radius1 = 100
        set radius2 = 900
    endif
    
    if GetUnitCurrentOrder(target1) != String2OrderIdBJ("stop") then
        set radius1 = radius1 + ms1
        set radius2 = radius2 - ms1
    endif
    
    if GetUnitCurrentOrder(target2) != String2OrderIdBJ("stop") then
        set radius2 = radius2 + ms2
        set radius1 = radius1 - ms2
    endif
    
    set tempReal = RAbsBJ((radius1*radius1)/(1+(t1dy*t1dy)/(t1dx*t1dx)))
    set x1max = SquareRoot(tempReal)
    if t1dx < 0 then
        set x1max = -x1max
    endif
    set y1max = x1max*(t1dy/t1dx)
    
    set tempReal = RAbsBJ((radius2*radius2)/(1+(t2dy*t2dy)/(t2dx*t2dx)))
    set x2max = SquareRoot(tempReal)
    if t2dx < 0 then
        set x2max = -x2max
    endif
    set y2max = x2max*(t2dy/t2dx)
    
    if RAbsBJ(t1dx) >= RAbsBJ(x1max) then
        call SetUnitX(target1, cx + x1max)
    endif
    if RAbsBJ(t1dy) >= RAbsBJ(y1max) then
        call SetUnitY(target1, cy + y1max)
    endif
    
    if RAbsBJ(t2dy) >= RAbsBJ(y2max) then
        call SetUnitY(target2, cy + y2max)
    endif
    if RAbsBJ(t2dx) >= RAbsBJ(x2max) then
        call SetUnitX(target2, cx + x2max)
    endif
    
    endif
    
    set t1x = GetUnitX(target1)
    set t1y = GetUnitY(target1)
    set t2x = GetUnitX(target2)
    set t2y = GetUnitY(target2)
    
    call MoveLightning(udg_lightning1[0], TRUE, cx, cy, t1x, t1y)
    call MoveLightning(udg_lightning2[0], TRUE, cx, cy, t2x, t2y)
    
endfunction

//===========================================================================
function InitTrig_StringMoveJASS takes nothing returns nothing
    set gg_trg_StringMoveJASS = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_StringMoveJASS, 0.03 )
    call TriggerAddCondition( gg_trg_StringMoveJASS, Condition( function StringInitCond ) )
    call TriggerAddAction( gg_trg_StringMoveJASS, function StringMove )
endfunction

edit:
I also notice this isnt exactly what you were looking for because it just auto attaches 2 units to the caster and he can drag them around and stuff instead of them being attached to an object. But that should be easy enough to fix, the hardest part was figuring out the math xD
 

Attachments

  • StringDistributionFinal.w3x
    20 KB · Views: 32
Level 11
Joined
May 31, 2008
Messages
698
Well i made a finalized upgraded version, i know im double posting but i figured it should be in a different post so people can tell which map is the final one and stuff (if im wrong feel free to correct me)

  • BindingsInit
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to (==) Magic Bindings
    • Actions
      • Set MAX_LENGTH = 1000.00
      • Set IndexMax = (IndexMax + 1)
      • Set Index = IndexMax
      • Set tempLoc[0] = (Target point of ability being cast)
      • Unit - Create 1 Dummy for (Owner of (Triggering unit)) at tempLoc[0] facing (Random angle) degrees
      • Unit - Add a 20.00 second Generic expiration timer to (Last created unit)
      • Set dummy[Index] = (Last created unit)
      • Set tempGroup = (Units within 1000.00 of tempLoc[0])
      • Set tempGroup = (Units within 1000.00 of tempLoc[0] matching ((((Matching unit) is alive) Equal to (==) True) and (((Matching unit) is A ground unit) Equal to (==) True)))
      • Unit Group - Remove (Triggering unit) from tempGroup
      • Unit Group - Pick every unit in tempGroup and do (Actions)
        • Loop - Actions
          • Set targetChoice[1] = (Picked unit)
          • Set tempLoc[1] = (Position of targetChoice[1])
          • Unit Group - Remove targetChoice[1] from tempGroup
          • Unit Group - Pick every unit in tempGroup and do (Actions)
            • Loop - Actions
              • Set targetChoice[2] = (Picked unit)
              • Set tempLoc[2] = (Position of targetChoice[2])
                • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                  • If - Conditions
                    • ((Distance between tempLoc[0] and tempLoc[1]) + (Distance between tempLoc[0] and tempLoc[2])) Less than or equal to (<=) MAX_LENGTH
                  • Then - Actions
                    • Set target1[0] = targetChoice[1]
                    • Set target2[0] = targetChoice[2]
                    • Set caster[0] = (Triggering unit)
                  • Else - Actions
              • Custom script: call RemoveLocation(udg_tempLoc[2])
          • Unit Group - Add targetChoice[1] to tempGroup
          • Custom script: call RemoveLocation(udg_tempLoc[1])
      • Set target1[Index] = target1[0]
      • Set target2[Index] = target2[0]
      • Set caster[Index] = caster[0]
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • Multiple ConditionsOr - Any (Conditions) are true
              • Conditions
                • target1[0] Equal to (==) No unit
                • target2[0] Equal to (==) No unit
          • Then - Actions
            • Set bindBool[Index] = False
            • Set caster[Index] = No unit
          • Else - Actions
            • Set bindBool[Index] = True
            • Set tempLoc[1] = (Position of targetChoice[1])
            • Lightning - Create a Magic Leash lightning effect from source tempLoc[0] to target tempLoc[1]
            • Set lightning1[Index] = (Last created lightning effect)
            • Set tempLoc[2] = (Position of targetChoice[2])
            • Lightning - Create a Magic Leash lightning effect from source tempLoc[0] to target tempLoc[2]
            • Set lightning2[Index] = (Last created lightning effect)
            • Custom script: call RemoveLocation(udg_tempLoc[1])
            • Custom script: call RemoveLocation(udg_tempLoc[2])
      • Custom script: call RemoveLocation(udg_tempLoc[0])
      • Custom script: call DestroyGroup(udg_tempGroup)
      • Set targetChoice[2] = No unit
      • Set targetChoice[1] = No unit
      • Set target1[0] = No unit
      • Set target2[0] = No unit
JASS:
function BindInitCond takes integer a returns boolean
        return udg_bindBool[a]
endfunction

function BindCond2 takes integer a returns boolean
    return IsUnitAliveBJ(udg_dummy[a])
endfunction

function BindMove takes integer i returns nothing
    local real MAX_LENGTH = udg_MAX_LENGTH              //Maximum binding length (of both sides combined)
    local unit dummy = udg_dummy[i]
    local unit target1 = udg_target1[i]
    local unit target2 = udg_target2[i]
    local real cx = GetUnitX(dummy)
    local real cy = GetUnitY(dummy)
    local real t1x = GetUnitX(target1)
    local real t1y = GetUnitY(target1)
    local real t2x = GetUnitX(target2)
    local real t2y = GetUnitY(target2)
    local real ms1 = GetUnitMoveSpeed(target1)*0.03
    local real ms2 = GetUnitMoveSpeed(target2)*0.03
    local real t1dx = t1x-cx
    local real t1dy = t1y-cy
    local real t2dx = t2x-cx
    local real t2dy = t2y-cy
    local real radius1 = SquareRoot(t1dx*t1dx + t1dy*t1dy)
    local real radius2 = SquareRoot(t2dx*t2dx + t2dy*t2dy)
    local real y1max
    local real x1max
    local real y2max
    local real x2max
    local real tempReal
    
    if ((radius1 + radius2) >= MAX_LENGTH) then             //Checks if combined length of bindings is greater than the max length
        set tempReal = ((radius1+radius2)-MAX_LENGTH)/2     //Sets the length of each binding to make sure they equal to the max length
        set radius1 = radius1 - tempReal
        set radius2 = radius2 - tempReal
    if radius2 < 100 then                   //Makes sure units dont get too close to the binding object
        set radius2 = 100
        set radius1 = MAX_LENGTH - 100
    endif
    if radius1 < 100 then
        set radius1 = 100
        set radius2 = MAX_LENGTH - 100
    endif
    
    if GetUnitCurrentOrder(target1) != String2OrderIdBJ("stop") then    //Changes the binding length and allows one unit to "pull" the other
        set radius1 = radius1 + ms1
        set radius2 = radius2 - ms1
    endif
    
    if GetUnitCurrentOrder(target2) != String2OrderIdBJ("stop") then
        set radius2 = radius2 + ms2
        set radius1 = radius1 - ms2
    endif
    
    set tempReal = RAbsBJ((radius1*radius1)/(1+(t1dy*t1dy)/(t1dx*t1dx)))        //Calculates where the edge of the binding should be
    set x1max = SquareRoot(tempReal)                                            //and restricts the bound unit from going beyond that point
    if t1dx < 0 then
        set x1max = -x1max
    endif
    set y1max = x1max*(t1dy/t1dx)
    
    set tempReal = RAbsBJ((radius2*radius2)/(1+(t2dy*t2dy)/(t2dx*t2dx)))
    set x2max = SquareRoot(tempReal)
    if t2dx < 0 then
        set x2max = -x2max
    endif
    set y2max = x2max*(t2dy/t2dx)
    
    if RAbsBJ(t1dx) >= RAbsBJ(x1max) then           //Actually moves the bound unit, this keeps the unit from
        call SetUnitX(target1, cx + x1max)          //moving farther than the binding will allow it to
    endif
    if RAbsBJ(t1dy) >= RAbsBJ(y1max) then
        call SetUnitY(target1, cy + y1max)
    endif
    
    if RAbsBJ(t2dy) >= RAbsBJ(y2max) then
        call SetUnitY(target2, cy + y2max)
    endif
    if RAbsBJ(t2dx) >= RAbsBJ(x2max) then
        call SetUnitX(target2, cx + x2max)
    endif
    
    endif
    
    set t1x = GetUnitX(target1)             //Synchronizes the units position to make sure the lightning has the right placement
    set t1y = GetUnitY(target1)
    set t2x = GetUnitX(target2)
    set t2y = GetUnitY(target2)
    
    call MoveLightning(udg_lightning1[i], TRUE, cx, cy, t1x, t1y)       //Moves the lightning effect
    call MoveLightning(udg_lightning2[i], TRUE, cx, cy, t2x, t2y)
    
endfunction

function BindInit takes nothing returns nothing
    local integer a = udg_IndexMin
    loop
        exitwhen a > udg_IndexMax
        if BindCond2(a) then            //Checks to make sure the binding object is still alive
            if BindInitCond(a) then     //Checks to make sure the binding is still in effect
                call BindMove(a)        //Calls the function that keeps the bound units within the binding range
            endif
        elseif a == udg_IndexMin then
            set udg_IndexMin = udg_IndexMin + 1     //Keeps control of the index
        endif
        if (not(BindCond2(a))) then
            set udg_bindBool[a] = false             //Stops the binding if the binding object is dead
            call DestroyLightning(udg_lightning1[a])
            call DestroyLightning(udg_lightning2[a])
        endif
        set a = a+1
    endloop
endfunction

//===========================================================================
function InitTrig_BindingsMove takes nothing returns nothing
    set gg_trg_BindingsMove = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( gg_trg_BindingsMove, 0.03 )
    call TriggerAddAction( gg_trg_BindingsMove, function BindInit )
endfunction

I think that this is what you were looking for. It seems to work great for MUI and i havent found any bugs or sources of lag yet. It even works perfectly fine when you have 2 units bound to multiple wards around them (it actually looks pretty cool)

Hope this helps you! :)
 

Attachments

  • MagicBindings.w3x
    22.9 KB · Views: 85
Level 37
Joined
Mar 6, 2006
Messages
9,243
Wolfman, here are some comments to improve the triggering:
Since you're using JASS, why not use MoveLightningEx.
The looping timer is on even when the spell is not active.
IsUnitAliveBJ -> not IsUnitType(unit, UNIT_TYPE_DEAD)
String2OrderIdBJ -> OrderId
Delete: Set tempGroup = (Units within 1000.00 of tempLoc[0])
I woudn't use an array for temploc as you only need two indexes.
I would use some sort of dynamic indexing.
The max index isn't resetting.
 
Level 11
Joined
May 31, 2008
Messages
698
Would it really matter if the max index resets? because there is IndexMin and IndexMax, so the index for any varaible at any given time would have to be in between there, and the loops only run from min to max. So i guess my question is would it really matter if it was indexing from 9000 - 9002 instead of 0 - 2?

I was just too lazy to change the BJ's :p i dont think it really makes a difference here.
Yeah i missed that extra tempGroup. my bad xD
And tempLoc has 3 indexes, 0, 1, and 2. I think it had more when i was first attempting to make this but i just left it as an array

Are there any tutorials on how to make a dynamic indexing system? (im assuming that means that it resets the max index)

Thanks for the feedback though, i might consider implementing your improvements and submitting this to the spells section, which if i do of course defskull will get credit for the idea :)
 
Level 37
Joined
Mar 6, 2006
Messages
9,243
So i guess my question is would it really matter if it was indexing from 9000 - 9002 instead of 0 - 2?

The max index of arrays is 8191. You would eventually reach that, even if it is a theoretical situation.

And tempLoc has 3 indexes, 0, 1, and 2.
But you only need 0 and 1 if you think about it :) Better to use two non array variables, takes a bit less memory.

Are there any tutorials on how to make a dynamic indexing system? (im assuming that means that it resets the max index)

Hanky's dynamic indexing, spell database.
 
Status
Not open for further replies.
Top