A lot of Ideas for Workshop

I’ve always been a huge fan of the Overwatch Workshop. To the point where it’s my most-played mode in both OW1 and OW2 (100+ hours). I’ve created a lot of scripts, some of which got a bit of attention at certain times (custom bosses, random modifiers, etc.).

And I think we could do so much more if we had a few additional features.

Apply a Skin to an AI

Useful for RPG-style game modes like boss fights. Many skins would fit perfectly.

Force an AI to Perform an Emote

Again, very useful for RPG modes. I’ve seen some heroes with really interesting emotes.

“All Players Except” Condition

I’ve noticed that scripting modes like Deathmatch is much more tedious than team-based modes. The reason is simple: the Workshop doesn’t offer a way to return a single player from the rest. I know there are workarounds (like using arrays), but I find it unnecessarily complicated. This condition would make things much simpler.

Place an Asset at a Location

By “asset,” I mean any non-living entity, such as Junkrat’s trap, Mei’s wall, etc. We could define which team the asset belongs to, its lifespan, and other properties.

Summon an Entity

This would allow summoning an entity other than a hero, like enemies from PvE modes. If Blizzard has given up on developing a full-fledged PvE experience for Overwatch, giving the community the tools to do it themselves would be a great compromise. I think this could finally put an end to the frustration some players have over broken promises, as the community would take things into its own hands.

Modify Reload Speed

Some heroes already have this ability, especially with the introduction of perks. So why not add it to the Workshop?

Force an AI to Follow a Path

Technically, this is already possible, but like other features mentioned earlier, it’s time-consuming and complicated. This effect would allow defining a list of vectors for the entity to follow, connecting the points one after another. A “Loop” option could be included to make the AI continuously repeat the path once it returns to the starting point.

Force an Old Ability

This would allow replacing a hero’s ability with an older version from before a rework (e.g., Sombra’s teleporter, Orisa’s gravity projectile, etc.).

Force the Use of Another Hero’s Ability

This would make a player or AI use an ability from a different hero. There wouldn’t be any animations since hero animations aren’t designed for other characters, but the physics, damage, and mechanics would still work. This could be useful for things like giving a hero Torbjorn’s turret, for example.

Ability to Add Tags to Custom Games

A predefined list of tags set by Blizzard would be useful. For example: “PvP,” “PvE,” “RPG,” “Fun,” “Competitive,” “Arcade,” etc. This would allow players browsing the custom games list to quickly identify the game modes that interest them, using a filtering system.

Earning Battle Pass XP When Others Play Your Script

The amount wouldn’t be huge (e.g., 2,000 or 3,000 XP) and would only apply once per season. The goal is simply to reward players who share a script that becomes popular in the community. Keeping the XP reward small and limiting it to once per season would prevent abuse—after all, why go to great lengths for such a small amount? It would just be a little bonus.

An In-Game Text Editor

Some websites offer code editors, but unfortunately, we can’t export our code to Overwatch, which is a bit frustrating. It would be great to have a built-in text editor for faster scripting. Alternatively, Blizzard could approve external sites (like workshop.codes) to allow direct code imports, since they already provide efficient editors.

6 Likes

If Blizzard took a renewed interest in the Workshop, Overwatch would be in a healthier place and would have more longevity.

3 Likes

The workshop needs an overhaul at this point, theres many bugs that have built up (like force outlines not working right) and it crashes easily in ow2. In ow1, the workshop was so well optimised, you could do crazy things and the workshop wouldnt crash.

1 Like

it always crashed a lot for me… but that was mostly from my AI that ran 11 Pathfinders & full Ability Support simultaneously, tbh it was a miracle that ran at all… but yeah it definitely struggled more after OW2. crashed more often, despite it running 9 AI instead of 11. These days i usually have to reduce it to about 5 Bots (3 per team) to get it to run. which is a major downside and also why i stopped updating it for new heroes… since its grown impossible to test.

2 Likes

Yeah it also always crashed for me, even in OW1. But they should definitely try to make it more stable. I always wanted a way to disable and enable rules mid game, like disabling an Ana rule because no one is playing Ana, but reenabling it if someone picks her

