Jump to content
Madin

Prism Fowarding, Part IV (idea storm thread)

Recommended Posts

It is time for part IV of the highly unsuccessful Prism forwarding series

 

A review of notable past threads:

Part 1: Nothing to see

Part 2: http://web.archive.org/web/20140705150621/http://www.commandandconquer.com/forums/index.php/topic/22229-prism-forwarding-issues-part-ii/

This centred around a Lua based solution, in the end, issues with getting a Lua launched power to target something other than itself thwarted the attempt, Golan has since disappeared!

Part 3: http://forums.revora.net/topic/89776-prism-forwarding-part-iii/

 

Why using the Beam cannon type forwarding does not work

There are too many hard coded issues with the Beam cannons forwarding method. The path that the beam will take to the target is not predictable, and it is possible for it to cover larger distances then it should be able to, missing forwarding targets in order to do so.

Basically it is messy code that just about works for its intended purpose (although this is the closest I have got to getting forwarding to work).

 

Goal

The goal is to get the most basic (ie RA3 style) Prism forwarding to work. That is; if a single Prism tower has a target and it is preparing to fire, if any Prism towers within a certain range do not have a target, they will fire a support beam at the tower with a target, that will increase the power of its attack.

I would like to get this basic style forwarding to work, before I think about full Ra2 style forwarding (ie towers in range of supporting towers also firing support beams for the tower with the target).

 

Here is some Lauren advice, although I am too simple minded to fully understand it:

"rather do a weapon which has a lua event nugget and a special power nugget
the lua event nugget tells other towers to support this one (causing them to fire on this one with a weapon which triggers the beam effect and an attribute mod), and the special power nugget executes a storeobjectsspecialpower
(also grant a state to itself so the weapon changes)
the other attrimods add bonus damage
and the new weapon fires the actual weapon with a hitstoredobjectfilter
you might want to delay a bit and fire via a specialpower to ensure the attrimods are all gathered
(I don't think the sp will break if the attrimods are still coming in, and if you give it a unpack which is equal to the time the other towers charge you can use the unpack animation for the actual weapon animation and let the weapon only draw the beam)
I can make a diagram if this was too hard to follow XD"

 

Thanks for any advice given, I believe we will get there in the end! :P:ra2:

Edited by Madin

Share this post


Link to post

Biber Mirage tank that was imported to KW's simple mod also lack that option. Didn't tried to make it connect with other "beams" yet but will be glad to know how to do it as well

 

 

p.s didn't knew there's a web that let you watch old sites o_o

did you saved the thread files and upload it to webarchive?

Edited by Egozi44

Share this post


Link to post

This is a bumping of this old issue.

I feel that there is a lot more that has been done with LUA over the past year and a half, so I figured that it might worth getting some fresh input on this. Please check the opening post on this thread for some background information.

This is a frustrating issue!

Share this post


Link to post

I'm very confident that this is solvable, even for large cascade forwardings. Golan's approach wasn't bad, it just needs some more fine tuning I guess. To decide which tower will support or NOT support you can use this in addition to Golan's code:

Use this function to test the minimum distances between the towers:

function GetObjectDistance(object1 ,object2)
 if object1==nil or object2==nil or not EvaluateCondition("NAMED_NOT_DESTROYED",object1) or not EvaluateCondition("NAMED_NOT_DESTROYED",object2) then return 0 end
 local v = {}
	v.GetDistance = function(lowerlimit,upperlimit)
	   local mid=(upperlimit+lowerlimit)/2
	   if upperlimit-lowerlimit<=1 then return lowerlimit
	   elseif EvaluateCondition("DISTANCE_BETWEEN_OBJ", %object1, %object2, CompareTable[">"], mid) then return %v.GetDistance(lowerlimit,mid)
	   else return round(%v.GetDistance(mid,upperlimit)) end
	end
 return v.GetDistance(0,12870)  --maxdiagsize=ceil(sqrt(2*(7500+2*800)^2)))
end
                                  
 CompareTable    	  = { LT=0, LE=1, EQ=2, GE=3, GT=4, NE=5, [">"]=0, [">="]=1, ["=="]=2, ["<="]=3, ["<"]=4, ["~="]=5 } 
                                  
                                  
function GetObj.String(object)       
   if type(object) == "string" then return object
   else 
	    local _, count = gsub(ObjectDescription(object),"%(","")
	    if count>1 then
          return strsub(ObjectDescription(object), strfind(ObjectDescription(object),"(",1,true)+1,strfind(ObjectDescription(object),")",1,true)-1)
        else
		  local StrRef = "object" .. RandomString(5)
		  ExecuteAction("SET_UNIT_REFERENCE", StrRef, object)
		  return StrRef
		end
	end
end   
         
function RandomString(l)
        if l < 1 then l=5 end 
        local s = ""
		local ascii_zone = {}
		for i=1,l,1 do
		  ascii_zone[1] = random(97, 122)
		  ascii_zone[2] = random(65, 90)
		  ascii_zone[3] = random(48, 57)
          s = s .. strchar(ascii_zone[random(3)])
        end
        return ("_" .. s)
end
                                  
                                  
                                  

Then you can use something like that:

if GetObjectDistance(GetObj.String(self),GetObj.String(supporter))<150 and ObjectCountNearbyEnemies(supporter,SupportAttackRadius)>0 and ObjectCountNearbyEnemies(self,AttackRadius)>0 then ... end

If I have time I can look more detailed into it. You could also avoid broadcasting and iterate through a list of all tower objects. In worst case a coroutine that gets called every n seconds and does all testing and proper attack distribution is also viable instead of every tower having to get triggered its own lua event.

Edited by Mjjstral
  • Upvote 1

Share this post


Link to post

You sure you want to do worst case 14 iterations of the compare for each object pair? I mean it's using a script, which takes a lot more time to execute than other code.

Also > is greater, < is lesser. Your table states the opposite.

Share this post


Link to post

Thanks for the reply! I will test this out on Tuesday.

Share this post


Link to post
Quote

You sure you want to do worst case 14 iterations of the compare for each object pair? I mean it's using a script, which takes a lot more time to execute than other code.

Right, just wanted to give a general solution first. If possible pure usage of

EvaluateCondition("DISTANCE_BETWEEN_OBJ", object1, object2, 0, distance)

should be a priority.

Quote

Also > is greater, < is lesser. Your table states the opposite.

Thanks, never noticed. The functions using it all work well though, so no big deal.

Share this post


Link to post

Ok here is my working solution, performance shouldn`t be a problem, although these algorithms could be more optimized if needed.

20170523141307_1.jpg.ec8aeaaa228ecc955ed63f42a8137c12.jpg

20170523141657_1.jpg.6e5b374b9081f3ef43b3521b02f439d9.jpg

function CascadeForwarding(self)
	self=GetObj.Table(self)
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}	
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) and ObjectCountNearbyEnemies(v,TowerAttackRange)==0 then
				tinsert(ObjectList,v)
			end
		end
	end
  	tinsert(ObjectList,self)
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							SupportConnection[tostring(ObjectList[i])]=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end	
		if MainNodeCriteria == true then 
			tinsert(MainNodeTable,ObjectList[i])
			SupportConnection[tostring(ObjectList[i])]=NextMainNode
		end	
	end	
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		--ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

All necessary helper functions:

GetObj={}

function GetObj.String(object)       
   if type(object) == "string" then return object
   else 
	    local _, count = gsub(ObjectDescription(object),"%(","")
	    if count>1 then
          return strsub(ObjectDescription(object), strfind(ObjectDescription(object),"(",1,true)+1,strfind(ObjectDescription(object),")",1,true)-1)
        else
		  local StrRef = "object" .. RandomString(5)
		  ExecuteAction("SET_UNIT_REFERENCE", StrRef, object)
		  return StrRef
		end
	end
end

function GetObj.Table(object)
    if type(object) == "table" then return object
	else  
		for k,v in globals() do
		  if strfind(k,"ObjID") ~= nil and strfind(ObjectDescription(v),object) then
			return rawget(globals(),k) 
		  end
		end
		return object
	end
end

function GetObj.Hash(object)
    return tostring(tonumber(strsub(ObjectTemplateName(GetObj.Table(object)), 6,  13), 16))
end

CompareTable    	  = { LT=0, LE=1, EQ=2, GE=3, GT=4, NE=5, ["<"]=0, ["<="]=1, ["=="]=2, [">="]=3, [">"]=4, ["~="]=5 } 

function GetObjectDistance(object1 ,object2, start_distance)
 if object1==nil or object2==nil or not EvaluateCondition("NAMED_NOT_DESTROYED",object1) or not EvaluateCondition("NAMED_NOT_DESTROYED",object2) then return 0 end
 object1=GetObj.String(object1)
 object2=GetObj.String(object2)
 local v = {}
	v.GetDistance = function(lowerlimit,upperlimit)
	   local mid=(upperlimit+lowerlimit)/2
	   if upperlimit-lowerlimit<=1 then return lowerlimit
	   elseif EvaluateCondition("DISTANCE_BETWEEN_OBJ", %object1, %object2, CompareTable["<"], mid) then return %v.GetDistance(lowerlimit,mid)
	   else return round(%v.GetDistance(mid,upperlimit)) end
	end
 if start_distance then return v.GetDistance(0,tonumber(start_distance)) 
 else return v.GetDistance(0,12870) end   --maxdiagsize=ceil(sqrt(2*(7500+2*800)^2)))
end

function random(...) --overwritting lua native function for multiplayer compatibility 
	local RandomNumber = function(a,b) return floor(a+((b-a)*GetRandomNumber())+0.5) end
	if getn(arg) == 0 then return floor(GetRandomNumber()+0.5)
	elseif getn(arg) == 1 then return RandomNumber(1,arg[1])
	elseif getn(arg) == 2 then return RandomNumber(arg[1],arg[2])
	else return arg[RandomNumber(1,getn(arg))] end
end  

function RandomString(l)
        if l < 1 then l=5 end
        local s = ""
		local ascii_zone = {}
		for i=1,l,1 do
		  ascii_zone[1] = random(97, 122)
		  ascii_zone[2] = random(65, 90)
		  ascii_zone[3] = random(48, 57)
          s = s .. strchar(ascii_zone[random(3)])
        end
        return ("_" .. s)
end

To test in meta mod easily execute (button in meta command menu) tthe following with the LIVE_INPUT.lua in the MetaModIO folder:

MainObelisk = spawn("NODObelisk")

Then build your Nod Obelisk or any other unit able to attack around the main unit in any way, comment out the spawn code and execute:

CascadeForwarding(MainObelisk)

Hope that finally solves your problem in a decent way. It`s now up to you to adapt the code for your stuff.

