Return to post
settings
{
main
{
Description: "Select TWO heroes instead of one! Switch between your heroes during combat, each hero has a separate health pool, ult charge, and cooldowns! Created by BluJay#11583 v1.2"
Description: "Select TWO heroes instead of one! Switch between your heroes during combat, each hero has a separate health pool, ult charge, and cooldowns! Created by BluJay#11583 v1.2.1"
}
lobby
{
Allow Players Who Are In Queue: Yes
Map Rotation: After A Game
Return To Lobby: Never
}
modes
{
Assault
Control
Escort
Hybrid
General
{
Game Mode Start: Immediately
Hero Limit: Off
}
}
}
variables
{
global:
0: ActiveSwapChargeRate
1: PassiveSwapChargeRate
2: HeroesWithResourceBar
3: HealingInBackground
4: HeroesWithSecondaryAbilityCD
5: Ability1CDs
6: Ability2CDs
7: RightclickCDs
8: HeroesWithAbility1Charges
9: HeroesWithAbility2Charges
10: HeroesWithCrouchAbility
11: HeroesWithJumpAbility
12: ReloadInBackground
13: HealingInBackgroundDelay
14: AllowHeroSwapping
15: ChargeHeroSwapInSpawn
16: CancelSwapAllowed
17: Version
18: VersionHud
19: HeroesArray
20: HeroesWithFourthAbilityCD
21: SwapChargeHealingMultiplier
22: CooldownInBackgroundScale
23: IgnoreAbility2InUseCheck
player:
0: Heroes
1: Ability1Cooldowns
2: Ability2Cooldowns
3: RightclickCooldowns
4: UltCharges
5: IsSelectingSecondHero
6: ReserveHero
7: HeroHealths
8: PlayerFacingDirection
9: SwapCharge
10: ChargeColor
11: CrouchAbilityCooldowns
12: CurrHero
13: SwapHudStringReady
14: IsUsingHalt
15: MaxSecondaryAmmoCounts
16: SecondaryAmmoCounts
17: AmmoCounts
18: ResourcePcts
19: AbilityCharges
20: JumpAbilityCooldowns
21: HeroSwitch
22: MaxHealths
23: IsDvaInMech
24: DvaMechUltCharge
25: MaxAmmoCounts
26: StartHudID
}
subroutines
{
0: SwapHero
1: TrackDvaUlt
2: StartHealingInBackground
3: CooldownsInBackground
}
disabled rule("* Global Init")
{
event
{
Ongoing - Global;
}
}
rule("Constants")
{
event
{
Ongoing - Global;
}
actions
{
Global.Version = Custom String("1.2");
Global.Version = Custom String("1.2.1");
Global.HeroesArray = Array(Hero(Reaper), Hero(Tracer), Hero(Mercy), Hero(Hanzo), Hero(Torbjörn), Hero(Reinhardt), Hero(Pharah),
Hero(Winston), Hero(Widowmaker), Hero(Bastion), Hero(Symmetra), Hero(Zenyatta), Hero(Genji), Hero(Roadhog), Hero(McCree), Hero(
Junkrat), Hero(Zarya), Hero(Soldier: 76), Hero(Lúcio), Hero(D.Va), Hero(Mei), Hero(Sombra), Hero(Doomfist), Hero(Ana), Hero(
Orisa), Hero(Brigitte), Hero(Moira), Hero(Wrecking Ball), Hero(Ashe), Hero(Echo), Hero(Baptiste), Hero(Sigma));
Global.HeroesWithSecondaryAbilityCD = Array(Hero(Doomfist), Hero(Echo), Hero(Lúcio), Hero(Orisa), Hero(Sombra), Hero(Soldier: 76),
Hero(Wrecking Ball), Hero(D.Va), Hero(Sigma));
Global.HeroesWithAbility1Charges = Array(Hero(Tracer), Hero(Symmetra), Hero(Junkrat));
Global.HeroesWithAbility2Charges = Array(Hero(Brigitte));
Global.HeroesWithCrouchAbility = Array(Hero(Wrecking Ball));
Global.HeroesWithJumpAbility = Array(Hero(Hanzo));
Global.Ability1CDs = Array(8, 3, 1.500, 0, 5, 10, 10, 6, 12, 0, 10, 0, 8, 8, 6, 8, 10, 0, 0, 4, 12, 6, 6, 12, 10, 4, 6, 0, 10, 6,
13, 12);
Global.Ability2CDs = Array(10, 12, 30, 10, 10, 6, 9, 13, 15, Null, 10, 0, 8, 8, 10, 10, 8, 15, 12, 8, 12, 6, 6, 10, 10, 6, 10, 15,
12, 8, 25, 10);
Global.RightclickCDs = Array(Null, Null, Null, Null, Null, Null, Null, Null, Null, 1, Null, Null, Null, Null, Null, Null, Null, 6,
4, 1, Null, 2, 4, Null, 8, Null, Null, 5, Null, 6, Null, 1);
Global.RightclickCDs = Array(Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null, Null,
6, 4, 1, Null, 2, 4, Null, 8, Null, Null, 5, Null, 6, Null, 2.500);
Global.HeroesWithFourthAbilityCD = Array(Hero(Wrecking Ball));
Global.HeroesWithResourceBar = Array(Hero(D.Va), Hero(Pharah), Hero(Bastion), Hero(Moira));
Global.IgnoreAbility2InUseCheck = Array(Hero(Junkrat), Hero(Widowmaker));
}
}
rule("Display Version")
{
event
{
Ongoing - Global;
}
actions
{
Create HUD Text(All Players(All Teams), Null, Null, Custom String("Duo Heroes v{0}\r\nby BluJay#11583\r\nCODE: 5KSKH",
Global.Version), Right, 0, Color(White), Color(White), Color(White), Visible To and String, Default Visibility);
Global.VersionHud = Last Text ID;
}
}
rule("Workshop Settings")
{
event
{
Ongoing - Global;
}
actions
{
Global.PassiveSwapChargeRate = 1.316 - 1.300 * Workshop Setting Real(Custom String("Swap Charge"), Custom String(
"Passive Swap Charge Rate"), 0.500, 0, 1, 100);
Global.ActiveSwapChargeRate = Workshop Setting Real(Custom String("Swap Charge"), Custom String("Active Swap Charge Rate"), 0.500,
0, 1, 101);
Global.SwapChargeHealingMultiplier = Workshop Setting Real(Custom String("Swap Charge"), Custom String(
"Healing Charge Multiplier"), 0.500, 0, 1, 102);
Global.CooldownInBackgroundScale = 2 - 1.750 * Workshop Setting Real(Custom String("Reserve Hero"), Custom String(
"Reserve Hero Ability Cooldown Speed Scalar"), 1, 0, 1, 120);
Global.ReloadInBackground = Workshop Setting Toggle(Custom String("Reserve Hero"), Custom String("Reload In Background"), False,
130);
Global.HealingInBackground = Workshop Setting Real(Custom String("Reserve Hero"), Custom String("Healing In Background Rate"), 0,
0, 1, 150) * 30;
Global.HealingInBackgroundDelay = Workshop Setting Real(Custom String("Reserve Hero"), Custom String(
"Healing In Background Delay"), 0.500, 0, 1, 151) * 10;
Global.AllowHeroSwapping = Workshop Setting Toggle(Custom String("Game Settings"), Custom String("Allow Changing Heroes In Spawn"),
True, 200);
Global.ChargeHeroSwapInSpawn = Workshop Setting Toggle(Custom String("Game Settings"), Custom String("Charge Hero Swap In Spawn"),
True, 220);
Global.CancelSwapAllowed = Workshop Setting Toggle(Custom String("Game Settings"), Custom String("Cancel Hero Swap With Crouch"),
True, 300);
}
}
rule("Game Start")
{
event
{
Ongoing - Global;
}
conditions
{
Is Game In Progress == True;
}
actions
{
Destroy HUD Text(Global.VersionHud);
disabled Disable Inspector Recording;
Disable Inspector Recording;
}
}
disabled rule("* Player Init")
{
event
{
Ongoing - Global;
}
}
rule("Freeze Player On Start")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
"For debugging"
Is Dummy Bot(Event Player) == False;
}
actions
{
"Player is forced to stay in spawn until selecting both heroes"
Disable Messages(Event Player);
Set Status(Event Player, Null, Rooted, 9999);
Set Ability 1 Enabled(Event Player, False);
Set Ability 2 Enabled(Event Player, False);
Set Primary Fire Enabled(Event Player, False);
Set Secondary Fire Enabled(Event Player, False);
Create HUD Text(Event Player, Custom String("Hero 1: {0}\r\nHero 2: SELECT", Hero Icon String(Hero Of(Event Player))), Null, Null,
Top, 100, Color(Aqua), Color(White), Color(White), Visible To and String, Default Visibility);
Event Player.StartHudID = Last Text ID;
"Set up variables"
Event Player.Ability1Cooldowns = Array(0, 0);
Event Player.Ability2Cooldowns = Array(0, 0);
Event Player.RightclickCooldowns = Array(0, 0);
Event Player.CrouchAbilityCooldowns = Array(0, 0);
Event Player.JumpAbilityCooldowns = Array(0, 0);
Event Player.ResourcePcts = Array(100, 100);
Event Player.AbilityCharges = Array(Hero Of(Event Player) == Hero(Junkrat) ? 2 : 3, 0);
}
}
rule("Player Select Reserve Hero")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
"Check if player has selected hero 0"
Has Spawned(Event Player) == True;
Is Alive(Event Player) == True;
"Make sure player hasn't already selected hero 1"
Event Player.IsSelectingSecondHero == False;
"For debugging"
Is Dummy Bot(Event Player) == False;
}
actions
{
Wait(2, Ignore Condition);
"Save hero 0 to heroes[0]"
Event Player.Heroes[0] = Hero Of(Event Player);
"Initialize hero 0 data - the rest will be saved on first hero swap"
Event Player.MaxHealths[0] = Max Health(Event Player);
Event Player.MaxAmmoCounts[0] = Max Ammo(Event Player, 0);
Event Player.MaxSecondaryAmmoCounts[0] = Max Ammo(Event Player, 1);
"Force player into hero select and disable current hero"
Set Player Allowed Heroes(Event Player, Remove From Array(All Heroes, Event Player.Heroes[0]));
"This flag stops this rule from ever activating again this game"
Event Player.IsSelectingSecondHero = True;
}
}
rule("Player Done Selecting Heroes")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Event Player.IsSelectingSecondHero == True;
"Check if player has selected hero 1"
Array Contains(All Heroes, Hero Of(Event Player)) == True;
}
actions
{
"Allow hero 0 again"
Reset Player Hero Availability(Event Player);
"Save hero 1 to heroes[1]"
Event Player.Heroes[1] = Hero Of(Event Player);
Event Player.MaxHealths[1] = Max Health(Event Player);
Event Player.MaxAmmoCounts[1] = Max Ammo(Event Player, 0);
Event Player.MaxSecondaryAmmoCounts[1] = Max Ammo(Event Player, 1);
"Save additional data for hero 1 to prepare for first swap"
Event Player.HeroHealths[1] = Max Health(Event Player);
Event Player.AmmoCounts[1] = Max Ammo(Event Player, 0);
Event Player.SecondaryAmmoCounts[1] = Max Ammo(Event Player, 1);
Event Player.AbilityCharges[1] = Hero Of(Event Player) == Hero(Junkrat) ? 2 : 3;
Wait(0.050, Ignore Condition);
"Put player back on hero 0"
Start Forcing Player To Be Hero(Event Player, Event Player.Heroes[0]);
Preload Hero(Event Player, Event Player.Heroes[1]);
"Save pointers"
Event Player.CurrHero = 0;
Event Player.ReserveHero = 1;
"Unfreeze player"
Set Ability 1 Enabled(Event Player, True);
Set Ability 2 Enabled(Event Player, True);
Set Primary Fire Enabled(Event Player, True);
Set Secondary Fire Enabled(Event Player, True);
Clear Status(Event Player, Rooted);
Destroy HUD Text(Event Player.StartHudID);
Enable Messages(Event Player);
}
}
rule("Hero HUD")
{
event
{
Ongoing - Each Player;
All;
All;
}
actions
{
"Show which hero is in reserve, what the player's swap charge is, and what to press to swap"
Create HUD Text(Event Player, Hero Icon String(Event Player.Heroes[Event Player.ReserveHero]), String("{0}%", Round To Integer(
Event Player.SwapCharge, Down)), Event Player.SwapHudStringReady == True ? Custom String("Press {0} to swap",
Input Binding String(Button(Interact))) : Custom String("Not ready"), Left, 0, Color(White), Event Player.ChargeColor, Color(
White), String and Color, Default Visibility);
"Show reserve hero HUD - DO NOT OPEN!"
Create HUD Text(Event Player, Null, Custom String("{0} {1} {2}", Event Player.Heroes[Event Player.ReserveHero] == Hero(
Wrecking Ball) ? Round To Integer(Event Player.CrouchAbilityCooldowns[Event Player.ReserveHero], Up) : (Array Contains(
Global.HeroesWithAbility1Charges, Event Player.Heroes[Event Player.ReserveHero]) ? Round To Integer(
Event Player.AbilityCharges[Event Player.ReserveHero], Down) : Round To Integer(
Event Player.Ability1Cooldowns[Event Player.ReserveHero], Up)), Event Player.Heroes[Event Player.ReserveHero] == Hero(Bastion)
? String("") : (Array Contains(Global.HeroesWithAbility2Charges, Event Player.Heroes[Event Player.ReserveHero])
? Round To Integer(Event Player.AbilityCharges[Event Player.ReserveHero], Down) : Round To Integer(
Event Player.Ability2Cooldowns[Event Player.ReserveHero], Up)), Array Contains(Global.HeroesWithSecondaryAbilityCD,
Event Player.Heroes[Event Player.ReserveHero]) ? Round To Integer(Event Player.RightclickCooldowns[Event Player.ReserveHero],
Up) : (Array Contains(Global.HeroesWithJumpAbility, Event Player.Heroes[Event Player.ReserveHero]) ? Round To Integer(
Event Player.JumpAbilityCooldowns[Event Player.ReserveHero], Up) : String(""))), Custom String("{0} {1} {2}",
Event Player.Heroes[Event Player.ReserveHero] == Hero(Wrecking Ball) ? Ability Icon String(Hero(Wrecking Ball), Button(Crouch))
: Ability Icon String(Event Player.Heroes[Event Player.ReserveHero], Button(Ability 1)), Ability Icon String(
Event Player.Heroes[Event Player.ReserveHero], Button(Ability 2)), Array Contains(Global.HeroesWithJumpAbility,
Event Player.Heroes[Event Player.ReserveHero]) ? Ability Icon String(Hero(Soldier: 76), Button(Ability 1)) : (Array Contains(
Global.HeroesWithSecondaryAbilityCD, Event Player.Heroes[Event Player.ReserveHero]) ? Ability Icon String(
Event Player.Heroes[Event Player.ReserveHero], Button(Secondary Fire)) : String(""))), Left, 150, Color(White), Color(White),
Color(White), String, Default Visibility);
"Show health and ult charge of reserve hero"
Create HUD Text(Event Player, Null, Null, Custom String("{0} {1}", Custom String("{0}: {1}%", Ability Icon String(
Event Player.Heroes[Event Player.ReserveHero], Button(Ultimate)), Event Player.UltCharges[Event Player.ReserveHero]),
Custom String("{0}: {1}", Custom String("HP"), Round To Integer(Event Player.HeroHealths[Event Player.ReserveHero], Down))),
Left, 120, Color(White), Color(White), Color(White), String, Default Visibility);
}
}
disabled rule("* Player Events")
{
event
{
Ongoing - Global;
}
}
rule("Swap Heroes")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Button Held(Event Player, Button(Interact)) == True;
Event Player.SwapCharge >= 100;
"Cancel swap if hero is in an invalid state"
Is Alive(Event Player) == True;
Has Status(Event Player, Hacked) == False;
Has Status(Event Player, Knocked Down) == False;
Has Status(Event Player, Frozen) == False;
Has Status(Event Player, Stunned) == False;
}
actions
{
Wait(0.016, Ignore Condition);
"Allows players to use interact button for normal function without swapping heroes"
Abort If((Is Button Held(Event Player, Button(Crouch)) && Global.CancelSwapAllowed) == True);
Loop If Condition Is True;
Play Effect(Remove From Array(All Players(All Teams), Event Player), Good Explosion, Color(Blue), Event Player, 1);
"Reset swap charge"
Event Player.SwapCharge = 0;
"Save facing direction to stop player viewpoint from jumping"
Event Player.PlayerFacingDirection = Facing Direction Of(Event Player);
"Handles cooldowns and other data needed to be saved and loaded"
Call Subroutine(SwapHero);
"Set facing direction to saved direction"
Set Facing(Event Player, Event Player.PlayerFacingDirection, To World);
"Due to waits, swap charge might already be past 50%"
If(Event Player.SwapCharge < 50);
"Set color of swap charge HUD to red"
Event Player.ChargeColor = Color(Red);
End;
If(Event Player.SwapCharge < 100);
"Used by HUD to tell player swap is not ready"
Event Player.SwapHudStringReady = False;
End;
If(Global.HealingInBackground != 0);
"Start healing reserve hero in background"
Start Rule(StartHealingInBackground, Restart Rule);
End;
If(Global.CooldownInBackgroundScale != 2);
"Start cooldowns in background"
Start Rule(CooldownsInBackground, Restart Rule);
End;
Play Effect(All Players(All Teams), Explosion Sound, Color(White), Evaluate Once(Eye Position(Event Player)), 150);
Wait(0.500, Ignore Condition);
Preload Hero(Event Player, Event Player.Heroes[Event Player.ReserveHero]);
}
}
rule("SUB: SwapHero")
{
event
{
Subroutine;
SwapHero;
}
actions
{
"Section 1: Save ability cooldowns, charges, resource percents, health, ult charge, and ammo"
Event Player.AbilityCharges[Event Player.CurrHero] = Ability Charge(Event Player, Hero Of(Event Player) == Hero(Brigitte) ? Button(
Ability 2) : Button(Ability 1));
Event Player.ResourcePcts[Event Player.CurrHero] = Ability Resource(Event Player, Hero Of(Event Player) == Hero(Moira) ? Button(
Primary Fire) : (Hero Of(Event Player) == Hero(Pharah) ? Button(Jump) : Button(Secondary Fire)));
Event Player.HeroHealths[Event Player.CurrHero] = Health(Event Player);
Event Player.AmmoCounts[Event Player.CurrHero] = Global.ReloadInBackground ? Max Ammo(Event Player, 0) : Ammo(Event Player, 0);
Event Player.SecondaryAmmoCounts[Event Player.CurrHero] = Global.ReloadInBackground ? Max Ammo(Event Player, 1) : Ammo(
Event Player, 1);
Event Player.UltCharges[Event Player.CurrHero] = Ultimate Charge Percent(Event Player);
Event Player.JumpAbilityCooldowns[Event Player.CurrHero] = Ability Cooldown(Event Player, Button(Jump));
Event Player.CrouchAbilityCooldowns[Event Player.CurrHero] = Ability Cooldown(Event Player, Button(Crouch));
If(Is Using Ability 1(Event Player) == True);
Event Player.Ability1Cooldowns[Event Player.CurrHero] = Global.Ability1CDs[Index Of Array Value(Global.HeroesArray, Hero Of(
Event Player))];
Else;
Event Player.Ability1Cooldowns[Event Player.CurrHero] = Ability Cooldown(Event Player, Button(Ability 1));
End;
If(Is Using Ability 2(Event Player) == True && Array Contains(Global.IgnoreAbility2InUseCheck, Hero Of(Event Player)) == False);
Event Player.Ability2Cooldowns[Event Player.CurrHero] = Global.Ability2CDs[Index Of Array Value(Global.HeroesArray, Hero Of(
Event Player))];
Else;
Event Player.Ability2Cooldowns[Event Player.CurrHero] = Ability Cooldown(Event Player, Button(Ability 2));
End;
If((Is Firing Secondary(Event Player) || Event Player.IsUsingHalt) == True);
Event Player.RightclickCooldowns[Event Player.CurrHero] = Global.RightclickCDs[Index Of Array Value(Global.HeroesArray, Hero Of(
Event Player))];
Else;
Event Player.RightclickCooldowns[Event Player.CurrHero] = Ability Cooldown(Event Player, Button(Secondary Fire));
End;
"Section 2: Swap heroes"
Start Forcing Player To Be Hero(Event Player, Event Player.Heroes[Event Player.ReserveHero]);
Event Player.ReserveHero = Event Player.CurrHero;
Event Player.CurrHero = Event Player.CurrHero == 0 ? 1 : 0;
"If saved state of D.Va was out of mech, destroy the mech"
If(Hero Of(Event Player) == Hero(D.Va));
Wait(0.020, Ignore Condition);
If(Event Player.IsDvaInMech == False);
Damage(Event Player, Null, 1000);
End;
"D.Va's data won't load properly without an extra long wait for some reason"
Wait(0.100, Ignore Condition);
End;
"Baptiste needs an extra wait for Immortality Field"
If(Hero Of(Event Player) == Hero(Baptiste));
Wait(0.020, Ignore Condition);
End;
"Section 3: Load data from hero"
Set Ability Cooldown(Event Player, Button(Ability 1), Event Player.Ability1Cooldowns[Event Player.CurrHero]);
Set Ability Cooldown(Event Player, Button(Ability 2), Event Player.Ability2Cooldowns[Event Player.CurrHero]);
Set Ability Cooldown(Event Player, Button(Secondary Fire), Event Player.RightclickCooldowns[Event Player.CurrHero]);
Set Ability Cooldown(Event Player, Button(Jump), Event Player.JumpAbilityCooldowns[Event Player.CurrHero]);
Set Ability Cooldown(Event Player, Button(Crouch), Event Player.CrouchAbilityCooldowns[Event Player.CurrHero]);
Set Player Health(Event Player, Event Player.HeroHealths[Event Player.CurrHero]);
Set Ammo(Event Player, 0, Event Player.AmmoCounts[Event Player.CurrHero]);
Set Ammo(Event Player, 1, Event Player.SecondaryAmmoCounts[Event Player.CurrHero]);
Set Ability Resource(Event Player, Hero Of(Event Player) == Hero(Moira) ? Button(Primary Fire) : Button(Secondary Fire),
Event Player.ResourcePcts[Event Player.CurrHero]);
Set Ability Charge(Event Player, Hero Of(Event Player) == Hero(Brigitte) ? Button(Ability 2) : Button(Ability 1), Round To Integer(
Event Player.AbilityCharges[Event Player.CurrHero], To Nearest));
"Need a delay before setting ult charge - D.Va needs a longer wait based on mech state"
Wait(Hero Of(Event Player) == Hero(D.Va) ? (Event Player.IsDvaInMech == True ? 0.500 : 1) : 0.020, Ignore Condition);
Set Ultimate Charge(Event Player, Event Player.UltCharges[Event Player.CurrHero]);
}
}
rule("Death Routine")
{
event
{
Player Died;
All;
All;
}
actions
{
"Both heroes get reset when a player dies"
Event Player.Ability1Cooldowns[0] = 0;
Event Player.Ability1Cooldowns[1] = 0;
Event Player.Ability2Cooldowns[0] = 0;
Event Player.Ability2Cooldowns[1] = 0;
Event Player.RightclickCooldowns[0] = 0;
Event Player.RightclickCooldowns[1] = 0;
Event Player.HeroHealths[0] = Event Player.MaxHealths[0];
Event Player.HeroHealths[1] = Event Player.MaxHealths[1];
Event Player.AmmoCounts[0] = Event Player.MaxAmmoCounts[0];
Event Player.AmmoCounts[1] = Event Player.MaxAmmoCounts[1];
Event Player.SecondaryAmmoCounts[0] = Event Player.MaxSecondaryAmmoCounts[0];
Event Player.SecondaryAmmoCounts[1] = Event Player.MaxSecondaryAmmoCounts[1];
Event Player.ResourcePcts[0] = 100;
Event Player.ResourcePcts[1] = 100;
Event Player.AbilityCharges[0] = Event Player.Heroes[0] == Hero(Junkrat) ? 2 : 3;
Event Player.AbilityCharges[1] = Event Player.Heroes[1] == Hero(Junkrat) ? 2 : 3;
}
}
rule("Allow Player To Change Heroes")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Global.AllowHeroSwapping == True;
Is In Spawn Room(Event Player) == True;
}
actions
{
Stop Forcing Player To Be Hero(Event Player);
"Stop player from selecting the same hero twice"
Set Player Allowed Heroes(Event Player, Remove From Array(All Heroes, Event Player.Heroes[Event Player.ReserveHero]));
}
}
rule("Change Heroes")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
"Detect if player is on a hero that is not one of their saved heroes in the heroes array"
Array Contains(Event Player.Heroes, Hero Of(Event Player)) == False;
"Stop this rule from activating before the player has selected hero 1"
Array Contains(All Heroes, Event Player.Heroes[1]) == True;
}
actions
{
"Replace current hero with new hero in the heroes array"
Event Player.Heroes[Event Player.CurrHero] = Hero Of(Event Player);
"Initialize new hero data - the rest will be saved on next swap"
Event Player.MaxHealths[Event Player.CurrHero] = Max Health(Event Player);
Event Player.MaxAmmoCounts[Event Player.CurrHero] = Max Ammo(Event Player, 0);
Event Player.MaxSecondaryAmmoCounts[Event Player.CurrHero] = Max Ammo(Event Player, 1);
Set Player Allowed Heroes(Event Player, All Heroes);
}
}
disabled rule("* Swap Charge")
{
event
{
Ongoing - Global;
}
}
rule("Active Swap Charge Damage")
{
event
{
Player Dealt Damage;
All;
All;
}
conditions
{
Event Player.SwapCharge < 100;
}
actions
{
Event Player.SwapCharge += Event Damage * Global.ActiveSwapChargeRate;
"Stop swap charge from surpassing 100%"
If(Event Player.SwapCharge > 100);
Event Player.SwapCharge = 100;
End;
"Set color of player HUD percent"
If(Event Player.SwapCharge < 50);
Event Player.ChargeColor = Color(Red);
Else If(Event Player.SwapCharge < 100);
Event Player.ChargeColor = Color(Yellow);
Else;
Event Player.ChargeColor = Color(Green);
"Tells player HUD to stop saying hero swap is not ready and instead show what button to press"
Event Player.SwapHudStringReady = True;
End;
}
}
rule("Active Swap Charge Heal")
{
event
{
Player Dealt Healing;
All;
All;
}
conditions
{
Event Player.SwapCharge < 100;
}
actions
{
Event Player.SwapCharge += Event Healing * Global.SwapChargeHealingMultiplier * Global.ActiveSwapChargeRate;
"Stop swap charge from surpassing 100%"
If(Event Player.SwapCharge > 100);
Event Player.SwapCharge = 100;
End;
"Set color of player HUD percent"
If(Event Player.SwapCharge < 50);
Event Player.ChargeColor = Color(Red);
Else If(Event Player.SwapCharge < 100);
Event Player.ChargeColor = Color(Yellow);
Else;
Event Player.ChargeColor = Color(Green);
"Tells player HUD to stop saying hero swap is not ready and instead show what button to press"
Event Player.SwapHudStringReady = True;
End;
}
}
rule("Passive Swap Charge")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
"Stops this rule from running all game if workshop settings has passive swap charge rate at 0"
Global.PassiveSwapChargeRate != 1.316;
Event Player.SwapCharge < 100;
}
actions
{
Event Player.SwapCharge += 1;
"Stop swap charge from surpassing 100%"
If(Event Player.SwapCharge > 100);
Event Player.SwapCharge = 100;
End;
"Set color of player HUD percent"
If(Event Player.SwapCharge < 50);
Event Player.ChargeColor = Color(Red);
Else If(Event Player.SwapCharge < 100);
Event Player.ChargeColor = Color(Yellow);
Else;
Event Player.ChargeColor = Color(Green);
"Tells player HUD to stop saying hero swap is not ready and instead show what button to press"
Event Player.SwapHudStringReady = True;
End;
Wait(Global.PassiveSwapChargeRate, Ignore Condition);
Loop If Condition Is True;
}
}
disabled rule("* Process Reserve Hero")
{
event
{
Ongoing - Global;
}
}
rule("SUB: CooldownsInBackground")
{
event
{
Subroutine;
CooldownsInBackground;
}
actions
{
Wait(Global.CooldownInBackgroundScale, Ignore Condition);
If(Event Player.Ability1Cooldowns[Event Player.ReserveHero] > 0);
Event Player.Ability1Cooldowns[Event Player.ReserveHero] -= 0.250;
End;
If(Event Player.Ability2Cooldowns[Event Player.ReserveHero] > 0);
Event Player.Ability2Cooldowns[Event Player.ReserveHero] -= 0.250;
End;
If(Event Player.RightclickCooldowns[Event Player.ReserveHero] > 0);
Event Player.RightclickCooldowns[Event Player.ReserveHero] -= 0.250;
End;
If(Event Player.CrouchAbilityCooldowns[Event Player.ReserveHero] > 0);
Event Player.CrouchAbilityCooldowns[Event Player.ReserveHero] -= 0.250;
End;
If(Event Player.JumpAbilityCooldowns[Event Player.ReserveHero] > 0);
Event Player.JumpAbilityCooldowns[Event Player.ReserveHero] -= 0.250;
End;
If(Event Player.AbilityCharges[Event Player.ReserveHero] < (Event Player.Heroes[Event Player.ReserveHero] == Hero(Junkrat)
? 2 : 3));
Event Player.AbilityCharges[Event Player.ReserveHero] += 0.250 / (Event Player.Heroes[Event Player.ReserveHero] == Hero(Brigitte)
? Global.Ability2CDs : Global.Ability1CDs)[Index Of Array Value(Global.HeroesArray,
Event Player.Heroes[Event Player.ReserveHero])];
End;
If(Event Player.Heroes[Event Player.ReserveHero] == Hero(Moira));
Event Player.ResourcePcts[Event Player.ReserveHero] += 0.600;
Else If(Event Player.Heroes[Event Player.ReserveHero] == Hero(D.Va));
Event Player.ResourcePcts[Event Player.ReserveHero] += 4;
Else If(Event Player.Heroes[Event Player.ReserveHero] == Hero(Pharah));
Event Player.ResourcePcts[Event Player.ReserveHero] += 8.750;
Else If(Event Player.Heroes[Event Player.ReserveHero] == Hero(Bastion));
Event Player.ResourcePcts[Event Player.ReserveHero] += 3.570;
End;
"Stop executing this rule if all cooldowns ready"
If(
Event Player.Ability1Cooldowns[Event Player.ReserveHero] <= 0 && Event Player.Ability2Cooldowns[Event Player.ReserveHero] <= 0 && Event Player.RightclickCooldowns[Event Player.ReserveHero] <= 0 && Event Player.CrouchAbilityCooldowns[Event Player.ReserveHero] <= 0 && Event Player.JumpAbilityCooldowns[Event Player.ReserveHero] <= 0);
"No special cases"
If(Array Contains(Global.HeroesWithResourceBar, Event Player.Heroes[Event Player.ReserveHero]) == False && Array Contains(
Global.HeroesWithAbility1Charges, Event Player.Heroes[Event Player.ReserveHero]) == False && Array Contains(
Global.HeroesWithAbility2Charges, Event Player.Heroes[Event Player.ReserveHero]) == False);
Abort;
"Reserve hero has resource bar"
Else If(Array Contains(Global.HeroesWithResourceBar, Event Player.Heroes[Event Player.ReserveHero])
== True && Event Player.ResourcePcts[Event Player.ReserveHero] >= 100);
Abort;
"Reserve hero has ability charges"
Else If(Array Contains(Append To Array(Global.HeroesWithAbility1Charges, Global.HeroesWithAbility2Charges),
Event Player.Heroes[Event Player.ReserveHero]) == True && Event Player.AbilityCharges[Event Player.ReserveHero] >= (
Event Player.Heroes[Event Player.ReserveHero] == Hero(Junkrat) ? 2 : 3));
Abort;
End;
End;
Loop;
}
}
rule("SUB: StartHealingInBackground")
{
event
{
Subroutine;
StartHealingInBackground;
}
actions
{
"Save which hero's health is getting restored"
Event Player.HeroSwitch = Event Player.ReserveHero;
"Provide a delay before background healing starts"
Wait(Global.HealingInBackgroundDelay, Ignore Condition);
While(Event Player.HeroHealths[Event Player.ReserveHero] < Event Player.MaxHealths[Event Player.ReserveHero]);
If(Event Player.HeroHealths[Event Player.ReserveHero] < Event Player.MaxHealths[Event Player.ReserveHero]);
"If next heal would put reserve hero health above max health, set to max health instead"
If(
Event Player.HeroHealths[Event Player.ReserveHero] + Global.HealingInBackground > Event Player.MaxHealths[Event Player.ReserveHero]);
Event Player.HeroHealths[Event Player.ReserveHero] = Event Player.MaxHealths[Event Player.ReserveHero];
"Else heal as normal"
Else;
Event Player.HeroHealths[Event Player.ReserveHero] += Global.HealingInBackground;
End;
End;
Wait(0.500, Ignore Condition);
"Check for a hero swap and abort if so"
If(Event Player.CurrHero == Event Player.HeroSwitch);
Abort;
End;
End;
}
}
rule("Charge Swap In Spawn")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Global.ChargeHeroSwapInSpawn == True;
Is In Spawn Room(Event Player) == True;
}
actions
{
"Keep player's swap charge at 100% if in spawn room"
Event Player.SwapCharge = 100;
Event Player.ChargeColor = Color(Green);
Event Player.SwapHudStringReady = True;
disabled Wait(0.250, Ignore Condition);
Wait Until(Event Player.SwapCharge != 100 || Is In Spawn Room(Event Player) == False, 999);
Loop If Condition Is True;
}
}
disabled rule("* Game Fixes")
{
event
{
Ongoing - Global;
}
}
rule("Is Orisa Using Halt!")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(Orisa);
Is Firing Secondary(Event Player) == True;
}
actions
{
"Fixes exploit where player swaps off of Orisa right as they pull players with Halt! to stop the cooldown from starting - Used in section 1 of SUB: SwapHero"
Event Player.IsUsingHalt = True;
While(Ability Cooldown(Event Player, Button(Secondary Fire)) == 0 && Hero Of(Event Player) == Hero(Orisa));
Wait(0.016, Ignore Condition);
End;
Event Player.IsUsingHalt = False;
}
}
rule("Track D.Va Mech State (Out Of Mech)")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(D.Va);
Is In Alternate Form(Event Player) == True;
}
actions
{
"Need to save D.Va's mech state, otherwise after losing mech a player could swap off of and back on to D.Va to recover the mech"
Event Player.IsDvaInMech = False;
}
}
rule("Track D.Va Mech State (In Mech)")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(D.Va);
Is In Alternate Form(Event Player) == False;
}
actions
{
"Need to save D.Va's mech state, otherwise after losing mech a player could swap off of and back on to D.Va to recover the mech"
Wait(0.250, Ignore Condition);
Event Player.IsDvaInMech = True;
}
}
rule("Track Self Destruct Ult Charge")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(D.Va);
Is In Alternate Form(Event Player) == False;
Is Alive(Event Player) == True;
}
actions
{
"Tracks D.Va's ult charge while she's in her mech"
Wait(1.250, Ignore Condition);
Abort If Condition Is False;
Call Subroutine(TrackDvaUlt);
}
}
rule("SUB: TrackDvaUlt")
{
event
{
Subroutine;
TrackDvaUlt;
}
actions
{
"Updates D.Va's ult charge 4 times a second - this is needed because D.Va has two separate ults"
Event Player.DvaMechUltCharge = Ultimate Charge Percent(Event Player);
Wait(0.250, Ignore Condition);
Loop If(Hero Of(Event Player) == Hero(D.Va) && Is In Alternate Form(Event Player) == False);
}
}
rule("Set Self Destruct Ult Charge After Call Mech")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(D.Va);
Is Using Ultimate(Event Player) == True;
}
actions
{
"If D.Va uses her call mech ult, load her self destruct ult charge that has been saved"
Wait(2, Ignore Condition);
Abort If(Is In Alternate Form(Event Player) == True);
Set Ultimate Charge(Event Player, Event Player.DvaMechUltCharge);
Wait(0.250, Ignore Condition);
Call Subroutine(TrackDvaUlt);
}
}
rule("Set Call Mech Charge To 0%")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Hero Of(Event Player) == Hero(D.Va);
Is In Alternate Form(Event Player) == True;
}
actions
{
"When D.Va loses her mech, set her ult charge to 0"
Set Ultimate Charge(Event Player, 0);
}
}
disabled rule("* Debug")
{
event
{
Ongoing - Global;
}
}
rule("DEBUG: Skip Hero Selection")
disabled rule("DEBUG: Skip Hero Selection")
{
event
{
Ongoing - Global;
}
conditions
{
Is Assembling Heroes == True;
}
actions
{
Set Match Time(12);
}
}
rule("DEBUG: Skip Setup")
disabled rule("DEBUG: Skip Setup")
{
event
{
Ongoing - Global;
}
conditions
{
Is In Setup == True;
}
actions
{
Set Match Time(7);
}
}
rule("DEBUG: Damage Self")
disabled rule("DEBUG: Damage Self")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Button Held(Event Player, Button(Jump)) == True;
}
actions
{
Damage(Event Player, Null, 30);
}
}
rule("DEBUG: Spawn Bot")
disabled rule("DEBUG: Spawn Bot")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Button Held(Event Player, Button(Reload)) == True;
Is Button Held(Event Player, Button(Crouch)) == True;
}
actions
{
disabled Create Dummy Bot(Random Value In Array(All Heroes), Opposite Team Of(Team Of(Event Player)), -1, Position Of(Event Player)
+ Facing Direction Of(Event Player), Vector(0, 0, 0));
Create Dummy Bot(Random Value In Array(All Heroes), Team Of(Event Player), -1, Position Of(Event Player) + Facing Direction Of(
disabled Create Dummy Bot(Random Value In Array(All Heroes), Team Of(Event Player), -1, Position Of(Event Player) + Facing Direction Of(
Event Player), Vector(0, 0, 0));
disabled Create Dummy Bot(Hero(Mei), Opposite Team Of(Team Of(Event Player)), -1, Position Of(Event Player) + Facing Direction Of(
Create Dummy Bot(Hero(Ana), Opposite Team Of(Team Of(Event Player)), -1, Position Of(Event Player) + Facing Direction Of(
Event Player), Vector(0, 0, 0));
}
}
rule("DEBUG: Keep Bots Alive")
disabled rule("DEBUG: Keep Bots Alive")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Dummy Bot(Event Player) == True;
}
actions
{
Start Heal Over Time(Event Player, Event Player, 9999, 9999);
}
}
rule("DEBUG: Pause Match Timer")
disabled rule("DEBUG: Pause Match Timer")
{
event
{
Ongoing - Global;
}
conditions
{
Is Game In Progress == True;
}
actions
{
Pause Match Time;
}
}
rule("DEBUG: Use Ability")
disabled rule("DEBUG: Use Ability")
{
event
{
Ongoing - Each Player;
All;
All;
}
conditions
{
Is Dummy Bot(Event Player) == True;
}
actions
{
disabled Wait(0.250, Ignore Condition);
disabled Set Ultimate Charge(Event Player, 100);
Wait(3.500, Ignore Condition);
disabled Start Holding Button(Event Player, Button(Primary Fire));
Event Player.Heroes = Array(Hero Of(Event Player), Hero(Mei));
Event Player.SwapCharge = 100;
Start Holding Button(Event Player, Button(Ability 1));
disabled Event Player.Heroes = Array(Hero Of(Event Player), Hero(Mei));
disabled Event Player.SwapCharge = 100;
disabled Kill(Event Player, Null);
Wait(1.250, Ignore Condition);
Start Holding Button(Event Player, Button(Interact));
Wait(0.250, Ignore Condition);
Stop Holding Button(Event Player, Button(Interact));
disabled Wait(1.250, Ignore Condition);
disabled Start Holding Button(Event Player, Button(Interact));
disabled Wait(0.250, Ignore Condition);
disabled Stop Holding Button(Event Player, Button(Interact));
}
}
rule("DEBUG: HUD")
disabled rule("DEBUG: HUD")
{
event
{
Ongoing - Global;
}
actions
{
Create HUD Text(All Players(All Teams), Custom String("{0} Load\r\n{1} Avg\r\n{2} Peak", Server Load, Server Load Average,
Server Load Peak), Null, Null, Right, 200, Color(White), Color(White), Color(White), Visible To and String,
Default Visibility);
}
}