oh yeah i don’t doubt they can be better. they were homemade on trail & error in about a week (only knowledge of pathfinders at the time was a single A* Video that i watched before starting)… i didn’t really know how to make a Pathfinder, still don’t I’m pretty sure. i just really wanted to make an AI, i had been making AI for awhile at that point but none had pathfinders they were all just on Workshop Expanse, i still found it fun to make & play tho.

i don’t even know how to optimization my scripts other than moving the condition order to make it fail before doing the more complex stuff if it can & it does update every 0.1 seconds * (Server Load / 1000) or something… idk i just tried adding that to the waits once and i think it helped so i kept adding it… tbh i think in most scripts it does more harm than good (probably does more harm in every script), also i did try without diving Server Load first but then it just updated every few seconds and it was way too slow.

i also have a resource intensive Targeting System has a whole lot of filters for heros doing certain things, if hearing is on it also adds heroes doing certain actions if they are within a certain distance (i always keep this & memory off tho, as when i play i find it annoying)

Edit: Pathfinder is Wait(Max(Server Load / 1000, 0.500), Ignore Condition); i just checked the Snippet.

And i definitely dont think this Targeting Script is doing the Server any favours, but i want to give my AI the ability to ignore Genji Deflect, Reaper Wraith, Moira Fade, Zen Ult, Etc…

