I've had a search around the forums and haven't quite found what I am after.
I am creating a TD map and there is an ability that I want to make but for the life of me, can't figure it out.
Basically I want a Tower to emit a beam to another Tower, then any mob that passes through that beam, gets dealt X damage.
Kind of like a laser fence, but spans between the chosen towers.
I had found some threads in regards to linking a slave unit to a master which would be good to determine host Tower to receiving tower.
But I don't know how to make this variable beam deal damage to anything that crosses it.
.: EDIT :.
Just had a thought about connecting the towers.
Is there a way to make a Tower auto connect and beam to another Tower of the same type WITHIN its range.
So if you had 3 in the range of each other, all would have beams shooting to them?
As for finding units inside the beam, that is just math.
First of all I made a region that covers the square between the two towers.
Then every x seconds, I pick every unit in this region, and check if it is within the rotated square where the beam is going.
This is done by splitting the square up into triangles, and checking if the unit is inside one of those, and to do that...
Point In Triangle
Options: Function
Return Type: Boolean
Parameters
point = No Point <Point>
s1 = No Point <Point>
s2 = No Point <Point>
s3 = No Point <Point>
Grammar Text: Point In Triangle(point, s1, s2, s3)
Hint Text: (None)
Custom Script Code
Local Variables
i = 0 <Integer>
j = 0 <Integer>
v = No Point <Point[2]>
dot = 0.0 <Real[1][2]>
d = 0.0 <Real>
x = 0.0 <Real>
y = 0.0 <Real>
Actions
Variable - Set v[0] = (Subtract Points(s3, s1))
Variable - Set v[1] = (Subtract Points(s2, s1))
Variable - Set v[2] = (Subtract Points(point, s1))
General - For each integer i from 0 to 1 with increment 1, do (Actions)
Actions
General - For each integer j from 0 to 2 with increment 1, do (Actions)
Actions
Variable - Set dot[i][j] = (Dot(v[i], v[j]))
Variable - Set d = ((dot[0][0] * dot[1][1]) - (dot[0][1] * dot[0][1]))
Variable - Set x = (lv_dot[1][1] * lv_dot[0][2] - lv_dot[0][1] * lv_dot[1][2]) / lv_d
Variable - Set y = (lv_dot[0][0] * lv_dot[1][2] - lv_dot[0][1] * lv_dot[0][2]) / lv_d
General - Return (lv_x >= 0) && (lv_y >= 0) && (lv_x + lv_y <= 1)
SubtractPoints({x1, x2, x3}, {y1, y2, y3}) just returns {x1 - y1, x2 - y2, x3 - y3}, and Dot(x, y) returns the dot product betwen the two "vectors".
To make them auto connect to nearby towers, just make a trigger that fires when one of your towers are created. Then you search for nearby towers, and for each of those, you do the above.
Awesome stuff! Cheers for the reply... I will definitely have to have a play with that and see what I can come up with.
My only concern is that you mention a region trigger... This really wouldn't work if the tower placement wasn't fixed.
IE if the player places a tower, how would there be a region trigger? Or can you add those with tower placement?
There are triggers to create a region at runtime. You can also add an event such as a unit enters region at runtime, but that is not how I use it. I create the smallest square region that contain both towers (at runtime), and then I periodically pick every enemy unit in this region, and deal damage to them if they are inside this square im talking about.
Could I however make the Tower fire arc of 360deg, then when a unit comes in its range, it then does the trigger to see if the unit is crossing its beam?
I dont see how it wouldn't work for a td map.. I dont think you understand me.
Ill try and post my trigger.
Beam Tower Link
Events
Unit - Any Unit uses Link at Generic3 - Execute stage (Ignore shared abilities)
Local Variables
distance = 0.0 <Real>
damage = 0.0 <Real>
region = No Region <Region>
Conditions
Actions
//Make the units face eachother (not important for the effect ur after)
Unit - Make (Triggering unit) face (Position of (Triggering ability target unit)) over 0.0 seconds
Unit - Make (Triggering ability target unit) face (Position of (Triggering unit)) over 0.0 seconds
//This is an effect that will cause some actors to make a beam between the two towers
Environment - Execute Beam Tower Persistent on (Triggering unit) from (Triggering ability target unit)
//I want the damage to be a function of the distance between the towers.
//Since getting the distance involves taking the squareroot, which is a bit slow, I save this to a varible
Variable - Set distance = (Distance between (Position of (Triggering unit)) and (Position of (Triggering ability target unit)))
//I made this function that will return a new region if I give it the center, width and height
General - Custom Script: lv_region = gf_RegionFromPoint(
gf_AvgPoints(UnitGetPosition(EventUnit()), UnitGetPosition(EventUnitTargetUnit())),
(AbsF((PointGetY(UnitGetPosition(EventUnit())) - PointGetY(UnitGetPosition(EventUnitTargetUnit())))) + 2.0),
(AbsF((PointGetX(UnitGetPosition(EventUnit())) - PointGetX(UnitGetPosition(EventUnitTargetUnit())))) + 2.0));
General - While (Conditions) are true, do (Actions)
Conditions
((Triggering unit) is alive) == true
((Triggering ability target unit) is alive) == true
Actions
Variable - Set damage = 20.0
Variable - Modify damage: / distance
//Like I talked about.. Pick each enemy unit in the region
Unit Group - Pick each unit in (Units in region having alliance Enemy with player (Triggering player) matching Excluded: Missile, Dead, Hidden, with at most Any Amount) and do (Actions)
Actions
General - If (Conditions) then do (Actions) else do (Actions)
If
//And if the point is in the square as illustrated by the picture, damage it
(Point In Square((Position of (Picked unit)), ((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) + 90.0) degrees), ((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) - 90.0) degrees), ( == true
Then
DamageUnit((Picked unit), (Triggering unit), damage)
Else
//My beam will hit two times pr second
General - Wait 0.5 Game Time seconds
First of all, in my map, you put two towers down, and then you use an ability to link one to the other. This trigger then fires when that ability is used.
I commented a bit to explain what happens.
Im calling some custom functions..I hope it's clear what they do.
I know it looks a bit messy, but hopfully it can clarify something you might have missed
Grrr... I have duplicated that effect verbatim and recreated the Beam ability from your map.
It will not work for me... The beam ability doesn't trigger from what I can tell.
I gave the ability to a structure and I am targeting a structure.
I thought maybe it was because it needed to target a Unit, so I changed those parameters to structures and nothing :(
Also, I can't choose the Beam ability in the script... Maybe because it doesn't have a sub command???
.: EDIT :.
Then
DamageUnit((Picked unit), (Triggering unit), damage)
Else
Meh. there is too much stuff to post in a thread. Ill just post the map then. There are a bunch of irrelevant stuff in there. What you should look for in the data editor is my unit Beam Tower, and the ability on it. Also, in the effects you will find the Beam Tower Persistent which I call from the trigger.
And in the trigger editor.. You can start at the Beam Tower Link trigger and see what functions it uses.
I'm not understanding this part of the code...
Also, the editor keeps erroring on your custom script too.
.: EDIT :.
I have noticed in your Triggers that you have a Function that looks to be for the way to create a rectangle region between the turrets.
However it looks messed up on my side for some reason...
it's the void ray beam fully charged. I got one of those in both directions.
@Sovaka:
First of all, the point in square function takes 5 points. The first point is the point you want to check for, and the remaning 4 points are the square.
To make the square, I take the position of one of the towers, and move a bit alog the line perpendicular to the line between the towers, in both directions.
Note: previously, I made the towers face eachother, so the simplest for me was to take the facing angle of the tower and then add or subtract 90 degrees, depending on which way I wanted to move.
As for the other trigger.. I have no idea why the names have been removed at your place.. it should look like this
Region From Point
Options: Function
Return Type: Region
Parameters
center = No Point <Point>
height = 0.0 <Real>
width = 0.0 <Real>
Grammar Text: Region From Point(center, height, width)
Hint Text: (None)
Custom Script Code
Local Variables
w = (width / 2.0) <Real>
h = (height / 2.0) <Real>
Actions
General - Return (Region(((X of center) - w), ((Y of center) - h), ((X of center) + w), ((Y of center) + h)))
This part of the code doesn't make sense to me and since your Trigger properties are all messed up on my side, would you be able to recopy and paste please?
Also, your Function DamageUnit, I can't seem to find it either.
Is there a way I can make these regions visible so I can tell if the code is triggering or not?
.: EDIT :.
Oh I just noticed in your custom script you also have a Function called "AvgPoints", this eludes me also.
I have no idea why all your code is broken on my side :S
Rather then make a record for a vector, I use points. This way I can pass them to a function and return them. So, AvgPoints takes two "vectors" and returns the avrage of those two (v1 + v2)/2.
Damage unit is also a custom function. You should find it under the util folder.
DamageUnit
Options: Action
Return Type: (None)
Parameters
target = No Unit <Unit>
source = No Unit <Unit>
ammount = 0.0 <Real>
Grammar Text: DamageUnit(target, source, ammount)
Hint Text: (None)
Custom Script Code
Local Variables
Actions
Catalog - Set value of Effects "TriggerDamage" "Amount" for player (Owner of source) to (String(ammount) with Any Precision decimal places)
Environment - Execute Trigger Damage on target from source
Obviously I have a damage effect in the data editor called TriggerDamage
As for the condition..
(Point In Square(
//The point to check
(Position of (Picked unit)),
//Four points making up the square
((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) + 90.0) degrees),
((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) - 90.0) degrees),
((Position of (Triggering ability target unit)) offset by 1.0 towards ((Facing of (Triggering ability target unit)) + 90.0) degrees),
((Position of (Triggering ability target unit)) offset by 1.0 towards ((Facing of (Triggering ability target unit)) - 90.0) degrees))
== true
hehe.. indeed it is :)
Like I said in my first post, I just split it into triangles
Point In Square
Options: Function
Return Type: Boolean
Parameters
point = No Point <Point>
s1 = No Point <Point>
s2 = No Point <Point>
s3 = No Point <Point>
s4 = No Point <Point>
Grammar Text: Point In Square(point, s1, s2, s3, s4)
Hint Text: (None)
Custom Script Code
Local Variables
Actions
General - If (Conditions) then do (Actions) else do (Actions)
If
Or
Conditions
(Point In Triangle(point, s1, s2, s3)) == true
(Point In Triangle(point, s1, s2, s4)) == true
(Point In Triangle(point, s1, s4, s3)) == true
Then
General - Return true
Else
General - Return false
You could say that I only need to split it into 2 triangles, but then it matters in what order the points are given to the function. Also, note that if you do a funky square - so one of the angles is above 180 degrees, this method will include an area outside the square.
lol your code is like the proverbial rabbit hole... That code links to another Function; Point In Triangle. :D
Options:FunctionReturnType:BooleanParameters=NoPoint<Point>=NoPoint<Point>=NoPoint<Point>=NoPoint<Point>GrammarText:(,,,)HintText:(None)CustomScriptCodeLocalVariables=0<Integer>=0<Integer>=NoPoint<Point[2]>
= 0.0 <Real[1][2]>
= 0.0 <Real>
= 0.0 <Real>
= 0.0 <Real>
Actions
Variable - Set (No Value)[0] = ((, ))
Variable - Set (No Value)[1] = ((, ))
Variable - Set (No Value)[2] = ((, ))
General - For each integer (No Value) from 0 to 1 with increment 1, do (Actions)
Actions
General - For each integer (No Value) from 0 to 2 with increment 1, do (Actions)
Actions
Variable - Set (No Value)[(NoValue)][(NoValue)] = ((, ))
Variable - Set (No Value) = (((No Value)[0][0] * (No Value)[1][1]) - ((No Value)[0][1] * (No Value)[0][1]))
Variable - Set (No Value) = (lv_dot[1][1] * lv_dot[0][2] - lv_dot[0][1] * lv_dot[1][2]) / lv_d
Variable - Set (No Value) = (lv_dot[0][0] * lv_dot[1][2] - lv_dot[0][1] * lv_dot[0][2]) / lv_d
General - Return (lv_x >= 0) && (lv_y >= 0) && (lv_x + lv_y <= 1)
Would it be that one?
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
Hey guys,
I've had a search around the forums and haven't quite found what I am after.
I am creating a TD map and there is an ability that I want to make but for the life of me, can't figure it out. Basically I want a Tower to emit a beam to another Tower, then any mob that passes through that beam, gets dealt X damage. Kind of like a laser fence, but spans between the chosen towers.
I had found some threads in regards to linking a slave unit to a master which would be good to determine host Tower to receiving tower. But I don't know how to make this variable beam deal damage to anything that crosses it.
.: EDIT :. Just had a thought about connecting the towers. Is there a way to make a Tower auto connect and beam to another Tower of the same type WITHIN its range. So if you had 3 in the range of each other, all would have beams shooting to them?
I have created such a tower in one of my maps. First of all, to make the beam.. Look here http://forums.sc2mapster.com/development/map-development/8589-solved-data-creating-a-permenent-beam/
As for finding units inside the beam, that is just math.
First of all I made a region that covers the square between the two towers.
Then every x seconds, I pick every unit in this region, and check if it is within the rotated square where the beam is going.
This is done by splitting the square up into triangles, and checking if the unit is inside one of those, and to do that...
Point In Triangle
Options: Function
Return Type: Boolean
Parameters
point = No Point <Point>
s1 = No Point <Point>
s2 = No Point <Point>
s3 = No Point <Point>
Grammar Text: Point In Triangle(point, s1, s2, s3)
Hint Text: (None)
Custom Script Code
Local Variables
i = 0 <Integer>
j = 0 <Integer>
v = No Point <Point[2]>
dot = 0.0 <Real[1][2]>
d = 0.0 <Real>
x = 0.0 <Real>
y = 0.0 <Real>
Actions
Variable - Set v[0] = (Subtract Points(s3, s1))
Variable - Set v[1] = (Subtract Points(s2, s1))
Variable - Set v[2] = (Subtract Points(point, s1))
General - For each integer i from 0 to 1 with increment 1, do (Actions)
Actions
General - For each integer j from 0 to 2 with increment 1, do (Actions)
Actions
Variable - Set dot[i][j] = (Dot(v[i], v[j]))
Variable - Set d = ((dot[0][0] * dot[1][1]) - (dot[0][1] * dot[0][1]))
Variable - Set x = (lv_dot[1][1] * lv_dot[0][2] - lv_dot[0][1] * lv_dot[1][2]) / lv_d
Variable - Set y = (lv_dot[0][0] * lv_dot[1][2] - lv_dot[0][1] * lv_dot[0][2]) / lv_d
General - Return (lv_x >= 0) && (lv_y >= 0) && (lv_x + lv_y <= 1)
SubtractPoints({x1, x2, x3}, {y1, y2, y3}) just returns {x1 - y1, x2 - y2, x3 - y3}, and Dot(x, y) returns the dot product betwen the two "vectors".
To make them auto connect to nearby towers, just make a trigger that fires when one of your towers are created. Then you search for nearby towers, and for each of those, you do the above.
@SBeier: Go
Awesome stuff! Cheers for the reply... I will definitely have to have a play with that and see what I can come up with. My only concern is that you mention a region trigger... This really wouldn't work if the tower placement wasn't fixed. IE if the player places a tower, how would there be a region trigger? Or can you add those with tower placement?
There are triggers to create a region at runtime. You can also add an event such as a unit enters region at runtime, but that is not how I use it. I create the smallest square region that contain both towers (at runtime), and then I periodically pick every enemy unit in this region, and deal damage to them if they are inside this square im talking about.
Edit: made a small image of it ;)
@SBeier: Go
Yeah that wouldn't work for a TD map tho :(
Could I however make the Tower fire arc of 360deg, then when a unit comes in its range, it then does the trigger to see if the unit is crossing its beam?
I dont see how it wouldn't work for a td map.. I dont think you understand me.
Ill try and post my trigger.
Beam Tower Link
Events
Unit - Any Unit uses Link at Generic3 - Execute stage (Ignore shared abilities)
Local Variables
distance = 0.0 <Real>
damage = 0.0 <Real>
region = No Region <Region>
Conditions
Actions
//Make the units face eachother (not important for the effect ur after)
Unit - Make (Triggering unit) face (Position of (Triggering ability target unit)) over 0.0 seconds
Unit - Make (Triggering ability target unit) face (Position of (Triggering unit)) over 0.0 seconds
//This is an effect that will cause some actors to make a beam between the two towers
Environment - Execute Beam Tower Persistent on (Triggering unit) from (Triggering ability target unit)
//I want the damage to be a function of the distance between the towers.
//Since getting the distance involves taking the squareroot, which is a bit slow, I save this to a varible
Variable - Set distance = (Distance between (Position of (Triggering unit)) and (Position of (Triggering ability target unit)))
//I made this function that will return a new region if I give it the center, width and height
General - Custom Script: lv_region = gf_RegionFromPoint(
gf_AvgPoints(UnitGetPosition(EventUnit()), UnitGetPosition(EventUnitTargetUnit())),
(AbsF((PointGetY(UnitGetPosition(EventUnit())) - PointGetY(UnitGetPosition(EventUnitTargetUnit())))) + 2.0),
(AbsF((PointGetX(UnitGetPosition(EventUnit())) - PointGetX(UnitGetPosition(EventUnitTargetUnit())))) + 2.0));
General - While (Conditions) are true, do (Actions)
Conditions
((Triggering unit) is alive) == true
((Triggering ability target unit) is alive) == true
Actions
Variable - Set damage = 20.0
Variable - Modify damage: / distance
//Like I talked about.. Pick each enemy unit in the region
Unit Group - Pick each unit in (Units in region having alliance Enemy with player (Triggering player) matching Excluded: Missile, Dead, Hidden, with at most Any Amount) and do (Actions)
Actions
General - If (Conditions) then do (Actions) else do (Actions)
If
//And if the point is in the square as illustrated by the picture, damage it
(Point In Square((Position of (Picked unit)), ((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) + 90.0) degrees), ((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) - 90.0) degrees), ( == true
Then
DamageUnit((Picked unit), (Triggering unit), damage)
Else
//My beam will hit two times pr second
General - Wait 0.5 Game Time seconds
First of all, in my map, you put two towers down, and then you use an ability to link one to the other. This trigger then fires when that ability is used.
I commented a bit to explain what happens.
Im calling some custom functions..I hope it's clear what they do.
I know it looks a bit messy, but hopfully it can clarify something you might have missed
Grrr... I have duplicated that effect verbatim and recreated the Beam ability from your map.
It will not work for me... The beam ability doesn't trigger from what I can tell. I gave the ability to a structure and I am targeting a structure. I thought maybe it was because it needed to target a Unit, so I changed those parameters to structures and nothing :(
Also, I can't choose the Beam ability in the script... Maybe because it doesn't have a sub command???
.: EDIT :. Then DamageUnit((Picked unit), (Triggering unit), damage) Else
Where did you get that from?
Meh. there is too much stuff to post in a thread. Ill just post the map then. There are a bunch of irrelevant stuff in there. What you should look for in the data editor is my unit Beam Tower, and the ability on it. Also, in the effects you will find the Beam Tower Persistent which I call from the trigger.
And in the trigger editor.. You can start at the Beam Tower Link trigger and see what functions it uses.
Cheers for the map... I will look over it.
In the meantime tho, I had figured out why my beam wasn't triggering... It was because the Unit Arc was set 0. Simple change to 360 fixed that issue.
So all I need to look at your map for, is how you handle the damage zone.
BTW, do you know how to offset the Beam (effect) so that it would appear say 2m above the Unit instead of from its center?
Your unit probally have an attachment point called overhead you can use.. If that isn't good enough, you should be able to use a Site actor (Local Offset) to move it further (like this guy does http://forums.sc2mapster.com/development/map-development/2049-psionic-zergling-effect/#p2).
Thanks, I will have a look.
Your code is very hard to read btw :p I can't understand how you have chucked so many if statements together under a single IF.
@SBeier: Go
I have created a beam deathtrap!
@tjw_90: Go
which beam model is this?
I'm not understanding this part of the code... Also, the editor keeps erroring on your custom script too.
.: EDIT :.
I have noticed in your Triggers that you have a Function that looks to be for the way to create a rectangle region between the turrets. However it looks messed up on my side for some reason...
@tjw_90:
hehe.. nice :)
@b0ne123:
it's the void ray beam fully charged. I got one of those in both directions.
@Sovaka:
First of all, the point in square function takes 5 points. The first point is the point you want to check for, and the remaning 4 points are the square.
To make the square, I take the position of one of the towers, and move a bit alog the line perpendicular to the line between the towers, in both directions.
Note: previously, I made the towers face eachother, so the simplest for me was to take the facing angle of the tower and then add or subtract 90 degrees, depending on which way I wanted to move.
As for the other trigger.. I have no idea why the names have been removed at your place.. it should look like this
Region From Point
Options: Function
Return Type: Region
Parameters
center = No Point <Point>
height = 0.0 <Real>
width = 0.0 <Real>
Grammar Text: Region From Point(center, height, width)
Hint Text: (None)
Custom Script Code
Local Variables
w = (width / 2.0) <Real>
h = (height / 2.0) <Real>
Actions
General - Return (Region(((X of center) - w), ((Y of center) - h), ((X of center) + w), ((Y of center) + h)))
@SBeier: Go
Awesome :) Sorry I hadn't got back on sooner, have been sick the past couple days :(
This part of the code doesn't make sense to me and since your Trigger properties are all messed up on my side, would you be able to recopy and paste please?
Also, your Function DamageUnit, I can't seem to find it either.
Is there a way I can make these regions visible so I can tell if the code is triggering or not?
.: EDIT :.
Oh I just noticed in your custom script you also have a Function called "AvgPoints", this eludes me also.
I have no idea why all your code is broken on my side :S
Rather then make a record for a vector, I use points. This way I can pass them to a function and return them. So, AvgPoints takes two "vectors" and returns the avrage of those two (v1 + v2)/2.
Damage unit is also a custom function. You should find it under the util folder.
DamageUnit
Options: Action
Return Type: (None)
Parameters
target = No Unit <Unit>
source = No Unit <Unit>
ammount = 0.0 <Real>
Grammar Text: DamageUnit(target, source, ammount)
Hint Text: (None)
Custom Script Code
Local Variables
Actions
Catalog - Set value of Effects "TriggerDamage" "Amount" for player (Owner of source) to (String(ammount) with Any Precision decimal places)
Environment - Execute Trigger Damage on target from source
Obviously I have a damage effect in the data editor called TriggerDamage
As for the condition..
(Point In Square(
//The point to check
(Position of (Picked unit)),
//Four points making up the square
((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) + 90.0) degrees),
((Position of (Triggering unit)) offset by 1.0 towards ((Facing of (Triggering unit)) - 90.0) degrees),
((Position of (Triggering ability target unit)) offset by 1.0 towards ((Facing of (Triggering ability target unit)) + 90.0) degrees),
((Position of (Triggering ability target unit)) offset by 1.0 towards ((Facing of (Triggering ability target unit)) - 90.0) degrees))
== true
Ok, so I have almost everything in and I am guessing the part that I have missing is the part that prevents it from applying damage.
Which comes back to Point In Square... Which I assume is also a Function that you call, the only thing close that I can find in your map is;
Is that it?
hehe.. indeed it is :)
Like I said in my first post, I just split it into triangles
Point In Square
Options: Function
Return Type: Boolean
Parameters
point = No Point <Point>
s1 = No Point <Point>
s2 = No Point <Point>
s3 = No Point <Point>
s4 = No Point <Point>
Grammar Text: Point In Square(point, s1, s2, s3, s4)
Hint Text: (None)
Custom Script Code
Local Variables
Actions
General - If (Conditions) then do (Actions) else do (Actions)
If
Or
Conditions
(Point In Triangle(point, s1, s2, s3)) == true
(Point In Triangle(point, s1, s2, s4)) == true
(Point In Triangle(point, s1, s4, s3)) == true
Then
General - Return true
Else
General - Return false
You could say that I only need to split it into 2 triangles, but then it matters in what order the points are given to the function. Also, note that if you do a funky square - so one of the angles is above 180 degrees, this method will include an area outside the square.
@SBeier: Go
lol your code is like the proverbial rabbit hole... That code links to another Function; Point In Triangle. :D
Would it be that one?