Madin 10 Posted November 12, 2017 I am looking to add a passive support ability to the Iron Curtain structure, and I was wondering what the best way to go about it would be. The ability would basically fire an RA1 style Iron Curtain (ie a single vehicle only version), at a Harvester on the same team whose health had gone below 25%. This passive ability would be ready every 30 seconds, and the Iron Curtain effect would last 30 seconds (ie it could only protect one Harvester at a time). The ability would be completely passive, the player would have no control over it at all. Obviously if the Iron Curtain structure was on low power, or had been manually powered down, the ability would not work. Any ideas how I could best implement this? Share this post Link to post
Ravendark 43 Posted November 12, 2017 From the top of my head: Have the ironcurtain use a fireweaponupdate with a fireonobject nugget, target = PLAYER harvesters. with a ForbiddenTargetModelCondition="PRISTINE RUBBLE" so it only fires when the harvester is DAMAGED/REALLY_DAMAGED ? Give the weapon a AttributeModifierNugget that does the actual ironcurtain effect and invulnerability stuff. Have a dummy specialability SP. with a 30 second recharge, use the MonitorSpecialPowerTimerUpdate to set a modelcondition like (SPECIALPOWER1_READY), use that modelcondition to trigger a status through lua that enables/disables the weapon by using ForbiddenFiringObjectStatus or RequiredFiringObjectStatus in the weapon. Trigger the dummy sp to fire itself on whenever the ironcurtain attacks/fires its weapon (ATTACKING condition should trigger of the Fireweapon module iirc) , have that condition trigger the Dospecialpower function in lua firing the SP. Alternatively you might work of your prismforwarding coding, take the fire weapon on object coding in there and adapt it to fire on harvesters? Maybe... Share this post Link to post
Madin 10 Posted November 12, 2017 4 hours ago, Ravendark said: From the top of my head: Have the ironcurtain use a fireweaponupdate with a fireonobject nugget, target = PLAYER harvesters. with a ForbiddenTargetModelCondition="PRISTINE RUBBLE" so it only fires when the harvester is DAMAGED/REALLY_DAMAGED ? Give the weapon a AttributeModifierNugget that does the actual ironcurtain effect and invulnerability stuff. Have a dummy specialability SP. with a 30 second recharge, use the MonitorSpecialPowerTimerUpdate to set a modelcondition like (SPECIALPOWER1_READY), use that modelcondition to trigger a status through lua that enables/disables the weapon by using ForbiddenFiringObjectStatus or RequiredFiringObjectStatus in the weapon. Trigger the dummy sp to fire itself on whenever the ironcurtain attacks/fires its weapon (ATTACKING condition should trigger of the Fireweapon module iirc) , have that condition trigger the Dospecialpower function in lua firing the SP. Alternatively you might work of your prismforwarding coding, take the fire weapon on object coding in there and adapt it to fire on harvesters? Maybe... Thanks for your input! I do wonder about the 'fireweaponupdate' and 'fireonobject' nugget, as I have found them unreliable in the past. Share this post Link to post
Ravendark 43 Posted November 12, 2017 Very true, they can be a bit spammy depending on conditions. But in your case you are applying a very strong filter. Dmg state + none-spammy unit + range (perhaps), so there might be the occasional multiple harvesters getting targeted at the same time by your ironcurtain, but that would mean multiple harvester drop down in health to the dmg condition at the exact same moment (most likely if multiple harvesters get hit by a aoe of magnitude like a nuke or so). But like i said, that's from the top of my mind, without having worked on something similar. Probably better solutions out there. Share this post Link to post
Mjjstral 25 Posted November 14, 2017 (edited) My solution assumes that a player will only have 1 iron curtain building at the same time (code adaption for more than 1 possible). 1. Make it a special power that triggers a weapon and then an attribute modifier. Use Ravendarks idea to trigger a SPECIALPOWER1_READY lua event that calls the function OnIronCurtainReady. On 12.11.2017 at 1:10 PM, Ravendark said: Have a dummy specialability SP. with a 30 second recharge, use the MonitorSpecialPowerTimerUpdate to set a modelcondition like (SPECIALPOWER1_READY), 2. In additon for the harvesters use these events to trigger the functions OnHarvesterHealth25Percent and OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead. <ModelConditionEvent Name="HEALTH_PERCENT_25"> <Conditions>+HEALTH_PERCENT_25</Conditions> </ModelConditionEvent> <ModelConditionEvent Name="HEALTH_PERCENT_25_NOT"> <Conditions>-HEALTH_PERCENT_25</Conditions> </ModelConditionEvent> 3. Now the lua code: IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self IronCurtain["Targets"][ObjectTeamName(self)] = {} IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true if getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPower_IronCurtain",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25Percent(self) tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(self,"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPower_IronCurtain",GetObj.String(self)) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end end function GetIndexOfTableElement(table,element) for i=1,getn(table),1 do if table[i]==element then return i end end end So you need in total 5 lua events (3 for the structure and 2 for the harvesters). 4. optionaly if you want to always attack the harbester with the lowest health from the pool of all harvesters that have health < 25: function GetMinHealthUnit(UnitTable) if getn(UnitTable) == 0 then return nil end local MinHealthUnitRef = UnitTable[1] local MinHealth = GetHealth(UnitTable[1]) for i=2,getn(UnitTable),1 do if GetHealth(UnitTable[i]) < MinHealth then MinHealthUnitRef = UnitTable[i] end end return MinHealthUnitRef end function GetHealth(Unit) local UnitRef = GetObj.String(Unit) for i=1,101,1 do if EvaluateCondition("UNIT_HEALTH", UnitRef, CompareTable["<"], i) then return i-1 end end return 0 end So replace GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1]) with GetObj.String(GetMinHealthUnit(IronCurtain["Targets"][ObjectTeamName(self)])) Edited November 14, 2017 by Mjjstral Share this post Link to post
Mjjstral 25 Posted November 14, 2017 And here is an alternative I made earlier but you can disregard. It uses lua script timers (meta mod) and gets the harvester refs automatically. So it's a more lua self contained solution. IronCurtainBuildingList = {} function IronCurtainBuildingOnCreated(self) tinsert(IronCurtainBuildingList,self) SetScriptTimer(1,IronCurtainAttackManagement) end function IronCurtainBuildingOnDestroyed(self) local Index = GetIndexOfTableElement(IronCurtainBuildingList,self) if Index then tremove(IronCurtainBuildingList, Index) end DeleteScriptTimerAction(IronCurtainAttackManagement) end function IronCurtainBuildingOnPowerOutage(self) local Index = GetIndexOfTableElement(IronCurtainBuildingList,self) if Index then tremove(IronCurtainBuildingList, Index) end DeleteScriptTimerAction(IronCurtainAttackManagement) end function IronCurtainBuildingOnPowerRestore(self) tinsert(IronCurtainBuildingList,self) SetScriptTimer(1,IronCurtainAttackManagement) end IronCurtainSchedule = {} function IronCurtainAttackManagement() local IronCurtainObjectWhiteList = GetObjectTypeListForTeam(team,AddToObjectTypeList("HarvesterTypeList","GDIHarvester","NODHarvester","ALIENHarvester")) for i=1,getn(IronCurtainBuildingList),1 do if IronCurtainSchedule[IronCurtainBuildingList[i]] == 0 and not ObjectTestModelCondition(GetObj.Table(IronCurtainBuildingList[i]),"UNDERPOWERED") then for j=1,getn(IronCurtainObjectWhiteList),1 do if GetHealth(IronCurtainObjectWhiteList[j].ref) <= 25 then ObjectCreateAndFireTempWeapon(IronCurtainObjectWhiteList[j].ref,"IronCurtainWeapon") --ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED", IronCurtainBuildingList[i], "Command_IronCurtainWeapon", IronCurtainObjectWhiteList[j].ref) end end else IronCurtainSchedule[IronCurtainBuildingList[i]] = IronCurtainSchedule[IronCurtainBuildingList[i]] - 1 end end end function GetObjectTypeListForTeam(team,ObjectType) --ObjectType or ObjectTypeList local RefList = {} local ObjRef = "" local y = 1 local TempObjectRefTable={} ExecuteAction("SET_IGNORE_SKIRMISH_VICTORY_CONDITIONS", 1) repeat ObjRef=RandomString(5) ExecuteAction("TEAM_SET_PLAYERS_NEAREST_UNIT_OF_TYPE_TO_REFERENCE",ObjectType,team,ObjRef) if EvaluateCondition("NAMED_NOT_DESTROYED",ObjRef) then RefList[y]={} RefList[y]["ref"]=ObjRef RefList[y]["team"]=team RefList[y]["type"]=ObjectType RefList[y]["powered"]=1 tinsert(TempObjectRefTable,ObjRef) ExecuteAction("UNIT_SET_TEAM",ObjRef,"team") y=y+1 else break end until(not EvaluateCondition("NAMED_NOT_DESTROYED",ObjRef)) for j=1,getn(TempObjectRefTable),1 do ExecuteAction("UNIT_SET_TEAM",TempObjectRefTable[j],team) end ExecuteAction("SET_IGNORE_SKIRMISH_VICTORY_CONDITIONS", 0) return RefList end function AddToObjectTypeList(ObjectTypeList,...) for i=1,getn(arg),1 do ExecuteAction("OBJECTLIST_ADDOBJECTTYPE", ObjectTypeList, arg[i]) end return ObjectTypeList end function GetHealth(input,HealthType) local HealthCompareType = "" if HealthType == nil or HealthType == "unit" or HealthType == 1 then HealthCompareType = "UNIT_HEALTH" else HealthCompareType = "EVAL_TEAM_HEALTH" end for i=1,101,1 do if EvaluateCondition(HealthCompareType, input, CompareTable["<"], i) then return i-1 end end return 0 end function GetIndexOfTableElement(table,element) for i=1,getn(table),1 do if table[i]==element then return i 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) --v end end return object end end 1 Share this post Link to post
Madin 10 Posted November 14, 2017 (edited) Thanks for your reply! I am getting an error message once the Special power used to trigger the Iron Curtain LUA is ready: Since I moved to Windows 7, I am no longer getting 'ErrorLog' text files. IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self IronCurtain["Targets"][ObjectTeamName(self)] = {} IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true if getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPower_IronCurtain",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25Percent(self) tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(self,"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPower_IronCurtain",GetObj.String(self)) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end end function GetIndexOfTableElement(table,element) for i=1,getn(table),1 do if table[i]==element then return i end end end Edited November 14, 2017 by Madin Share this post Link to post
Mjjstral 25 Posted November 15, 2017 15 hours ago, Madin said: I am getting an error message This should fix it. It's now more error robust in general: IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end if IronCurtain["Targets"][ObjectTeamName(self)] and getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and not ObjectTestModelCondition(self,"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPower_IronCurtain",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25Percent(self) if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(self,"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPower_IronCurtain",GetObj.String(self)) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end end function GetIndexOfTableElement(table,element) if not table then return nil end for i=1,getn(table),1 do if table[i]==element then return i end end end 15 hours ago, Madin said: Since I moved to Windows 7, I am no longer getting 'ErrorLog' text files. Hm I'm also on win7... Shouldn't be system dependent acually. Why does the lua code pasted in these forum text boxes appear so whitish and unreadable ? 1 Share this post Link to post
Madin 10 Posted November 15, 2017 This is still not working fully (although now there are no errors). IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end IronCurtain["Ready"][ObjectTeamName(self)] = false ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_BUILT", 5) end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_READY", 5) if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_NOT_FIRED", 5) if IronCurtain["Targets"][ObjectTeamName(self)] and getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and not ObjectTestModelCondition(self,"UNDERPOWERED") then -- ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"RA1IronCurtainInfantryDeathWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPowerIronCurtainRA1_Start",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_FIRED", 5) IronCurtain["Ready"][ObjectTeamName(self)] = false ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_FINISHED", 5) end end function OnHarvesterHealth25Percent(self) if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end ExecuteAction("SHOW_MILITARY_CAPTION", "HARVESTER_FIRST", 5) tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) ExecuteAction("SHOW_MILITARY_CAPTION", "HARVESTER_IN_TABLE", 5) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then -- ObjectCreateAndFireTempWeapon(GetObj.String(self),"RA1IronCurtainInfantryDeathWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPowerIronCurtainRA1_Start",GetObj.String(self)) ExecuteAction("SHOW_MILITARY_CAPTION", "IRONCURTAIN_HARVESTER", 5) IronCurtain["Ready"][ObjectTeamName(self)] = false ExecuteAction("SHOW_MILITARY_CAPTION", "HARVESTER_CODE_FINISHED", 5) end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end ExecuteAction("SHOW_MILITARY_CAPTION", "HARVESTER_NOT_IN_TABLE", 5) end function GetIndexOfTableElement(table,element) if not table then return nil end for i=1,getn(table),1 do if table[i]==element then return i end end end The parts of the code that are supposed to fire either a 'Special Power' or weapon, do not work. The test messages before that point do play. The Harvester gets to the "HARVESTER_IN_TABLE" message when its gets to 25% health or lower, and gets the "HARVESTER_NOT_IN_TABLE" message when it goes above 25% health (they have self heal). When the Iron Curtain comes to ready, it will correctly get to the "IRONCURTAIN_NOT_FIRED" message (I have not tried having the Harvesters damaged before the Iron Curtain special power is ready). Share this post Link to post
Madin 10 Posted September 28, 2019 Cannot get this to work unfortunately. Can anyone see any issues with the code? Share this post Link to post