rule("Bot Target List")
{
	event
	{
		Ongoing - Each Player;
		All;
		All;
	}

	conditions
	{
		(Is Dummy Bot(Event Player) == True || Event Player.AI_Control_Player == True) == True;
		Has Spawned(Event Player) == True;
		Is Alive(Event Player) == True;
	}

	actions
	{
		Skip If(Global.Targeting_Type == (0 || 2), 3);
		If(Is Hero Being Played(Hero(Widowmaker), Team Of(Event Player)) && Is True For Any(Players On Hero(Hero(Widowmaker), Team Of(
			Event Player)), Is Using Ultimate(Current Array Element)));
			Event Player.Targets = All Living Players(Opposite Team Of(Team Of(Event Player)));
		Else;
			Skip If(Global.Targeting_Type == (0 || 2), 1);
			Event Player.Targets = Append To Array(Filtered Array(All Living Players(Opposite Team Of(Team Of(Event Player))),
				Is In Line of Sight(Eye Position(Event Player), Eye Position(Current Array Element), Barriers Do Not Block LOS)),
				Filtered Array(All Living Players(Opposite Team Of(Team Of(Event Player))), Event Player.Seen_By_Memory > 0));
			Skip If(Global.Targeting_Type == (0 || 1), 8);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Has Status(Current Array Element, Hacked)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Primary_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && ((Is Duplicating(Current Array Element) ? Hero Being Duplicated(
				Current Array Element) : Hero Of(Current Array Element)) == Hero(D.Va) || (Is Duplicating(Current Array Element)
				? Hero Being Duplicated(Current Array Element) : Hero Of(Current Array Element)) == Hero(Illari) ? Is Button Held(
				Current Array Element, Button(Primary Fire)) : Is Firing Primary(Current Array Element))));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Secondary_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Firing Secondary(Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Ability1_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Using Ability 1(Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Ability2_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Using Ability 2(Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Ultimate_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Using Ultimate(Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Walking_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && !Is Crouching(Current Array Element) && Is Moving(
				Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Crouch_Walk_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Crouching(Current Array Element) && Is Moving(
				Current Array Element)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Melee_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Meleeing(Current Array Element)));
			If(Global.Invisible_Targeting == 0);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(Current Array Element))
					> 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Hero Being Duplicated(Current Array Element) == Hero(Sombra) && Is Using Ability 1(Current Array Element)
					&& !Is Firing Secondary(Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(
					Current Array Element)) > 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
			Else If(Global.Invisible_Targeting == 3);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Hero Being Duplicated(Current Array Element) == Hero(Sombra)
					&& Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array));
			Else If(Global.Invisible_Targeting == 1);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element)));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element) && Hero Being Duplicated(Current Array Element) == Hero(Sombra)));
			Else;
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))), Is Using Ability 1(Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(
					Current Array Element)) > 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Is Using Ability 1(Current Array Element) && Hero Being Duplicated(Current Array Element) == Hero(Sombra)
					&& Distance Between(Eye Position(Event Player), Position Of(Current Array Element)) > 4 && Distance Between(Position Of(
					Event Player), Position Of(Current Array Element)) > 4));
			End;
		End;
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(All Players(All Teams), Has Status(
			Current Array Element, Asleep) || Is In Spawn Room(Current Array Element) || Has Status(Current Array Element, Phased Out)
			|| !Has Spawned(Current Array Element)));
		Skip If(Global.Ignore_Abilitys == True, 5);
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(D.Va),
			All Teams), Hero Being Duplicated(Event Player) == Hero(D.Va)), Is Firing Secondary(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Zarya),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Zarya)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Reaper),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Reaper)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Genji),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Genji)), Is Using Ability 2(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Sigma),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Sigma)), Is Using Ability 1(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Skip If(Global.Team_Based_Targeting == False, 25);
		If((Hero Being Duplicated(Event Player) == Hero(Zarya) || Hero Of(Event Player) == Hero(Zarya)) && Ability Cooldown(Event Player,
			Button(Ability 2)) == Null);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 0.400));
		Else If((Hero Being Duplicated(Event Player) == Hero(Moira) || Hero Of(Event Player) == Hero(Moira)) && Ability Resource(
				Event Player, Button(Primary Fire)) > 0);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Hero Being Duplicated(Event Player) == Hero(Mercy) || Hero Of(Event Player) == Hero(Mercy));
			Modify Player Variable(Event Player, Targets, Append To Array, Append To Array(Filtered Array(All Living Players(Team Of(
				Event Player)), Normalized Health(Current Array Element) < 1), Filtered Array(All Players(Team Of(Event Player)), Is Dead(
				Current Array Element))));
		Else If((Hero Being Duplicated(Event Player) == Hero(Brigitte) || Hero Of(Event Player) == Hero(Brigitte)) && Ability Charge(
				Event Player, Button(Ability 2)) > 0);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 1));
		Else If(Hero Being Duplicated(Event Player) == Hero(Baptiste) || Hero Of(Event Player) == Hero(Baptiste));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Hero Being Duplicated(Event Player) == Hero(Ana) || Hero Of(Event Player) == Hero(Ana));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If((Hero Being Duplicated(Event Player) == Hero(Lúcio) || Hero Of(Event Player) == Hero(Lúcio)) && !Is In Alternate Form(
				Event Player));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 1));
		Else If(Hero Being Duplicated(Event Player) == Hero(Kiriko) || Hero Of(Event Player) == Hero(Kiriko));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If((Hero Being Duplicated(Event Player) == Hero(Zenyatta) || Hero Of(Event Player) == Hero(Zenyatta))
				&& !Is In Alternate Form(Event Player));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(Remove From Array(All Living Players(Team Of(
				Event Player)), Event Player), Normalized Health(Current Array Element) < 1));
			Skip If(!Entity Exists(First Of(Filtered Array(Event Player.Targets, Team Of(Current Array Element) == Team Of(Event Player)))),
				1);
			Event Player.Targets = First Of(Filtered Array(Sorted Array(Event Player.Targets, Distance Between(Event Player,
				Current Array Element) * Health(Current Array Element)), Team Of(Current Array Element) == Team Of(Event Player)))
				== Event Player.Zen_Orb_Target ? Filtered Array(Event Player.Targets, Team Of(Current Array Element) != Team Of(Event Player))
				: Event Player.Targets;
		Else If(Hero Being Duplicated(Event Player) == Hero(Lifeweaver) || Hero Of(Event Player) == Hero(Lifeweaver));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Hero Being Duplicated(Event Player) == Hero(Illari) || Hero Of(Event Player) == Hero(Illari));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		End;
		Modify Player Variable(Event Player, Targets, Remove From Array By Value, Append To Array(Event Player, Filtered Array(All Players(
			All Teams), !Has Spawned(Current Array Element))));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Doomfist),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Doomfist)), Is Using Ultimate(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Moira),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Moira)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Mei),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Mei)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Zenyatta),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Zenyatta)), Is Using Ultimate(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Tracer),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Tracer)), Is Using Ability 2(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(All Living Players(Opposite Team Of(Team Of(
			Event Player))), !Is On Objective(Current Array Element) && Is Communicating Any Emote(Current Array Element)));
		Event Player.Target_Order = Sorted Array(Event Player.Targets, Distance Between(Event Player, Current Array Element) * Health(
			Current Array Element) - (Team Of(Current Array Element) == Team Of(Event Player) ? (Is In Line of Sight(Eye Position(
			Event Player), Eye Position(Current Array Element), Barriers Do Not Block LOS) ? 1000 : 0) : 0));
		If(!Entity Exists(First Of(Event Player.Target_Order)));
			Press Button(Event Player, Button(Reload));
		End;
		Wait(Max(Server Load / 1000, 1.500), Ignore Condition);
		Loop If Condition Is True;
	}
}