Edited by Mjjstral
  • Upvote 1

Share this post


Link to post

In terms of me testing this in a C&C3 skirmish match, is the 'function CascadeForwarding(self)' a script that is to be broadcast when the main Obelisk gets a target and goes into 'PREATTACK_A'?

It is quite a complex script (for me), so I am just trying to minimise my errors.

Share this post


Link to post
Quote

In terms of me testing this in a C&C3 skirmish match, is the 'function CascadeForwarding(self)' a script that is to be broadcast when the main Obelisk gets a target and goes into 'PREATTACK_A'?

The only thing needed is the attacking unit/prism tower/obelisk or whatever to use that function on attacking:

<ModelConditionEvent Name="BeginPreAttackA">
			<Conditions>+PREATTACK_A</Conditions>
</ModelConditionEvent>

<EventList Name="PrismTower_EventList" Inherit="BaseScriptFunctions">
		<EventHandler EventName="BeginPreAttackA" ScriptFunctionName="CascadeForwarding" DebugSingleStep="false"/>
</EventList>

No more broadcasting to other towers needed. That function (CascadeForwarding) handles everything else for you conveniently.

I will possibly edit my main post above in the next days and change some lines and add all necessary helper functions needed for completness.

Quote

It is quite a complex script (for me), so I am just trying to minimise my errors.

It is indeed not a trivial script and also some more problems will possibly occur while setting everything up for the real prism tower. Just keep me updated and we will fix everything one by one. The things you need to change later for your `final product` is the ObjectHash, TowerAttackradius, MaximumSupportDistance and also adapt the content of the last two for loops according to your taste. For the hash you can actually set ObjectHash=tostring(tonumber(strsub(ObjectTemplateName(self), 6,  13), 16))