As for the path-finding since it’s A* based there’s no shot at avoiding calculating only part of the path without having the AI run into dead ends or loops.
However I’d at least recommend reducing the lookup set size for when the target location is known.
Like it being a health pack or the objective.
Pre-filter those locations so that even if you need to know the current closest node to the target it isn’t looking through all of them, even ones that are completely unreasonable.



Yeah that targeting would greatly profit off of just setting select things as variables on the Heroes and comparing the variables in the targeting system instead.

Similarly many of the Fade out’s like Doom’s Meteorstrike, Moira’s Fade, and Reaper’s wraith could be unified under the Phased out status.
Never mind, that’s already in it as well so all those checks are mostly redundant. (Disabled)

As a micro improvement, which might have some bigger change here due to the sheer amount of comparisons, you can Check most cases of Duplication & Non - Duplication Hero with a single Array Contains comparison.

Event Player.Targets = Append To Array(Filtered Array(All Living Players(Opposite Team Of(Team Of(Event Player))),
				Is In Line of Sight(Eye Position(Event Player), Eye Position(Current Array Element), Barriers Do Not Block LOS)),
				Filtered Array(All Living Players(Opposite Team Of(Team Of(Event Player))), Event Player.Seen_By_Memory > 0));

Unless I am mistaken, this has some redundancy in adding duplicate entries & needlessly filtering over an entire array

Else If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Zenyatta)))
				&& !Is In Alternate Form(Event Player));

Can Zenyatta even enter an “Alternate form” not that I know of, so could also be trimmed if true-

Any variation of Global.Targeting_Type == (0 || 2) also shouldn’t work as intended as the result of the “Or” will always be true, allowing any non-zero “Targeting_Type” state to trigger this or its variations.

The final wait should also always be 1.5 thanks to the Max which makes the Server Load math redundant.
Same with it in the pathfinding, except there it locks down to 0.5 then.
The highest the server can ideally reach without crashing, would probably be around 0.3 with that calculation.

Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Secondary_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && Is Firing Secondary(Current Array Element)));

The Volume related blocks can be improved by directly Filtering the already made Targets array as this avoid needlessly iterating over targets which already aren’t in it anymore.
Technically not only them, but I am not dealing with rewriting all that as well…


And from the looks of it I think the stealth detection code is outdated due to Sombra’s reworks.


Most of the changes mentioned should be made in this, no guarantees the rule is still working as intended, though hard to tell if it was previously.

rule("Bot Target List")
{
	event
	{
		Ongoing - Each Player;
		All;
		All;
	}

	conditions
	{
		(Is Dummy Bot(Event Player) || Event Player.AI_Control_Player) == True;
		Has Spawned(Event Player) == True;
		Is Alive(Event Player) == True;
	}

	actions
	{
		Skip If(Array Contains(Array(0, 2), Global.Targeting_Type), 4);
		If(Is True For Any(Players On Hero(Hero(Widowmaker), Team Of(
			Event Player)), Is Using Ultimate(Current Array Element)));
			Event Player.Targets = All Living Players(Opposite Team Of(Team Of(Event Player)));
		Else;
			Event Player.Targets = ((Event Player.Seen_By_Memory > 0) ? All Living Players(Opposite Team Of(Team Of(Event Player))) : Filtered Array(All Living Players(Opposite Team Of(Team Of(Event Player))),
				Is In Line of Sight(Eye Position(Event Player), Eye Position(Current Array Element), Barriers Do Not Block LOS)));
			Skip If(Array Contains(Array(0, 1), Global.Targeting_Type), 8);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Has Status(Current Array Element, Hacked)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Opposite Team Of(Team Of(
				Event Player))), Distance Between(Event Player, Current Array Element) <= Global.Primary_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] && ((Is Duplicating(Current Array Element) ? Hero Being Duplicated(
				Current Array Element) : Hero Of(Current Array Element)) == Hero(D.Va) || (Is Duplicating(Current Array Element)
				? Hero Being Duplicated(Current Array Element) : Hero Of(Current Array Element)) == Hero(Illari) ? Is Button Held(
				Current Array Element, Button(Primary Fire)) : Is Firing Primary(Current Array Element))));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Firing Secondary(Current Array Element) ? Distance Between(Event Player, Current Array Element) > Global.Secondary_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Using Ability 1(Current Array Element) ? Distance Between(Event Player, Current Array Element) > Global.Ability1_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Using Ability 2(Current Array Element) ? Distance Between(Event Player, Current Array Element) > Global.Ability2_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Using Ultimate(Current Array Element) ? Distance Between(Event Player, Current Array Element) > Global.Ultimate_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Moving(
				Current Array Element) ? Distance Between(Event Player, Current Array Element) > (Is Crouching(Current Array Element) ? Global.Crouch_Walk_Sound_Max : Global.Walking_Sound_Max)[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			Event Player.Targets = Filtered Array(Event Player.Targets, (Is Meleeing(Current Array Element) ? Distance Between(Event Player, Current Array Element) > Melee_Sound_Max[Index Of Array Value(
				Global.All_Heros, Hero Of(Current Array Element))] : True));
			If(Global.Invisible_Targeting == 0);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(Current Array Element))
					> 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Hero Being Duplicated(Current Array Element) == Hero(Sombra) && Is Using Ability 1(Current Array Element)
					&& !Is Firing Secondary(Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(
					Current Array Element)) > 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
			Else If(Global.Invisible_Targeting == 3);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Hero Being Duplicated(Current Array Element) == Hero(Sombra)
					&& Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array));
			Else If(Global.Invisible_Targeting == 1);
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element)));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))),
					Current Array Element.Invisible_Cooldown == 0 && Current Array Element.Is_Visible_Array == Empty Array && !Is Firing Secondary(
					Current Array Element) && Hero Being Duplicated(Current Array Element) == Hero(Sombra)));
			Else;
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Sombra), Opposite Team Of(
					Team Of(Event Player))), Is Using Ability 1(Current Array Element) && Distance Between(Eye Position(Event Player), Position Of(
					Current Array Element)) > 4 && Distance Between(Position Of(Event Player), Position Of(Current Array Element)) > 4));
				Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Players On Hero(Hero(Echo), Opposite Team Of(Team Of(
					Event Player))), Is Using Ability 1(Current Array Element) && Hero Being Duplicated(Current Array Element) == Hero(Sombra)
					&& Distance Between(Eye Position(Event Player), Position Of(Current Array Element)) > 4 && Distance Between(Position Of(
					Event Player), Position Of(Current Array Element)) > 4));
			End;
		End;
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(All Players(All Teams), Has Status(
			Current Array Element, Asleep) || Is In Spawn Room(Current Array Element) || Has Status(Current Array Element, Phased Out)
			|| !Has Spawned(Current Array Element)));
		Skip If(Global.Ignore_Abilitys == True, 5);
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(D.Va),
			All Teams), Hero Being Duplicated(Event Player) == Hero(D.Va)), Is Firing Secondary(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Zarya),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Zarya)), Is Using Ability 1(Current Array Element)));
		disabled Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Reaper),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Reaper)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Genji),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Genji)), Is Using Ability 2(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Sigma),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Sigma)), Is Using Ability 1(Current Array Element) && Is In View Angle(
			Current Array Element, Eye Position(Event Player), 70)));
		Skip If(Global.Team_Based_Targeting == False, 25);
		If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Zarya))) && Ability Cooldown(Event Player,
			Button(Ability 2)) == Null);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 0.400));
		Else If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Moira))) && Ability Resource(
				Event Player, Button(Primary Fire)) > 0);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Mercy)));
			Modify Player Variable(Event Player, Targets, Append To Array, Append To Array(Filtered Array(All Living Players(Team Of(
				Event Player)), Normalized Health(Current Array Element) < 1), Filtered Array(All Players(Team Of(Event Player)), Is Dead(
				Current Array Element))));
		Else If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Brigitte))) && Ability Charge(
				Event Player, Button(Ability 2)) > 0);
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 1));
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Baptiste)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Ana)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Lúcio))) && !Is In Alternate Form(
				Event Player));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < 1));
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Kiriko)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If((Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Zenyatta)))
				&& !Is In Alternate Form(Event Player));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(Remove From Array(All Living Players(Team Of(
				Event Player)), Event Player), Normalized Health(Current Array Element) < 1));
			Skip If(!Entity Exists(First Of(Filtered Array(Event Player.Targets, Team Of(Current Array Element) == Team Of(Event Player)))),
				1);
			Event Player.Targets = First Of(Filtered Array(Sorted Array(Event Player.Targets, Distance Between(Event Player,
				Current Array Element) * Health(Current Array Element)), Team Of(Current Array Element) == Team Of(Event Player)))
				== Event Player.Zen_Orb_Target ? Filtered Array(Event Player.Targets, Team Of(Current Array Element) != Team Of(Event Player))
				: Event Player.Targets;
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Lifeweaver)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		Else If(Array Contains(Array(Hero Of(Event Player), Hero Being Duplicated(Event Player)), Hero(Illari)));
			Modify Player Variable(Event Player, Targets, Append To Array, Filtered Array(All Living Players(Team Of(Event Player)),
				Normalized Health(Current Array Element) < (Count Of(Event Player.Targets) > 0 ? 0.800 : 1)));
		End;
		Modify Player Variable(Event Player, Targets, Remove From Array By Value, Append To Array(Event Player, Filtered Array(All Players(
			All Teams), !Has Spawned(Current Array Element))));
		disabled Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Doomfist),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Doomfist)), Is Using Ultimate(Current Array Element)));
		disabled Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Moira),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Moira)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Mei),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Mei)), Is Using Ability 1(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Zenyatta),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Zenyatta)), Is Using Ultimate(Current Array Element)));
		disabled Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(Append To Array(Players On Hero(Hero(Tracer),
			All Teams), Hero Being Duplicated(Event Player) == Hero(Tracer)), Is Using Ability 2(Current Array Element)));
		Event Player.Targets = Remove From Array(Event Player.Targets, Filtered Array(All Living Players(Opposite Team Of(Team Of(
			Event Player))), !Is On Objective(Current Array Element) && Is Communicating Any Emote(Current Array Element)));
		Event Player.Target_Order = Sorted Array(Event Player.Targets, Distance Between(Event Player, Current Array Element) * Health(
			Current Array Element) - (Team Of(Current Array Element) == Team Of(Event Player) ? (Is In Line of Sight(Eye Position(
			Event Player), Eye Position(Current Array Element), Barriers Do Not Block LOS) ? 1000 : 0) : 0));
		If(!Entity Exists(First Of(Event Player.Target_Order)));
			Press Button(Event Player, Button(Reload));
		End;
		Wait(Max(Server Load / 105, 1.5), Ignore Condition);
		Loop If Condition Is True;
	}
}