Also don`t forget to add the AttributeModifierWeapon and the special power (last two for loops).

For the weapon and attribute modifier my idea was something like this maybe:

	<WeaponTemplate
		id="PrismTowerSupportWeapon"
		Name="PrismTowerSupportWeapon"
		RadiusDamageAffects="ALLIES"
		AttackRange="15000.0">
		<Nuggets>
			<AttributeModifierNugget
				Radius="15000"
				PartitionFilterTestType="SPHERE"
				AttributeModifierName="AttributeModifier_PrismTowerSupport">
                <SpecialObjectFilter 
				  Rule="ANY" 
				  Relationship="SAME_PLAYER">					
				</SpecialObjectFilter> 
			</AttributeModifierNugget>			
		</Nuggets>
	</WeaponTemplate>	
	
	<AttributeModifier
		id="AttributeModifier_PrismTowerSupport"
		Category="NONE"
		StackingLimit="999"
		Duration="1.0s">
		<Modifier Type="DAMAGE_MULT" Value="100%"/>
	</AttributeModifier>

At least the duration needs adjustment and I^m not sure about the DAMAGE_MULT, as I^ve seen absolute as well as percent values.

Edited by Mjjstral
  • Upvote 1

Share this post


Link to post

Thanks for your help!

My first skirmish test failed.

Script:

function CascadeForwarding(self)
	local MaximumSupportDistance = 10000
	local TowerAttackRadius = 375 --here attack range of nod obelisk
	local ObjectHash = "3868904616" --"NODObelisk"
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}	
	--create list of all relevant objects
	for k,v in globals() do
		if strfind(k,"ObjID")~=nil and GetObj.Hash(v)==ObjectHash and v~=self and ObjectTeamName(self)==ObjectTeamName(v) then
			local Distance = GetObjectDistance(GetObj.String(self),GetObj.String(v))
			if Distance <= MaximumSupportDistance and ObjectCountNearbyEnemies(v,TowerAttackRadius)==0 then
				tinsert(ObjectList,v)
			end
		end
	end
 	LastSupportTowerCount=getn(ObjectList) 
	tinsert(ObjectList,self)	
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	--tinsert(MainNodeTable,{[1]=self,["n"]=1})	
	tinsert(MainNodeTable,self)
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							SupportConnection[tostring(ObjectList[i])]=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end	
		if MainNodeCriteria == true then 
			tinsert(MainNodeTable,ObjectList[i])
			SupportConnection[tostring(ObjectList[i])]=NextMainNode
		end	
	end	
	--execute 
	for i=1,LastSupportTowerCount,1 do
		--fire attribute booster weapon on main tower (for each support tower one weapon):
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
		--alternative via object:
		--ExecuteAction("CREATE_NAMED_ON_TEAM_AT_OBJECTTYPE_WITH_ORIENTATION",RandomString(5),ObjectTypeToSpawn,GetTeamName(self),self,0)
	end		
	for i=2,getn(ObjectList),1 do
		--use direct attack for testing:
		-- ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])
		--else use non damaging special power weapon:
		ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
	end
end

I am using the attribute weapon suggested.

And a simple weapon fire special power:

<SpecialPower id="ModuleTag_PrismForwardingSpecialPower" SpecialPowerTemplate="SpecialPower_PrismForwarding" UpdateModuleStartsAttack="true"></SpecialPower>
      <WeaponFireSpecialAbilityUpdate id="ModuleTag_PrismForwardingBeamUpdate" SpecialPowerTemplate="SpecialPower_PrismForwarding" SpecialWeapon="TestObeliskLaser" StartAbilityRange="99999.0" UnpackTime="1s" WhichSpecialWeapon="1"></WeaponFireSpecialAbilityUpdate>

  <SpecialPowerTemplate id="SpecialPower_PrismForwarding" Flags="NEEDS_OBJECT_FILTER" TargetType="OBJECT">
    <ObjectFilter Relationship="SAME_PLAYER" Rule="NONE">
      <IncludeThing>NODOBELISK</IncludeThing>
    </ObjectFilter>
  </SpecialPowerTemplate>

The scriptevent is also setup.

I am running the test on the Nod Obelisk to start with.

Share this post


Link to post

Ok copy the now updated code and also all the helper functions from my main post and try again. For lua error logs use the following function and insert it into your scripts.lua file:

function _ALERT(message)
	ExecuteAction("SHOW_MILITARY_CAPTION", "\n\n\n\n" .. tostring(message) .. "\n", 20)
	local file = ""
	local filehandle = openfile("ErrorLog",'a')
	if filehandle ~= nil then
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)
	else
		local filehandle = writeto(file)
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)  
	end
	flush(filehandle)
	closefile(filehandle)
end

It will save/refresh a file called "ErrorLog" which will be in the same folder where your "cnc3game.dat" is placed. Send the logs to me then if you can't solve it. Else there must be a xml coding flaw somewhere we missed.

Here some pictures of some more tests, also in first person mode which looks epic with all the towers doing their action at once:

20170524181559_1.jpg.5f901fd6321c8374b0cc2ff29e495ea8.jpg

20170524181710_1.jpg.d30e82ecf344ec8afca1652b9b8deab8.jpg

 

Edited by Mjjstral
  • Upvote 1

Share this post


Link to post

Nice! I now have error messages! I have attached it.

Here is then code order I added comments for the  error line numbers (although you probably do not need them):

GetObj={}

function GetObj.String(object)       
   if type(object) == "string" then return object
   else 
	    local _, count = gsub(ObjectDescription(object),"%(","")
	    if count>1 then
          return strsub(ObjectDescription(object), strfind(ObjectDescription(object),"(",1,true)+1,strfind(ObjectDescription(object),")",1,true)-1)
        else
		  local StrRef = "object" .. RandomString(5)
		  ExecuteAction("SET_UNIT_REFERENCE", StrRef, object)
		  return StrRef
		end
	end
end

function GetObj.Table(object)
    if type(object) == "table" then return object
	else  
		for k,v in globals() do
		  if strfind(k,"ObjID") ~= nil and strfind(ObjectDescription(v),object) then
			return rawget(globals(),k) 
		  end
		end
		return object
	end
end

function GetObj.Hash(object)
    return tostring(tonumber(strsub(ObjectTemplateName(GetObj.Table(object)), 6,  13), 16))
end

CompareTable    	  = { LT=0, LE=1, EQ=2, GE=3, GT=4, NE=5, ["<"]=0, ["<="]=1, ["=="]=2, [">="]=3, [">"]=4, ["~="]=5 } 

function GetObjectDistance(object1 ,object2, start_distance)
 if object1==nil or object2==nil or not EvaluateCondition("NAMED_NOT_DESTROYED",object1) or not EvaluateCondition("NAMED_NOT_DESTROYED",object2) then return 0 end
 object1=GetObj.String(object1)
 object2=GetObj.String(object2)
 local v = {}
	v.GetDistance = function(lowerlimit,upperlimit)
	   local mid=(upperlimit+lowerlimit)/2
	   if upperlimit-lowerlimit<=1 then return lowerlimit
	   elseif EvaluateCondition("DISTANCE_BETWEEN_OBJ", %object1, %object2, CompareTable["<"], mid) then return --Error (Line 45)
	   else return round(%v.GetDistance(mid,upperlimit)) end --Error (Line 46)
	end
 if start_distance then return v.GetDistance(0,tonumber(start_distance)) 
 else return v.GetDistance(0,12870) end   --maxdiagsize=ceil(sqrt(2*(7500+2*800)^2))) --Error (Line 49)
end

function random(...) --overwritting lua native function for multiplayer compatibility 
	local RandomNumber = function(a,b) return floor(a+((b-a)*GetRandomNumber())+0.5) end
	if getn(arg) == 0 then return floor(GetRandomNumber()+0.5)
	elseif getn(arg) == 1 then return RandomNumber(1,arg[1])
	elseif getn(arg) == 2 then return RandomNumber(arg[1],arg[2])
	else return arg[RandomNumber(1,getn(arg))] end
end  

function RandomString(l)
        if l < 1 then l=5 end
        local s = ""
		local ascii_zone = {}
		for i=1,l,1 do
		  ascii_zone[1] = random(97, 122)
		  ascii_zone[2] = random(65, 90)
		  ascii_zone[3] = random(48, 57)
          s = s .. strchar(ascii_zone[random(3)])
        end
        return ("_" .. s)
end

function CascadeForwarding(self)
	self=GetObj.Table(self)
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}	
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) and ObjectCountNearbyEnemies(v,TowerAttackRange)==0 then
				tinsert(ObjectList,v)
			end
		end
	end
  	tinsert(ObjectList,self)
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j])) --Error (Line 99)
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							SupportConnection[tostring(ObjectList[i])]=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end	
		if MainNodeCriteria == true then 
			tinsert(MainNodeTable,ObjectList[i])
			SupportConnection[tostring(ObjectList[i])]=NextMainNode
		end	
	end	
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		--ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

function _ALERT(message)
	ExecuteAction("SHOW_MILITARY_CAPTION", "\n\n\n\n" .. tostring(message) .. "\n", 20)
	local file = ""
	local filehandle = openfile("ErrorLog",'a')
	if filehandle ~= nil then
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)
	else
		local filehandle = writeto(file)
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)  
	end
	flush(filehandle)
	closefile(filehandle)
end

Thanks!

ErrorLog.txt

Share this post


Link to post

 Oh sorry, that one function was missing. Simple math round:

Quote

function round(number)

    return floor(number+0.5)

end

Let's see what comes next ;).

  • Upvote 1

Share this post


Link to post

That code certainly cleared up the errors, thanks!

Now I can onto the fact that this dam thing won't work... fully.

When I try using the Special power for the visual link, it does not work. So I switched to the 'NAMED_ATTACK_NAMED' method, and removed the ability for the Obelisk weapon to damage other Obelisk.

On testing this, the visual link works, but the issue is that the Obelisk will all fire support beams at each other, and ignore enemy targets!

TestFail_02.jpg

Code:

GetObj={}

function GetObj.String(object)       
   if type(object) == "string" then return object
   else 
	    local _, count = gsub(ObjectDescription(object),"%(","")
	    if count>1 then
          return strsub(ObjectDescription(object), strfind(ObjectDescription(object),"(",1,true)+1,strfind(ObjectDescription(object),")",1,true)-1)
        else
		  local StrRef = "object" .. RandomString(5)
		  ExecuteAction("SET_UNIT_REFERENCE", StrRef, object)
		  return StrRef
		end
	end
end

function GetObj.Table(object)
    if type(object) == "table" then return object
	else  
		for k,v in globals() do
		  if strfind(k,"ObjID") ~= nil and strfind(ObjectDescription(v),object) then
			return rawget(globals(),k) 
		  end
		end
		return object
	end
end

function GetObj.Hash(object)
    return tostring(tonumber(strsub(ObjectTemplateName(GetObj.Table(object)), 6,  13), 16))
end

CompareTable    	  = { LT=0, LE=1, EQ=2, GE=3, GT=4, NE=5, ["<"]=0, ["<="]=1, ["=="]=2, [">="]=3, [">"]=4, ["~="]=5 } 

function GetObjectDistance(object1 ,object2, start_distance)
 if object1==nil or object2==nil or not EvaluateCondition("NAMED_NOT_DESTROYED",object1) or not EvaluateCondition("NAMED_NOT_DESTROYED",object2) then return 0 end
 object1=GetObj.String(object1)
 object2=GetObj.String(object2)
 local v = {}
	v.GetDistance = function(lowerlimit,upperlimit)
	   local mid=(upperlimit+lowerlimit)/2
	   if upperlimit-lowerlimit<=1 then return lowerlimit
	   elseif EvaluateCondition("DISTANCE_BETWEEN_OBJ", %object1, %object2, CompareTable["<"], mid) then return %v.GetDistance(lowerlimit,mid)
	   else return round(%v.GetDistance(mid,upperlimit)) end
	end
 if start_distance then return v.GetDistance(0,tonumber(start_distance)) 
 else return v.GetDistance(0,12870) end   --maxdiagsize=ceil(sqrt(2*(7500+2*800)^2)))
end

function random(...) --overwritting lua native function for multiplayer compatibility 
	local RandomNumber = function(a,b) return floor(a+((b-a)*GetRandomNumber())+0.5) end
	if getn(arg) == 0 then return floor(GetRandomNumber()+0.5)
	elseif getn(arg) == 1 then return RandomNumber(1,arg[1])
	elseif getn(arg) == 2 then return RandomNumber(arg[1],arg[2])
	else return arg[RandomNumber(1,getn(arg))] end
end  

function RandomString(l)
        if l < 1 then l=5 end
        local s = ""
		local ascii_zone = {}
		for i=1,l,1 do
		  ascii_zone[1] = random(97, 122)
		  ascii_zone[2] = random(65, 90)
		  ascii_zone[3] = random(48, 57)
          s = s .. strchar(ascii_zone[random(3)])
        end
        return ("_" .. s)
end

function CascadeForwarding(self)
	self=GetObj.Table(self)
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}	
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) and ObjectCountNearbyEnemies(v,TowerAttackRange)==0 then
				tinsert(ObjectList,v)
			end
		end
	end
  	tinsert(ObjectList,self)
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							SupportConnection[tostring(ObjectList[i])]=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end	
		if MainNodeCriteria == true then 
			tinsert(MainNodeTable,ObjectList[i])
			SupportConnection[tostring(ObjectList[i])]=NextMainNode
		end	
	end	
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		-- ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

function round(number)

    return floor(number+0.5)

end

function _ALERT(message)
	ExecuteAction("SHOW_MILITARY_CAPTION", "\n\n\n\n" .. tostring(message) .. "\n", 20)
	local file = ""
	local filehandle = openfile("ErrorLog",'a')
	if filehandle ~= nil then
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)
	else
		local filehandle = writeto(file)
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)  
	end
	flush(filehandle)
	closefile(filehandle)
end

Thanks!

Edited by Madin
Added code

Share this post


Link to post

Hey I've changed quite alot, so that towers that have enemies in attack range and do not have "attack on position" commands issued won't support other tower anymore. That means you can now test the system with attack on position commands without enemies even.

The problems:

I also didn't succeed to make it work via a special power on the xml coding side. My advice is to create that special power as a button ability and make sure it is actually working via manual use. Then it will surely work on the lua side. There was something similiar with the devourer conversion reserve use we worked on, where the special power use on another object also worked.

My alternative approach is to still use ExecuteAction("NAMED_ATTACK_NAMED",...) , but with a tertiary non damaging weapon that only can be used on the respective tower object (here NodObelisk). The very strange thing for me was, that every second support beam, does damage to the tower they support. This can probably get solved by reissueing the attack command precisely after each attack.

Last problem: The attribute modifier didn't work for me at all, there's something missing that you need to find. I've used attribute modifiers with ObjectCreateAndFireTempWeapon before, so in theory it is viable. If it has to do with the weapon with it's damage attribute already locked after EventName="PREATTACK_A" then you can use EventName="ATTACKING" which gets triggered before.

This gets more and more complicated each day xD

So here is all the code:

MainTowerSupporterList= {}
MainTowerSupportConnectionList= {}

function CascadeForwarding(self) --made for "Madin" (cncnz forums)
	self=GetObj.Table(self)
	local IsObjectInSupporterList = function(object)
		for q,p in MainTowerSupporterList do  --to test if a tower already supports another tower
			if q~=tostring(%self) then
				for i=1,getn(p),1 do if p[i]==object then return true end end
			end
		end
		return false
	end	
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 and IsObjectInSupporterList(self) then return end
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if ObjectCountNearbyEnemies(v,TowerAttackRange)==0 and EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) then
				if not IsObjectInSupporterList(v) then tinsert(ObjectList,v) end
			end
		end
	end
  	tinsert(ObjectList,self)
	if getn(ObjectList)<2 then return end
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	local i = 2
	local ObjectListSize
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode, NextMainNodeAlternative  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j and ObjectList[j]~=nil then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							NextMainNodeAlternative=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end
		if MainNodeCriteria == true then 
			if TowerAttackRange > DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])] then
				tinsert(MainNodeTable,ObjectList[i])
				SupportConnection[tostring(ObjectList[i])]=NextMainNode
			else
				ObjectList[i]=nil
			end
		else 
			if TowerAttackRange > ShortestLinkToNextMainNodeDistance then 
				SupportConnection[tostring(ObjectList[i])]=NextMainNodeAlternative
			else 
				ObjectList[i]=nil
			end
		end
	end	
	--remove all unneccasary towers
	local CleanedObjectList = {}
	for i=1,getn(ObjectList),1 do
		if not ObjectList[i]==nil then tinsert(CleanedObjectList,ObjectList[i]) end
	end
	ObjectList=CleanedObjectList
	MainTowerSupporterList[tostring(self)] = CleanedObjectList
	MainTowerSupportConnectionList[tostring(self)]=SupportConnection
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		--ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		ExecuteAction("NAMED_STOP",ObjectList[i])
		ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

And here are the additional functions:

function GetMyMainTower(object)
	for q,p in MainTowerSupporterList do 
		if q~=tostring(object) then
			for i=1,getn(p),1 do 
				if p[i]==object then 
					return p[1]
				end
			end
		end
	end
end

function IsTowerStillAttacking(MainTower)
	if ObjectTestModelCondition(MainTower,"ATTACKING") or ObjectTestModelCondition(MainTower,"PREATTACK_A") or ObjectTestModelCondition(MainTower,"RELOADING_A") or ObjectTestModelCondition(MainTower,"FIRING_A") then return true else return false end
end
	
function NodObelisk_RELOADING(self)
	local TowerAttackRange = 375
	if MainTowerSupporterList[tostring(self)]~=nil then --and ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then
		for i=2,getn(MainTowerSupporterList[tostring(self)]),1 do
			ExecuteAction("NAMED_STOP",MainTowerSupporterList[tostring(self)][i])
		end
		--MainTowerSupporterList[tostring(self)]=nil
	else
		if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then ExecuteAction("NAMED_STOP",self) end
		local MainTower = GetMyMainTower(self)
		if not IsTowerStillAttacking(MainTower) then 
			MainTowerSupporterList[tostring(MainTower)]=nil
		else 
			ExecuteAction("NAMED_STOP",self)
			ExecuteAction("NAMED_ATTACK_NAMED",self,MainTowerSupportConnectionList[tostring(MainTower)][tostring(self)]) 
		end	
	end
end

function getn2(table)
   local size = 0
   for k,v in table do size=size+1 end
   return size
end

And for the scriptevents.xml:

    <EventList Name="NodObeliskEvents">
<!-- 		<EventHandler EventName="ATTACKING" ScriptFunctionName="CascadeForwarding" DebugSingleStep="false"/> -->
		<EventHandler EventName="PREATTACK_A" ScriptFunctionName="CascadeForwarding" DebugSingleStep="false"/>
<!-- 		<EventHandler EventName="PREATTACK_B" ScriptFunctionName="CascadeForwarding" DebugSingleStep="false"/> -->
<!-- 		<EventHandler EventName="FIRING_A" ScriptFunctionName="NodObelisk_FIRING_A" DebugSingleStep="false"/> -->
		<EventHandler EventName="RELOADING_A" ScriptFunctionName="NodObelisk_RELOADING" DebugSingleStep="false"/>
		<EventHandler EventName="RELOADING_C" ScriptFunctionName="NodObelisk_RELOADING" DebugSingleStep="false"/>		
    </EventList>

		<ModelConditionEvent Name="PREATTACK_A">
			<Conditions>+PREATTACK_A</Conditions>
		</ModelConditionEvent>
		
		<ModelConditionEvent Name="PREATTACK_B">
			<Conditions>+PREATTACK_B</Conditions>
		</ModelConditionEvent>
		
		<ModelConditionEvent Name="FIRING_A">
			<Conditions>+FIRING_A</Conditions>
		</ModelConditionEvent>
		
		<ModelConditionEvent Name="RELOADING_A">
			<Conditions>+RELOADING_A</Conditions>
		</ModelConditionEvent>
		
		<ModelConditionEvent Name="RELOADING_C">
			<Conditions>+RELOADING_C</Conditions>
		</ModelConditionEvent>
		
		<ModelConditionEvent Name="ATTACKING">
			<Conditions>+ATTACKING</Conditions>
		</ModelConditionEvent>

And here is the NodObelisk object and it's weapons and stuff, I've used:

<?xml version="1.0" encoding="utf-8"?>
<AssetDeclaration xmlns="uri:ea.com:eala:asset" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xi="http://www.w3.org/2001/XInclude">
	<Tags></Tags>
	<Includes>
		<Include
			type="all"
			source="ART:NBObelisk.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskA_SN.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskA_AN.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskD2_SN.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskD2_AN.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskD3_SN.w3x" />
		<Include
			type="all"
			source="ART:NBObeliskD3_AN.w3x" />
		<Include
			type="all"
			source="ART:FXObeliskLaser.xml" />
		<Include
			type="all"
			source="ART:FXLaserOverlay.xml" />
		<Include
			type="all"
			source="ART:NBObeliskCrstl.w3x" />
		<Include
			type="all"
			source="DATA:GlobalData/GlobalDefines.xml" />
		<Include
			type="all"
			source="ART:FXspotlight.w3x" />
		<!-- Base Object -->
		<Include
			type="instance"
			source="DATA:BaseObjects/BaseStructure.xml" />
	</Includes>
	<GameObject
		id="NODObelisk"
		inheritFrom="BaseStructure"
		SelectPortrait="Portrait_NODObeliskofLight"
		ButtonImage="Portrait_NODObeliskofLight"
		Side="NOD"
		EditorSorting="STRUCTURE"
		TransportSlotCount="1"
		BuildCost="2000"
		BuildTime="20"
		EnergyProduction="-15"
		CommandSet="NODObeliskCommandSet"
		KindOf="STRUCTURE SELECTABLE IMMOBILE CAN_CAST_REFLECTIONS FS_BASE_DEFENSE POWERED AUTO_ACQUIRABLE_BY_AI COVER CAN_ATTACK CAN_SEE_THROUGH_STRUCTURE ATTACK_NEEDS_LINE_OF_SIGHT LINE_OF_SIGHT_IGNORES_BUILDINGS"
		RadarPriority="STRUCTURE"
		PlacementViewAngle="315d"
		ProductionQueueType="OTHER_STRUCTURE"
		BuildPlacementTypeFlag="OTHER_STRUCTURE"
		EditorName="NODObelisk"
		WeaponCategory="CANNON"
		TypeDescription="Type:NODObelisk"
		Description="Desc:NODObelisk">
		<DisplayName>Name:NODObelisk</DisplayName>
		<GameDependency>
			<RequiredObject>NODTechAssemblyPlant</RequiredObject>
		</GameDependency>
		<ArmorSet
			Armor="NodObeliskArmor"
			DamageFX="FactionStructureDamageFX" />
		<SkirmishAIInformation
			BaseBuildingLocation="SPREAD" />
		<Draws>
			<ScriptedModelDraw
				id="ModuleTag_01"
				OkToChangeModelColor="true"
				ExtraPublicBone="FXBONE01">
				<ModelConditionState
					ParseCondStateType="PARSE_DEFAULT">
					<Model
						Name="NBObelisk" />
					<WeaponFireFXBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<WeaponLaunchBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
				</ModelConditionState>
				<ModelConditionState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="STRUCTURE_UNPACKING">
					<Model
						Name="NBObeliskA_SN" />
					<WeaponFireFXBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<WeaponLaunchBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
				</ModelConditionState>
				<ModelConditionState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="DAMAGED">
					<Model
						Name="NBObeliskD2_SN" />
					<WeaponFireFXBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<WeaponLaunchBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<ParticleSysBone
						BoneName="FXBone01"
						FXParticleSystemTemplate="NODObDamagedSteam"
						FollowBone="true" />
					<ParticleSysBone
						BoneName="FXBone01"
						FXParticleSystemTemplate="NODObDamagedDist"
						FollowBone="true" />
				</ModelConditionState>
				<ModelConditionState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="REALLYDAMAGED">
					<Model
						Name="NBObeliskD2_SN" />
					<WeaponFireFXBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<WeaponLaunchBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<ParticleSysBone
						BoneName="FXBone01"
						FXParticleSystemTemplate="NODObDamagedSpew"
						FollowBone="true" />
					<ParticleSysBone
						BoneName="FXBone01"
						FXParticleSystemTemplate="NODObDamagedSpewDist"
						FollowBone="true" />
					<ParticleSysBone
						BoneName="FXBone02"
						FXParticleSystemTemplate="NODObDamagedSmoke"
						FollowBone="true" />
				</ModelConditionState>
				<ModelConditionState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="DYING">
					<Model
						Name="NBObeliskD3_SN" />
					<WeaponFireFXBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
					<WeaponLaunchBone
						WeaponSlotType="PRIMARY_WEAPON"
						BoneName="FX_Weapon" />
				</ModelConditionState>
				<AnimationState
					ParseCondStateType="PARSE_DEFAULT"></AnimationState>
				<!-- BUILD UP ANIMATION -->
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="STRUCTURE_UNPACKING"
					StateName="STATE_Buildup">
					<Animation
						AnimationName="NBObeliskA_AN"
						AnimationMode="ONCE" />
					<ParticleSysBone
						BoneName="NONE"
						FXParticleSystemTemplate="Con_Small"
						FollowBone="true" />
				</AnimationState>
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="DAMAGED"
					StateName="STATE_Damaged"
					Flags="START_FRAME_FIRST">
					<Animation
						AnimationName="NBObeliskD2_AN"
						AnimationMode="MANUAL" />
				</AnimationState>
				<!-- REALLY DAMAGED ANIMATION -->
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="REALLYDAMAGED"
					StateName="STATE_ReallyDamaged">
					<Animation
						AnimationName="NBObeliskD2_AN"
						AnimationMode="ONCE" />
					<Script>
						Prev = CurDrawablePrevAnimationState();
							if Prev == "STATE_Damaged" then CurDrawableSetTransitionAnimState("TRANS_DamagedToReallyDamaged") end;
					</Script>
				</AnimationState>
				<!-- DYING ANIMATION -->
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="DYING">
					<Animation
						AnimationName="NBObeliskD3_AN"
						AnimationMode="ONCE" />
					<ParticleSysBone
						BoneName="NONE"
						FXParticleSystemTemplate="Des_Small"
						FollowBone="false" />
				</AnimationState>
				<!-- Transitions -->
				<AnimationState
					ParseCondStateType="PARSE_TRANSITION"
					ConditionsYes="TRANS_DamagedToReallyDamaged">
					<Animation
						AnimationName="NBObeliskD2_AN"
						AnimationMode="ONCE" />
				</AnimationState>
			</ScriptedModelDraw>
			<!-- DRAW CRYSTAL -->
			<ScriptedModelDraw
				id="ModuleTag_Draw_Crystal"
				OkToChangeModelColor="true">
				<ModelConditionState
					ParseCondStateType="PARSE_DEFAULT">
					<Model
						Name="NBObeliskCrstl" />
				</ModelConditionState>
				<AnimationState
					ParseCondStateType="PARSE_DEFAULT"
					Flags="START_FRAME_FIRST">
					<Animation
						AnimationName="NBObeliskCrstl"
						AnimationMode="MANUAL" />
				</AnimationState>
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="PREATTACK_A">
					<Animation
						AnimationName="NBObeliskCrstl"
						AnimationMode="ONCE" />
					<ParticleSysBone
						BoneName="FX_Weapon"
						FXParticleSystemTemplate="NODObeliskFireDistort" />
					<ParticleSysBone
						BoneName="FX_Weapon"
						FXParticleSystemTemplate="NODObeliskFireGlow" />
				</AnimationState>
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="PREATTACK_C">
					<Animation
						AnimationName="NBObeliskCrstl"
						AnimationMode="ONCE" />
					<ParticleSysBone
						BoneName="FX_Weapon"
						FXParticleSystemTemplate="NODObeliskFireDistort" />
					<ParticleSysBone
						BoneName="FX_Weapon"
						FXParticleSystemTemplate="NODObeliskFireGlow" />
				</AnimationState>
				<AnimationState
					ParseCondStateType="PARSE_NORMAL"
					ConditionsYes="FIRING_A"
					Flags="START_FRAME_LAST">
					<Animation
						AnimationName="NBObeliskCrstl"
						AnimationMode="MANUAL" />
				</AnimationState>
			</ScriptedModelDraw>
			<SpotlightDraw
				id="ModuleTag_Spotlight"
				AttachToBoneInAnotherModule="FXBone01"
				RefreshTime="2.0s"
				SweepTime="1.0s">
				<ModelConditionState
					ParseCondStateType="PARSE_DEFAULT">
					<Model
						Name="FXspotlight" />
				</ModelConditionState>
			</SpotlightDraw>
			<LaserDraw
				id="ModuleTag_LaserDraw"
				Texture1_UTile="1"
				Texture1_VTile=".5"
				Texture1_UScrollRate="0"
				Texture1_VScrollRate=".2"
				Texture1_NumFrames="1"
				Texture1_FrameRate="30"
				Texture2_UTile="1"
				Texture2_VTile=".03"
				Texture2_UScrollRate="0"
				Texture2_VScrollRate="1"
				Texture2_NumFrames="1"
				Texture2_FrameRate="30"
				LaserWidth="20"
				LaserStateID="1">
				<FXShader
					ShaderName="Laser.fx"
					TechniqueIndex="0">
					<Constants>
						<Texture
							Name="Texture1">
							<Value>FXObeliskLaser</Value>
						</Texture>
						<Texture
							Name="Texture2">
							<Value>FXLaserOverlay</Value>
						</Texture>
					</Constants>
				</FXShader>
			</LaserDraw>
			<LaserDraw
				id="ModuleTag_AntiInfantryLaserDraw"
				Texture1_UTile="1"
				Texture1_VTile=".5"
				Texture1_UScrollRate="0"
				Texture1_VScrollRate=".2"
				Texture1_NumFrames="1"
				Texture1_FrameRate="30"
				Texture2_UTile="1"
				Texture2_VTile=".03"
				Texture2_UScrollRate="0"
				Texture2_VScrollRate="1"
				Texture2_NumFrames="1"
				Texture2_FrameRate="30"
				LaserWidth="20"
				LaserStateID="2">
				<FXShader
					ShaderName="Laser.fx"
					TechniqueIndex="0">
					<Constants>
						<Texture
							Name="Texture1">
							<Value>FXObeliskLaser</Value>
						</Texture>
						<Texture
							Name="Texture2">
							<Value>FXLaserOverlay</Value>
						</Texture>
					</Constants>
				</FXShader>
			</LaserDraw>
		</Draws>
		<Behaviors>
			<WeaponSetUpdate
				id="ModuleTag_WeaponSetUpdate">
				<WeaponSlotHardpoint
					ID="1">
					<Weapon
						Ordering="PRIMARY_WEAPON"
						Template="NODObeliskLaser" />
					<Weapon
						Ordering="SECONDARY_WEAPON"
						Template="NODObeliskAntiInfantryLaser" />
					<Weapon
						Ordering="TERTIARY_WEAPON"
						Template="NODObeliskLaserSupport" />						
				</WeaponSlotHardpoint>
			</WeaponSetUpdate>
			<SpecialPower 
				id="ModuleTag_PrismForwardingSpecialPower" 
				SpecialPowerTemplate="SpecialPower_PrismForwarding" 
				UpdateModuleStartsAttack="true">
			</SpecialPower>
			<WeaponFireSpecialAbilityUpdate 
				id="ModuleTag_PrismForwardingBeamUpdate" 
				SpecialPowerTemplate="SpecialPower_PrismForwarding" 
				SpecialWeapon="NODObeliskLaserSupport" 
				StartAbilityRange="99999.0" 
				UnpackTime="0s" 
				WhichSpecialWeapon="1">
			</WeaponFireSpecialAbilityUpdate>			
			<LaserState
				id="ModuleTag_LaserState"
				LaserId="1" />
			<SweepingLaserState
				id="ModuleTag_AntiInfantryLaserState"
				LaserId="2"
				Radius="25.0"
				SweepFXList="FX_NodLaserSweep"
				SweepWeapon="NODObeliskSweepWeapon" />
			<SlowDeath
				id="ModuleTag_Death"
				SinkDelay="3.0s"
				SinkRate="4.0"
				DestructionDelay="8.0s">
				<Sound Type="INITIAL" List="HumanFaction_SmallBuilding_DieMS" />
				<DieMuxData
					DeathTypes="ALL" />
			</SlowDeath>
			<StealthDetectorUpdate
				id="ModuleTag_StealthDetect"
				DetectionRange="200" />
			<FXListBehavior
				id="ModuleTag_FXList">
				<DieMuxData
					DeathTypes="ALL" />
				<Event
					Index="onTransitionToDamaged"
					FX="FX_BuildDamaged_Medium" />
				<Event
					Index="onTransitionToReallyDamaged"
					FX="FX_BuildDamaged_Medium" />
				<Event
					Index="onTransitionToRubble"
					FX="FX_BuildDamaged_Medium" />
			</FXListBehavior>
			<StructureUnpackUpdate
				UnpackTime="1.5s" />
			<!--This is temporary and should be replaced by an inherited object-->
			<xi:include
				href="../../Includes/GenericBuildingRepair.xml" />
			<xi:include
				href="../../Includes/GenericNODBuildingSuicide.xml" />
			<xi:include
				href="../../Includes/RepairAlliesEngineerContain.xml" />
		</Behaviors>
		<AI>
			<AIUpdate
				id="ModuleTag_AIUpdate"
				AutoAcquireEnemiesWhenIdle="YES"
				AILuaEventsList="NodObeliskEvents">
				<UnitAITargetChooserData
					SympathyRange="25.0" />
			</AIUpdate>
		</AI>
		<Body>
			<ActiveBody
				id="ModuleTag_ActiveBody"
				MaxHealth="7500.0" />
		</Body>
		<ClientBehaviors>
			<ModelConditionAudioLoopClientBehavior id="ModuleTag_NOD_Obelisk_LaserChargeUp">
				<ModelConditionSound Sound="NOD_Obelisk_LaserChargeUp" RequiredFlags="PREATTACK_A" ExcludedFlags="DYING" />
				<ModelConditionSound Sound="NOD_Obelisk_LaserChargeUp" RequiredFlags="PREATTACK_C" ExcludedFlags="DYING" />
			</ModelConditionAudioLoopClientBehavior>
		</ClientBehaviors>
		<Geometry
			IsSmall="false">
			<Shape
				Type="CYLINDER"
				MajorRadius="18.0"
				Height="10.0"></Shape>
			<Shape
				Type="CYLINDER"
				MajorRadius="13.0"
				Height="10.0">
				<Offset
					x="0"
					y="0"
					z="10" />
			</Shape>
			<Shape
				Type="CYLINDER"
				MajorRadius="10.0"
				Height="10.0">
				<Offset
					x="0"
					y="0"
					z="20" />
			</Shape>
			<Shape
				Type="CYLINDER"
				MajorRadius="7.0"
				Height="15.0">
				<Offset
					x="2"
					y="0"
					z="30" />
			</Shape>
			<Shape
				Type="CYLINDER"
				MajorRadius="5.0"
				Height="15.0">
				<Offset
					x="5"
					y="0"
					z="45" />
			</Shape>
			<Shape
				Type="CYLINDER"
				MajorRadius="4.0"
				Height="14.0">
				<Offset
					x="9"
					y="0"
					z="60" />
			</Shape>
			<ContactPoint>
				<Pos
					x="-2.0"
					y="2.0"
					z="40" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="5.0"
					y="5.0"
					z="0" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="2.0"
					y="2.0"
					z="40" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="-5.0"
					y="-5.0"
					z="0" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="2.0"
					y="-2.0"
					z="40" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="5.0"
					y="-5.0"
					z="0" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="-2.0"
					y="-2.0"
					z="40" />
			</ContactPoint>
			<ContactPoint>
				<Pos
					x="-5.0"
					y="5.0"
					z="0" />
			</ContactPoint>
		</Geometry>
		<AudioArrayVoice>
			<AudioEntry
				Sound="NOD_ObeliskOfLightSelect"
				AudioType="voiceSelect" />
		</AudioArrayVoice>
		<AudioArraySound>
			<AudioEntry Sound="HumanFaction_SmallBuilding_LightDamageMS" AudioType="soundOnDamaged" />
			<AudioEntry Sound="HumanFaction_SmallBuilding_HeavyDamageMS" AudioType="soundOnReallyDamaged" />
		</AudioArraySound>
		<ShadowInfo
			Type="VOLUME" />
		<VisionInfo
			VisionRange="450"
			ShroudClearingRange="500" />
		<ProjectedBuildabilityInfo
			Radius="10.0"
			BuildPlacementTypes="MAIN_STRUCTURE OTHER_STRUCTURE" />
	</GameObject>

 	<WeaponTemplate
		id="NODObeliskLaserSupport"
		Name="NODObeliskLaserSupport"
		AttackRange="375.0"
		CanFireWhileMoving="false"
		WeaponSpeed="999999.0"
		FireSound="NOD_Obelisk_LaserFireMS"
		RadiusDamageAffects="ALLIES"
		AcceptableAimDelta="180d"
		PreAttackType="PER_SHOT"
		ReAcquireDetailType="PRE_FIRE"
		ClipSize="1"
		AutoReloadsClip="AUTO">
		<PreAttackDelay
			MinSeconds="2s"
			MaxSeconds="2s" />
		<FiringDuration
			MinSeconds="1s"
			MaxSeconds="1s" />
		<ClipReloadTime
			MinSeconds="1.0s"
			MaxSeconds="1.0s" />
		<Nuggets>
			<ActivateLaserNugget
				Lifetime="0.75s"
				LaserId="1">
				<SpecialObjectFilter
					Rule="NONE"
					Relationship="SAME_PLAYER">
					<IncludeThing>NODObelisk</IncludeThing>
				</SpecialObjectFilter>
			</ActivateLaserNugget>
			<DamageNugget
				Damage="0.0"
				Radius="0.0"
				DelayTimeSeconds="0.1s"
				DamageType="CANNON"
				DamageFXType="NOD_LASER"
				DeathType="NORMAL">
				<SpecialObjectFilter
					Rule="NONE"
					Relationship="SAME_PLAYER">
					<IncludeThing>NODObelisk</IncludeThing>
				</SpecialObjectFilter>
			</DamageNugget>
		</Nuggets>
	</WeaponTemplate>
	
	<WeaponTemplate
		id="PrismTowerSupportWeapon"
		Name="PrismTowerSupportWeapon"
		RadiusDamageAffects="ALLIES"
		AttackRange="15000.0">
		<Nuggets>
			<AttributeModifierNugget
				Radius="15000"
				PartitionFilterTestType="SPHERE"
				AttributeModifierName="AttributeModifier_PrismTowerSupport">
				<SpecialObjectFilter
					Rule="ANY"
					Relationship="SAME_PLAYER">
					<IncludeThing>NODObelisk</IncludeThing>
				</SpecialObjectFilter>
			</AttributeModifierNugget>			
		</Nuggets>
	</WeaponTemplate>	
	
	<AttributeModifier
		id="AttributeModifier_PrismTowerSupport"
		Category="BUFF"
		StackingLimit="999"
		Duration="4.0s">
		<Modifier Type="DAMAGE_MULT" Value="100%"/>
	</AttributeModifier>
	
<!-- actually not needed if solution via tertiary weapon -->
	
	<SpecialPowerTemplate 
		id="SpecialPower_PrismForwarding" 
		Flags="NEEDS_OBJECT_FILTER" 
		TargetType="OBJECT">
		<ObjectFilter Relationship="SAME_PLAYER" Rule="NONE">
			<IncludeThing>NODObelisk</IncludeThing>
		</ObjectFilter>
	</SpecialPowerTemplate>
	
	<UnitAbilityButtonTemplate
		id="ButtonSpecialPower_PrismForwarding"
		LogicCommand="Command_SpecialPower_PrismForwarding">
		<Data>
			<TargetedSpecialPower
				StateData="ButtonStateSpecialPower_PrismForwarding"
				ValidTargetCursor="Bombard" />
		</Data>
	</UnitAbilityButtonTemplate>
	
	<ButtonSingleStateData
		id="ButtonStateSpecialPower_PrismForwarding">
		<State
			Image="Portrait_NODObeliskofLight"
			Title="Name:NODObelisk"/>
	</ButtonSingleStateData>
	
	<LogicCommand
		Options="NEED_TARGET_ALLY_OBJECT"
		Type="SPECIAL_POWER"
		id="Command_SpecialPower_PrismForwarding">
		<SpecialPower>SpecialPower_PrismForwarding</SpecialPower>
	</LogicCommand>
	
	<LogicCommandSet
		id="NODObeliskCommandSet">
		<Cmd>Command_SpecialPower_PrismForwarding</Cmd>
	</LogicCommandSet>
  
</AssetDeclaration>

 

Edited by Mjjstral
  • Upvote 1

Share this post


Link to post

Thanks for your detailed input!

I am back to errors again, I am using the 'NAME_ATTACK_NAMED' method.

Below is the error:

Quote

ErrorLogToFile(05/27/17 19:06:25): error: attempt to index global `GetObj' (a nil value)
stack traceback:
   1:  function `CascadeForwarding' at line 48 [string "Data\Scripts\Scripts.lua"]

Line 48 is:  self=GetObj.Table(self)

Here is the full Lua code:

function GetMyMainTower(object)
	for q,p in MainTowerSupporterList do 
		if q~=tostring(object) then
			for i=1,getn(p),1 do 
				if p[i]==object then 
					return p[1]
				end
			end
		end
	end
end

function IsTowerStillAttacking(MainTower)
	if ObjectTestModelCondition(MainTower,"ATTACKING") or ObjectTestModelCondition(MainTower,"PREATTACK_A") or ObjectTestModelCondition(MainTower,"RELOADING_A") or ObjectTestModelCondition(MainTower,"FIRING_A") then return true else return false end
end
	
function NodObelisk_RELOADING(self)
	local TowerAttackRange = 375
	if MainTowerSupporterList[tostring(self)]~=nil then --and ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then
		for i=2,getn(MainTowerSupporterList[tostring(self)]),1 do
			ExecuteAction("NAMED_STOP",MainTowerSupporterList[tostring(self)][i])
		end
		--MainTowerSupporterList[tostring(self)]=nil
	else
		if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then ExecuteAction("NAMED_STOP",self) end
		local MainTower = GetMyMainTower(self)
		if not IsTowerStillAttacking(MainTower) then 
			MainTowerSupporterList[tostring(MainTower)]=nil
		else 
			ExecuteAction("NAMED_STOP",self)
			ExecuteAction("NAMED_ATTACK_NAMED",self,MainTowerSupportConnectionList[tostring(MainTower)][tostring(self)]) 
		end	
	end
end

function getn2(table)
   local size = 0
   for k,v in table do size=size+1 end
   return size
end

MainTowerSupporterList= {}
MainTowerSupportConnectionList= {}

function CascadeForwarding(self) --made for "Madin" (cncnz forums)
	self=GetObj.Table(self)
	local IsObjectInSupporterList = function(object)
		for q,p in MainTowerSupporterList do  --to test if a tower already supports another tower
			if q~=tostring(%self) then
				for i=1,getn(p),1 do if p[i]==object then return true end end
			end
		end
		return false
	end	
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 and IsObjectInSupporterList(self) then return end
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if ObjectCountNearbyEnemies(v,TowerAttackRange)==0 and EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) then
				if not IsObjectInSupporterList(v) then tinsert(ObjectList,v) end
			end
		end
	end
  	tinsert(ObjectList,self)
	if getn(ObjectList)<2 then return end
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	local i = 2
	local ObjectListSize
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode, NextMainNodeAlternative  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j and ObjectList[j]~=nil then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							NextMainNodeAlternative=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end
		if MainNodeCriteria == true then 
			if TowerAttackRange > DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])] then
				tinsert(MainNodeTable,ObjectList[i])
				SupportConnection[tostring(ObjectList[i])]=NextMainNode
			else
				ObjectList[i]=nil
			end
		else 
			if TowerAttackRange > ShortestLinkToNextMainNodeDistance then 
				SupportConnection[tostring(ObjectList[i])]=NextMainNodeAlternative
			else 
				ObjectList[i]=nil
			end
		end
	end	
	--remove all unneccasary towers
	local CleanedObjectList = {}
	for i=1,getn(ObjectList),1 do
		if not ObjectList[i]==nil then tinsert(CleanedObjectList,ObjectList[i]) end
	end
	ObjectList=CleanedObjectList
	MainTowerSupporterList[tostring(self)] = CleanedObjectList
	MainTowerSupportConnectionList[tostring(self)]=SupportConnection
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		--ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		ExecuteAction("NAMED_STOP",ObjectList[i])
		ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

function _ALERT(message)
	ExecuteAction("SHOW_MILITARY_CAPTION", "\n\n\n\n" .. tostring(message) .. "\n", 20)
	local file = ""
	local filehandle = openfile("ErrorLog",'a')
	if filehandle ~= nil then
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)
	else
		local filehandle = writeto(file)
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)  
	end
	flush(filehandle)
	closefile(filehandle)
end

That error log and message is really helpful!

Share this post


Link to post

Ok you also need to include all functions from previous posts. And be aware to always take the most recent functions I've posted. It can't find the GetObj table in which we stored the functions like GetObj.String and more. See above, we declared GetObj = {}.

  • Upvote 1

Share this post


Link to post
30 minutes ago, Mjjstral said:

Ok you also need to include all functions from previous posts. And be aware to always take the most recent functions I've posted. It can't find the GetObj table in which we stored the functions like GetObj.String and more. See above, we declared GetObj = {}.

OK thanks!

3 hours ago, Mjjstral said:

The very strange thing for me was, that every second support beam, does damage to the tower they support. This can probably get solved by reissuing the attack command precisely after each attack.

Same happening for me!

for i=2,getn(ObjectList),1 do		--ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		ExecuteAction("NAMED_STOP",ObjectList[i])
		ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
                ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])	
	end

?

If the main 'PRIMARY' weapon cannot damage Obelisk from the same team, will it cause code issues?

Edited by Madin
Question

Share this post


Link to post
Quote

If the main 'PRIMARY' weapon cannot damage Obelisk from the same team, will it cause code issues?

That was my idea too, just try it. Should't interfere with the script. Or use the special power ^_^

  • Upvote 1

Share this post


Link to post
55 minutes ago, Madin said:

If the main 'PRIMARY' weapon cannot damage Obelisk from the same team, will it cause code issues?

Apparently the answer is that it will cause code issues! The Obelisk get stuck in a support loop eventually. They start off targeting enemy units. But once a the enemy units stop coming, the Obelisk start firing on each other.

I am going to have another attempt at the special power, I do not even understand why it is not working.

Share this post


Link to post

For whatever reason, I cannot get a 'Weaponfireabilityupdate' to work. I manually tested a modified 'Beamabilityupdate' and it worked:

      <SpecialPower id="ModuleTag_PrismForwardingSpecialPower" SpecialPowerTemplate="SpecialPower_PrismForwarding" UpdateModuleStartsAttack="true"></SpecialPower>
      <BeamSpecialAbilityUpdate id="ModuleTag_ChargeDefensesSPUpdate" SpecialPowerTemplate="SpecialPower_PrismForwarding" JoinWithOtherBeams="false" UnpackTime="2s" UnpackSound="NOD_Obelisk_LaserChargeUp" PrepSoundLoop="NOD_Obelisk_LaserFireMS" PackTime="1s" PreparationTime="0.5s" StartAbilityRange="375.0" TargetSamePlayerOnly="true" Options="CHECK_SPECIALPOWER_REQUIREMENTS_DURING_UPDATE" PreferredTargetBone="FXBONE01" DamagePerSecond="0.0">
        <!--TargetAttributeModifierAdd="AttributeModifier_ChargeDefenses"-->
      </BeamSpecialAbilityUpdate>

However, when I attempt to use it with the LUA script, nothing happens! (no errors, just no support beam).

Script:

GetObj={}

function GetObj.String(object)       
   if type(object) == "string" then return object
   else 
	    local _, count = gsub(ObjectDescription(object),"%(","")
	    if count>1 then
          return strsub(ObjectDescription(object), strfind(ObjectDescription(object),"(",1,true)+1,strfind(ObjectDescription(object),")",1,true)-1)
        else
		  local StrRef = "object" .. RandomString(5)
		  ExecuteAction("SET_UNIT_REFERENCE", StrRef, object)
		  return StrRef
		end
	end
end

function GetObj.Table(object)
    if type(object) == "table" then return object
	else  
		for k,v in globals() do
		  if strfind(k,"ObjID") ~= nil and strfind(ObjectDescription(v),object) then
			return rawget(globals(),k) 
		  end
		end
		return object
	end
end

function GetObj.Hash(object)
    return tostring(tonumber(strsub(ObjectTemplateName(GetObj.Table(object)), 6,  13), 16))
end

CompareTable    	  = { LT=0, LE=1, EQ=2, GE=3, GT=4, NE=5, ["<"]=0, ["<="]=1, ["=="]=2, [">="]=3, [">"]=4, ["~="]=5 } 

function GetObjectDistance(object1 ,object2, start_distance)
 if object1==nil or object2==nil or not EvaluateCondition("NAMED_NOT_DESTROYED",object1) or not EvaluateCondition("NAMED_NOT_DESTROYED",object2) then return 0 end
 object1=GetObj.String(object1)
 object2=GetObj.String(object2)
 local v = {}
	v.GetDistance = function(lowerlimit,upperlimit)
	   local mid=(upperlimit+lowerlimit)/2
	   if upperlimit-lowerlimit<=1 then return lowerlimit
	   elseif EvaluateCondition("DISTANCE_BETWEEN_OBJ", %object1, %object2, CompareTable["<"], mid) then return %v.GetDistance(lowerlimit,mid)
	   else return round(%v.GetDistance(mid,upperlimit)) end
	end
 if start_distance then return v.GetDistance(0,tonumber(start_distance)) 
 else return v.GetDistance(0,12870) end   --maxdiagsize=ceil(sqrt(2*(7500+2*800)^2)))
end

function random(...) --overwritting lua native function for multiplayer compatibility 
	local RandomNumber = function(a,b) return floor(a+((b-a)*GetRandomNumber())+0.5) end
	if getn(arg) == 0 then return floor(GetRandomNumber()+0.5)
	elseif getn(arg) == 1 then return RandomNumber(1,arg[1])
	elseif getn(arg) == 2 then return RandomNumber(arg[1],arg[2])
	else return arg[RandomNumber(1,getn(arg))] end
end  

function RandomString(l)
        if l < 1 then l=5 end
        local s = ""
		local ascii_zone = {}
		for i=1,l,1 do
		  ascii_zone[1] = random(97, 122)
		  ascii_zone[2] = random(65, 90)
		  ascii_zone[3] = random(48, 57)
          s = s .. strchar(ascii_zone[random(3)])
        end
        return ("_" .. s)
end
function GetMyMainTower(object)
	for q,p in MainTowerSupporterList do 
		if q~=tostring(object) then
			for i=1,getn(p),1 do 
				if p[i]==object then 
					return p[1]
				end
			end
		end
	end
end

function IsTowerStillAttacking(MainTower)
	if ObjectTestModelCondition(MainTower,"ATTACKING") or ObjectTestModelCondition(MainTower,"PREATTACK_A") or ObjectTestModelCondition(MainTower,"RELOADING_A") or ObjectTestModelCondition(MainTower,"FIRING_A") then return true else return false end
end
	
function NodObelisk_RELOADING(self)
	local TowerAttackRange = 375
	if MainTowerSupporterList[tostring(self)]~=nil then --and ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then
		for i=2,getn(MainTowerSupporterList[tostring(self)]),1 do
			ExecuteAction("NAMED_STOP",MainTowerSupporterList[tostring(self)][i])
		end
		--MainTowerSupporterList[tostring(self)]=nil
	else
		if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 then ExecuteAction("NAMED_STOP",self) end
		local MainTower = GetMyMainTower(self)
		if not IsTowerStillAttacking(MainTower) then 
			MainTowerSupporterList[tostring(MainTower)]=nil
		else 
			ExecuteAction("NAMED_STOP",self)
			ExecuteAction("NAMED_ATTACK_NAMED",self,MainTowerSupportConnectionList[tostring(MainTower)][tostring(self)]) 
		end	
	end
end

function getn2(table)
   local size = 0
   for k,v in table do size=size+1 end
   return size
end

function round(number)

    return floor(number+0.5)

end

MainTowerSupporterList= {}
MainTowerSupportConnectionList= {}

function CascadeForwarding(self) --made for "Madin" (cncnz forums)
	self=GetObj.Table(self)
	local IsObjectInSupporterList = function(object)
		for q,p in MainTowerSupporterList do  --to test if a tower already supports another tower
			if q~=tostring(%self) then
				for i=1,getn(p),1 do if p[i]==object then return true end end
			end
		end
		return false
	end	
	local MaximumSupportDistance = 13000
	local TowerAttackRange = 375 --here attack range of nod obelisk
	if ObjectCountNearbyEnemies(self,TowerAttackRange)==0 and IsObjectInSupporterList(self) then return end
	local ObjectList = {}
	local DistanceTable = {}
	local SupportConnection={}
	--create object list of all support towers
	for k,v in globals() do
		if strfind(k,"ObjID") and GetObj.Hash(self)==GetObj.Hash(v) and self~=v and ObjectTeamName(self)==ObjectTeamName(v) then
			if ObjectCountNearbyEnemies(v,TowerAttackRange)==0 and EvaluateCondition("DISTANCE_BETWEEN_OBJ", GetObj.String(self),GetObj.String(v), CompareTable["<="], MaximumSupportDistance) then
				if not IsObjectInSupporterList(v) then tinsert(ObjectList,v) end
			end
		end
	end
  	tinsert(ObjectList,self)
	if getn(ObjectList)<2 then return end
	--create 2d matrix with distances
	for i=1,getn(ObjectList),1 do
		DistanceTable[tostring(ObjectList[i])]={}
	end
	for i=1,getn(ObjectList),1 do
		for j=i,getn(ObjectList),1 do
			if i==j then 
				DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=0
			else 
				if DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])] == nil then
					DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]=GetObjectDistance(GetObj.String(ObjectList[i]),GetObj.String(ObjectList[j]))
					DistanceTable[tostring(ObjectList[j])][tostring(ObjectList[i])]=DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]	
				end
			end
		end	
	end
	--sort object list by distance to main tower
	local SortHelper=function(Object1,Object2)
		if %DistanceTable[tostring(%self)][tostring(Object1)] < %DistanceTable[tostring(%self)][tostring(Object2)] then return true end
	end
	sort(ObjectList,SortHelper)
	--main part: distribute one tower for each tower to support
	local MainNodeTable = {}
	tinsert(MainNodeTable,self)
	local i = 2
	local ObjectListSize
	for i=2,getn(ObjectList),1 do
		local MainNodeCriteria = true
		local NextMainNode, NextMainNodeAlternative  --closest main node
		local NextMainNodeDistance = 13000
		for k=1,getn(MainNodeTable),1 do
			if NextMainNodeDistance > DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])] then
				NextMainNodeDistance = DistanceTable[tostring(MainNodeTable[k])][tostring(ObjectList[i])]
				NextMainNode = MainNodeTable[k]
			end
		end		
		local ShortestLinkToNextMainNodeDistance = 13000
		for j=3,getn(ObjectList),1 do	
			if not i==j and ObjectList[j]~=nil then
				local iNodeTojNodeDistance = DistanceTable[tostring(ObjectList[i])][tostring(ObjectList[j])]
				local iNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])]
				local jNodeToNextMainNodeDistance = DistanceTable[tostring(NextMainNode)][tostring(ObjectList[j])]
				if jNodeToNextMainNodeDistance < iNodeToNextMainNodeDistance then 
					if iNodeTojNodeDistance < iNodeToNextMainNodeDistance then
						MainNodeCriteria = false
						local LinkToNextMainNodeDistance = iNodeTojNodeDistance + jNodeToNextMainNodeDistance
						if ShortestLinkToNextMainNodeDistance > LinkToNextMainNodeDistance then
							NextMainNodeAlternative=ObjectList[j]
							ShortestLinkToNextMainNodeDistance = LinkToNextMainNodeDistance
						end
					end
				end
			end
		end
		if MainNodeCriteria == true then 
			if TowerAttackRange > DistanceTable[tostring(NextMainNode)][tostring(ObjectList[i])] then
				tinsert(MainNodeTable,ObjectList[i])
				SupportConnection[tostring(ObjectList[i])]=NextMainNode
			else
				ObjectList[i]=nil
			end
		else 
			if TowerAttackRange > ShortestLinkToNextMainNodeDistance then 
				SupportConnection[tostring(ObjectList[i])]=NextMainNodeAlternative
			else 
				ObjectList[i]=nil
			end
		end
	end	
	--remove all unneccasary towers
	local CleanedObjectList = {}
	for i=1,getn(ObjectList),1 do
		if not ObjectList[i]==nil then tinsert(CleanedObjectList,ObjectList[i]) end
	end
	ObjectList=CleanedObjectList
	MainTowerSupporterList[tostring(self)] = CleanedObjectList
	MainTowerSupportConnectionList[tostring(self)]=SupportConnection
	--fire cumulative attribute modifier weapon on main tower (self)
	for i=2,getn(ObjectList),1 do
		ObjectCreateAndFireTempWeapon(self,"PrismTowerSupportWeapon")
	end		
	--fire special power to display visual support beams
	for i=2,getn(ObjectList),1 do
		ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",ObjectList[i],"SpecialPower_PrismForwarding",SupportConnection[tostring(ObjectList[i])])	
		-- ExecuteAction("NAMED_STOP",ObjectList[i])
		-- ExecuteAction("NAMED_ATTACK_NAMED",ObjectList[i],SupportConnection[tostring(ObjectList[i])])		
	end
end

function _ALERT(message)
	ExecuteAction("SHOW_MILITARY_CAPTION", "\n\n\n\n" .. tostring(message) .. "\n", 20)
	local file = ""
	local filehandle = openfile("ErrorLog",'a')
	if filehandle ~= nil then
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)
	else
		local filehandle = writeto(file)
		write(filehandle,"\n" .. "ErrorLogToFile(" .. date() .. "): " .. message)  
	end
	flush(filehandle)
	closefile(filehandle)
end

Am I doing something wrong? (maybe it has to be a 'WeaponFireSpecialAbilityUpdate' to work?)

or does it have to be done using 'NAME_ATTACK_NAMED'?

Edited by Madin

Share this post


Link to post

In general your options for special powers/commandbuttons/abilities/attacking are:

ExecuteAction("NAMED_FIRE_SPECIAL_POWER",...)
ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",...)
ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_WAYPOINT",...)
ExecuteAction("NAMED_FIRE_WEAPON_FOLLOWING_WAYPOINT_PATH",...)
ExecuteAction("NAMED_FIRE_METAGAME_OP_INSTANT",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY_AT_WAYPOINT",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_ENEMY_BUILDING",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_ENEMY_BUILDING_CLASS",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_ENEMY_UNIT",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_GARRISONED_BUILDING",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_KINDOF",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_OBJECTTYPE",...)
ExecuteAction("NAMED_ATTACK_NAMED",...)
ObjectDoSpecialPower(object,specialpowername)

Try to figure out a way to use your sp with these commands, else WeaponFireSpecialAbilityUpdate could be mandatory/easier to use ?

ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED",...) looks promising.

Quote

or does it have to be done using 'NAME_ATTACK_NAMED'?

No the code will work with a special power too, but uncomment "--MainTowerSupporterList[tostring(self)]=nil" in NodObelisk_RELOADING function then.

Edited by Mjjstral

Share this post


Link to post

Just a sidenote observation: You can trigger an untargeted special power in 4 ways, it's crazy:

ExecuteAction("NAMED_FIRE_SPECIAL_POWER",...)
ExecuteAction("NAMED_FIRE_METAGAME_OP_INSTANT",...)
ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY",...)
ObjectDoSpecialPower(object,specialpowername)

And a lame single forwarded prism forwarding could be achieved with

ExecuteAction("NAMED_USE_COMMANDBUTTON_ON_NEAREST_OBJECTTYPE",...)

Plus some testings of course.

Share this post


Link to post

For whatever reason, the Special power method is doing nothing. I tried 'NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED', and also tried a test with a ' WeaponFireSpecialAbilityUpdate', using both 'NAMED_FIRE_SPECIAL_POWER_AT_NAMED' and 'NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED' methods. I used a power that I was sure worked (Orca targeted sensor pod), tested the button to make sure it worked, and after all that the Lua script did nothing with it.

I am going back to the attack method.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×