The Pathfinder isn’t A* in fact its not really any type of pathfinder to my knowledge, its just a i watched an A* video before starting, it only ever computes the current node while using filters, sorted & a cooldown array thing, to determine which node to go to at that time.

both the Pathfinder & Targeting system where made back in OW1 in like 2019 or something (which i always find crazy to think about…) that Zenyatta thing filtered him when his in his Ultimate State im pretty sure, as its not filtered by the generic checks. its been so long since i made it i dont actually recall if the alt state is a thing or not, but if it is then its for ultimate. if not then tbh it probably was for ultimate then they broke it.

anyway, you didnt have to go out of your way to optimise my scripts, i was just providing context not looking for help. i dont work on the Workshop anymore and thus dont plan to update my AI anytime soon. i moved to Minecraft Modding & Learning Unity in 2022 due to the limits & bugs in the workshop combined with the fact i finally had an opportunity to work with real code (i got my laptop, first & only computer. in 2022)

Well, that might also be quite heavy then depending on the working set size.
But at least it profits off of not doing the entire path at once, which in a game like this is just too easily worthless as in the AI gets thrown off the calculated path needing recalculation.

¯\_(ツ)_/¯

Equally would look bad for me if I’d just go write off other people ability to script and then don’t provide at least some improvements.
Maybe it’ll help someone.
Either way the Workshop is actually quite a lot more powerful than many imagine but it also takes optimizations to make the script perform well.

I did actually do something similar to your AI script, originally started off as just improvements to the version Ambush had made at the time, by now barely anything resembles his old code.
8JBZK
I was actually looking for path-finding solutions as well but all the available ones just had the same issue of high up front computation cost or path inaccuracy, so I just ended up making my own based on A* though I did divide the graph into smaller sets to not lose too much accuracy while still allowing them to have their path computed by the time they reach the first node in the most common cases.
But I never did limit testing so they might not hold the 10/12 players controlled through script either, no clue.
I just know I gave up on supporting the AI to run on both teams at once so it’d have to be a lopsided test…

my pathfinder is so basic, i dont even support edges on my nodes. when i first made it it was only nodes themselves (Path, Health-Pack, Objective1 & Objective2) with filter & sorter, no cooldowns or unique nodes. in OW1 it ran all 12 AI perfectly fine most of the time. later on i added Staircase (3 arrays: Top, Middle, Bottom), Health-Pack Small, Health-Pack Large, Chokepoint (which never worked out and was immediately scraped) & Jump Nodes (2 arrays: Start, End) which i never see my AI actually use because they prefer to walk around but its supported.

also the very first filter it does on every node, is filtering it down to all nodes with 30 meters then filters by line of sight. so it doesn’t use sets of nodes, but it does greatly reduce the check array as the very first thing. (been doing that since i first made it, figured why bother having them in the calculations if the AI cant even use them)

The Health-Pack ones can be ignored as they are just for the Health-Pack Tracker system that barely works for some reason (used to work, i’ve made Zero changes to it)

(First added node) Chokepoints were meant to fix how the AI always got stuck going back & forth in a corner if the target location was really far away, so it would instead move to the chokepoint first but i had issues figuring out when to use it (i think, its been so long… oh wait no!) the AI would walk to it & for some reason never start walking to the next chokepoint / location… (this is what the cooldown system i added much later, was fixing)

(Second added node) Staircase, these were added so i could stop doing many… many, nodes to go up staircases/vertical areas. since my pathfinder filters nodes based on height difference (upwards, not down since they can fall). so with Staircase Nodes it basically just ignores any filters on the next Node from that staircase.

(Last added node) Jump, this was made to allow the AI to do a common Jump gap like the one to the right of defenders spawn on hanamura. i added it, i made it work by having the AI upon reaching the start [Stop its Start Throttle, Start Throttle Towards Jump End, Wait, Press Jump, Wait until(!On ground), Wait until(On ground), Restart Pathfinder Throttle]. anyway that was tested and worked so i added it to the AI and then every time it just walked around… (i did made it consider the distance between location & node to be the end instead of the start for this is the sorter but that did nothing)

Also my Pathfinder does have a Path & Follow Mode, but i never use it. its in a separate code i made, it functions the same but it calculates every node back-tracking on dead-ends and stuff then once its done has the AI just follow that path with zero regarded for if they can get to that node or if that path is still valid or not. (does not recompute unless a new location is selected), i made that version for fun in i want to say 2021…?

Both Version of my Pathfinder & the tool i use to create the Nodes on maps are available on my Workshop.codes Page in my AI Tools Collection https://workshop.codes/c/sja5r3 as well as a AI Template with no Hero Code but the Pathfinder & Targeting System + my Homemade Aimbot for the AI (wait no… that’s only on Universal AI, the templates just uses a basic facing)

i also provide a lot of Navmeshs for Maps on my Github, most of them unused but i like having them available for people so they dont have to go thru the trouble of creating it themselves since it takes me anywhere from 20 minutes to an hour and 1/2 (those bigger maps are brutal)

https://github.com/Spiderman31807/Overwatch-Workshop/tree/main/AI/Navmeshs
The pre-build Navmeshs include:

  • All 4 Assault Maps + Hanamura (since i made it before it got removed)
  • All 3 Capture the Flag Maps
  • Hanaoka
  • 3 Control Maps (Lijiang, Nepal & Oasis)
  • 14 Deathmatch Maps
  • 4 Elimination Maps (Ayutthaya, Black Forest, Castillo & Ecopoint: Antarctica)
  • 5 Escort Maps (Dorado, Havana, Junkertown, Rialto & Route 66)
  • The Yeti Hunter Version of Nepal Village
  • New Junk City & Suravasa
  • 4 Hybrid Maps (Blizzard World, Eichenwalde, Hollywood & King’s Row)
  • 3 Push Maps (i think theres only 3 but i dont keep up to date these days…)

Oh God look at these brilliant ideas. I can only hope to see one of them come to fruition in my life.