diff --git a/OpenRA.Game/GameInformation.cs b/OpenRA.Game/GameInformation.cs
index 5775ab17c1..ef10fcbcd6 100644
--- a/OpenRA.Game/GameInformation.cs
+++ b/OpenRA.Game/GameInformation.cs
@@ -123,6 +123,7 @@ namespace OpenRA
DisplayFactionId = runtimePlayer.DisplayFaction.InternalName,
Color = runtimePlayer.Color,
Team = client.Team,
+ Handicap = client.Handicap,
SpawnPoint = runtimePlayer.SpawnPoint,
IsRandomFaction = runtimePlayer.Faction.InternalName != client.Faction,
IsRandomSpawnPoint = runtimePlayer.DisplaySpawnPoint == 0,
@@ -166,6 +167,7 @@ namespace OpenRA
/// The team ID on start-up, or 0 if the player is not part of a team.
public int Team;
public int SpawnPoint;
+ public int Handicap;
/// True if the faction was chosen at random; otherwise, false.
public bool IsRandomFaction;
diff --git a/OpenRA.Game/Map/PlayerReference.cs b/OpenRA.Game/Map/PlayerReference.cs
index 27561656e5..ca071e1f2e 100644
--- a/OpenRA.Game/Map/PlayerReference.cs
+++ b/OpenRA.Game/Map/PlayerReference.cs
@@ -50,6 +50,9 @@ namespace OpenRA
public bool LockTeam = false;
public int Team = 0;
+ public bool LockHandicap = false;
+ public int Handicap = 0;
+
public string[] Allies = { };
public string[] Enemies = { };
diff --git a/OpenRA.Game/Network/GameSave.cs b/OpenRA.Game/Network/GameSave.cs
index d823db8873..813f5be71d 100644
--- a/OpenRA.Game/Network/GameSave.cs
+++ b/OpenRA.Game/Network/GameSave.cs
@@ -25,6 +25,7 @@ namespace OpenRA.Network
public readonly string Faction;
public readonly int SpawnPoint;
public readonly int Team;
+ public readonly int Handicap;
public readonly string Slot;
public readonly string Bot;
public readonly bool IsAdmin;
@@ -39,6 +40,7 @@ namespace OpenRA.Network
Faction = client.Faction;
SpawnPoint = client.SpawnPoint;
Team = client.Team;
+ Handicap = client.Handicap;
Slot = client.Slot;
Bot = client.Bot;
IsAdmin = client.IsAdmin;
@@ -53,6 +55,7 @@ namespace OpenRA.Network
client.Faction = Faction;
client.SpawnPoint = SpawnPoint;
client.Team = Team;
+ client.Handicap = Handicap;
client.Slot = Slot;
client.Bot = Bot;
client.IsAdmin = IsAdmin;
diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs
index c39b5b808c..090b218c30 100644
--- a/OpenRA.Game/Network/Session.cs
+++ b/OpenRA.Game/Network/Session.cs
@@ -144,6 +144,7 @@ namespace OpenRA.Network
public ClientState State = ClientState.Invalid;
public int Team;
+ public int Handicap;
public string Slot; // Slot ID, or null for observer
public string Bot; // Bot type, null for real clients
public int BotControllerClientIndex; // who added the bot to the slot
@@ -193,6 +194,7 @@ namespace OpenRA.Network
public bool LockFaction;
public bool LockColor;
public bool LockTeam;
+ public bool LockHandicap;
public bool LockSpawn;
public bool Required;
diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs
index 5cd47d7ff7..53d6ccfb22 100644
--- a/OpenRA.Game/Player.cs
+++ b/OpenRA.Game/Player.cs
@@ -58,6 +58,7 @@ namespace OpenRA
public readonly bool Playable = true;
public readonly int ClientIndex;
public readonly CPos HomeLocation;
+ public readonly int Handicap;
public readonly PlayerReference PlayerReference;
public readonly bool IsBot;
public readonly string BotType;
@@ -180,6 +181,8 @@ namespace OpenRA
HomeLocation = assignSpawnPoints?.AssignHomeLocation(world, client, playerRandom) ?? pr.HomeLocation;
SpawnPoint = assignSpawnPoints?.SpawnPointForPlayer(this) ?? client.SpawnPoint;
DisplaySpawnPoint = client.SpawnPoint;
+
+ Handicap = client.Handicap;
}
else
{
@@ -195,6 +198,7 @@ namespace OpenRA
DisplayFaction = ResolveDisplayFaction(world, pr.Faction);
HomeLocation = pr.HomeLocation;
SpawnPoint = DisplaySpawnPoint = 0;
+ Handicap = pr.Handicap;
}
if (!spectating)
diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs
index f234d954c9..fd975777a4 100644
--- a/OpenRA.Game/Server/Server.cs
+++ b/OpenRA.Game/Server/Server.cs
@@ -93,6 +93,8 @@ namespace OpenRA.Server
c.SpawnPoint = pr.Spawn;
if (pr.LockTeam)
c.Team = pr.Team;
+ if (pr.LockHandicap)
+ c.Team = pr.Handicap;
c.Color = pr.LockColor ? pr.Color : c.PreferredColor;
}
@@ -437,6 +439,7 @@ namespace OpenRA.Server
Faction = "Random",
SpawnPoint = 0,
Team = 0,
+ Handicap = 0,
State = Session.ClientState.Invalid,
};
diff --git a/OpenRA.Mods.Common/Scripting/Properties/PlayerProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/PlayerProperties.cs
index 9ccf0fff36..58f516cecc 100644
--- a/OpenRA.Mods.Common/Scripting/Properties/PlayerProperties.cs
+++ b/OpenRA.Mods.Common/Scripting/Properties/PlayerProperties.cs
@@ -52,6 +52,16 @@ namespace OpenRA.Mods.Common.Scripting
}
}
+ [Desc("The player's handicap level.")]
+ public int Handicap
+ {
+ get
+ {
+ var c = Player.World.LobbyInfo.Clients.FirstOrDefault(i => i.Index == Player.ClientIndex);
+ return c?.Handicap ?? 0;
+ }
+ }
+
[Desc("Returns true if the player is a bot.")]
public bool IsBot { get { return Player.IsBot; } }
diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
index 7abbe924c3..48b6d8326f 100644
--- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
+++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
@@ -43,6 +43,7 @@ namespace OpenRA.Mods.Common.Server
{ "name", Name },
{ "faction", Faction },
{ "team", Team },
+ { "handicap", Handicap },
{ "spawn", Spawn },
{ "clear_spawn", ClearPlayerSpawn },
{ "color", PlayerColor },
@@ -245,6 +246,7 @@ namespace OpenRA.Mods.Common.Server
client.Slot = null;
client.SpawnPoint = 0;
client.Team = 0;
+ client.Handicap = 0;
client.Color = Color.White;
server.SyncLobbyClients();
CheckAutoStart(server);
@@ -377,6 +379,7 @@ namespace OpenRA.Mods.Common.Server
Faction = "Random",
SpawnPoint = 0,
Team = 0,
+ Handicap = 0,
State = Session.ClientState.NotReady,
BotControllerClientIndex = controllerClientIndex
};
@@ -736,6 +739,7 @@ namespace OpenRA.Mods.Common.Server
targetClient.Slot = null;
targetClient.SpawnPoint = 0;
targetClient.Team = 0;
+ targetClient.Handicap = 0;
targetClient.Color = Color.White;
targetClient.State = Session.ClientState.NotReady;
server.SendMessage("{0} moved {1} to spectators.".F(client.Name, targetClient.Name));
@@ -824,6 +828,42 @@ namespace OpenRA.Mods.Common.Server
}
}
+ static bool Handicap(S server, Connection conn, Session.Client client, string s)
+ {
+ lock (server.LobbyInfo)
+ {
+ var parts = s.Split(' ');
+ var targetClient = server.LobbyInfo.ClientWithIndex(Exts.ParseIntegerInvariant(parts[0]));
+
+ // Only the host can change other client's info
+ if (targetClient.Index != client.Index && !client.IsAdmin)
+ return true;
+
+ // Map has disabled handicap changes
+ if (server.LobbyInfo.Slots[targetClient.Slot].LockHandicap)
+ return true;
+
+ if (!Exts.TryParseIntegerInvariant(parts[1], out var handicap))
+ {
+ Log.Write("server", "Invalid handicap: {0}", s);
+ return false;
+ }
+
+ // Handicaps may be set between 0 - 95% in steps of 5%
+ var options = Enumerable.Range(0, 20).Select(i => 5 * i);
+ if (!options.Contains(handicap))
+ {
+ Log.Write("server", "Invalid handicap: {0}", s);
+ return false;
+ }
+
+ targetClient.Handicap = handicap;
+ server.SyncLobbyClients();
+
+ return true;
+ }
+ }
+
static bool ClearPlayerSpawn(S server, Connection conn, Session.Client client, string s)
{
var spawnPoint = Exts.ParseIntegerInvariant(s);
@@ -1003,6 +1043,7 @@ namespace OpenRA.Mods.Common.Server
LockFaction = pr.LockFaction,
LockColor = pr.LockColor,
LockTeam = pr.LockTeam,
+ LockHandicap = pr.LockHandicap,
LockSpawn = pr.LockSpawn,
Required = pr.Required,
};
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/HandicapDamageMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/HandicapDamageMultiplier.cs
new file mode 100644
index 0000000000..cc9398d0aa
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/HandicapDamageMultiplier.cs
@@ -0,0 +1,40 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("Modifies the damage applied to this actor based on the owner's handicap.")]
+ public class HandicapDamageMultiplierInfo : TraitInfo
+ {
+ public override object Create(ActorInitializer init) { return new HandicapDamageMultiplier(init.Self); }
+ }
+
+ public class HandicapDamageMultiplier : IDamageModifier
+ {
+ readonly Actor self;
+
+ public HandicapDamageMultiplier(Actor self)
+ {
+ this.self = self;
+ }
+
+ int IDamageModifier.GetDamageModifier(Actor attacker, Damage damage)
+ {
+ // Equivalent to the health handicap from C&C3:
+ // 5% handicap = 95% health = 105% damage
+ // 50% handicap = 50% health = 200% damage
+ // 95% handicap = 5% health = 2000% damage
+ return 10000 / (100 - self.Owner.Handicap);
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/HandicapFirepowerMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/HandicapFirepowerMultiplier.cs
new file mode 100644
index 0000000000..3f7b4cb923
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/HandicapFirepowerMultiplier.cs
@@ -0,0 +1,40 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("Modifies the damage applied by this actor based on the owner's handicap.")]
+ public class HandicapFirepowerMultiplierInfo : TraitInfo
+ {
+ public override object Create(ActorInitializer init) { return new HandicapFirepowerMultiplier(init.Self); }
+ }
+
+ public class HandicapFirepowerMultiplier : IFirepowerModifier
+ {
+ readonly Actor self;
+
+ public HandicapFirepowerMultiplier(Actor self)
+ {
+ this.self = self;
+ }
+
+ int IFirepowerModifier.GetFirepowerModifier()
+ {
+ // Equivalent to the firepower handicap from C&C3:
+ // 5% handicap = 95% firepower
+ // 50% handicap = 50% firepower
+ // 95% handicap = 5% firepower
+ return 100 - self.Owner.Handicap;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/Traits/Multipliers/HandicapProductionTimeMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/HandicapProductionTimeMultiplier.cs
new file mode 100644
index 0000000000..36d2a8572f
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Multipliers/HandicapProductionTimeMultiplier.cs
@@ -0,0 +1,32 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version. For more
+ * information, see COPYING.
+ */
+#endregion
+
+using System.Collections.Generic;
+using System.Linq;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("Modifies the production time of this actor based on the producer's handicap.")]
+ public class HandicapProductionTimeMultiplierInfo : TraitInfo, IProductionTimeModifierInfo
+ {
+ int IProductionTimeModifierInfo.GetProductionTimeModifier(TechTree techTree, string queue)
+ {
+ // Equivalent to the build speed handicap from C&C3:
+ // 5% handicap = 105% build time
+ // 50% handicap = 150% build time
+ // 95% handicap = 195% build time
+ return 100 + techTree.Owner.Handicap;
+ }
+ }
+
+ public class HandicapProductionTimeMultiplier { }
+}
diff --git a/OpenRA.Mods.Common/Traits/Player/TechTree.cs b/OpenRA.Mods.Common/Traits/Player/TechTree.cs
index e8c04fcdf8..02934ef144 100644
--- a/OpenRA.Mods.Common/Traits/Player/TechTree.cs
+++ b/OpenRA.Mods.Common/Traits/Player/TechTree.cs
@@ -106,6 +106,8 @@ namespace OpenRA.Mods.Common.Traits
return ret;
}
+ public Player Owner { get { return player; } }
+
class Watcher
{
public readonly string Key;
diff --git a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs
index f74fd832a1..c6d4e1304d 100644
--- a/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs
+++ b/OpenRA.Mods.Common/Traits/World/CreateMapPlayers.cs
@@ -64,6 +64,7 @@ namespace OpenRA.Mods.Common.Traits
DisplayFactionId = clientFaction.InternalName,
Color = client.Color,
Team = client.Team,
+ Handicap = client.Handicap,
SpawnPoint = resolvedSpawnPoint,
IsRandomFaction = clientFaction.RandomFactionMembers.Any(),
IsRandomSpawnPoint = client.SpawnPoint == 0,
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
index 46d05d2373..1302c04596 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
@@ -624,6 +624,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview);
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
+ LobbyUtils.SetupEditableHandicapWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, map);
}
@@ -640,6 +641,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (isHost)
{
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
+ LobbyUtils.SetupEditableHandicapWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
LobbyUtils.SetupPlayerActionWidget(template, slot, client, orderManager, worldRenderer,
lobby, () => panel = PanelType.Kick, () => panel = PanelType.Players);
@@ -648,6 +650,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
LobbyUtils.SetupNameWidget(template, slot, client, orderManager, worldRenderer);
LobbyUtils.SetupTeamWidget(template, slot, client);
+ LobbyUtils.SetupHandicapWidget(template, slot, client);
LobbyUtils.SetupSpawnWidget(template, slot, client);
}
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
index 685166426e..b87d742de7 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
@@ -146,6 +146,25 @@ namespace OpenRA.Mods.Common.Widgets.Logic
dropdown.ShowDropDown("TEAM_DROPDOWN_TEMPLATE", 150, options, setupItem);
}
+ public static void ShowHandicapDropDown(DropDownButtonWidget dropdown, Session.Client client,
+ OrderManager orderManager)
+ {
+ Func setupItem = (ii, itemTemplate) =>
+ {
+ var item = ScrollItemWidget.Setup(itemTemplate,
+ () => client.Handicap == ii,
+ () => orderManager.IssueOrder(Order.Command("handicap {0} {1}".F(client.Index, ii))));
+
+ var label = "{0}%".F(ii);
+ item.Get("LABEL").GetText = () => label;
+ return item;
+ };
+
+ // Handicaps may be set between 0 - 95% in steps of 5%
+ var options = Enumerable.Range(0, 20).Select(i => 5 * i);
+ dropdown.ShowDropDown("TEAM_DROPDOWN_TEMPLATE", 150, options, setupItem);
+ }
+
public static void ShowSpawnDropDown(DropDownButtonWidget dropdown, Session.Client client,
OrderManager orderManager, IEnumerable spawnPoints)
{
@@ -562,6 +581,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic
HideChildWidget(parent, "TEAM_DROPDOWN");
}
+ public static void SetupEditableHandicapWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
+ {
+ var dropdown = parent.Get("HANDICAP_DROPDOWN");
+ dropdown.IsVisible = () => true;
+ dropdown.IsDisabled = () => s.LockTeam || orderManager.LocalClient.IsReady;
+ dropdown.OnMouseDown = _ => ShowHandicapDropDown(dropdown, c, orderManager);
+
+ var handicapLabel = new CachedTransform(h => "{0}%".F(h));
+ dropdown.GetText = () => handicapLabel.Update(c.Handicap);
+
+ HideChildWidget(parent, "HANDICAP");
+ }
+
+ public static void SetupHandicapWidget(Widget parent, Session.Slot s, Session.Client c)
+ {
+ var team = parent.Get("HANDICAP");
+ team.IsVisible = () => true;
+
+ var handicapLabel = new CachedTransform(h => "{0}%".F(h));
+ team.GetText = () => handicapLabel.Update(c.Handicap);
+ HideChildWidget(parent, "HANDICAP_DROPDOWN");
+ }
+
public static void SetupEditableSpawnWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
{
var dropdown = parent.Get("SPAWN_DROPDOWN");
diff --git a/mods/cnc/chrome/lobby-players.yaml b/mods/cnc/chrome/lobby-players.yaml
index 237cf3ab2c..6a6644403f 100644
--- a/mods/cnc/chrome/lobby-players.yaml
+++ b/mods/cnc/chrome/lobby-players.yaml
@@ -35,6 +35,13 @@ Container@LOBBY_PLAYER_BIN:
Text: Team
Align: Center
Font: Bold
+ Label@HANDICAP:
+ X: 491
+ Width: 75
+ Height: 25
+ Text: Handicap
+ Align: Center
+ Font: Bold
Label@SPAWN:
X: 571
Width: 50
@@ -142,6 +149,13 @@ Container@LOBBY_PLAYER_BIN:
Width: 50
Height: 25
Font: Regular
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 491
+ Width: 75
+ Height: 25
+ Font: Regular
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
DropDownButton@SPAWN_DROPDOWN:
X: 571
Width: 50
@@ -252,6 +266,18 @@ Container@LOBBY_PLAYER_BIN:
Height: 25
Font: Regular
Visible: false
+ Label@HANDICAP:
+ X: 491
+ Width: 50
+ Height: 25
+ Align: Center
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 491
+ Width: 75
+ Height: 25
+ Font: Regular
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
Label@SPAWN:
X: 571
Width: 25
diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml
index 183221e510..1abf0faf7e 100644
--- a/mods/cnc/rules/defaults.yaml
+++ b/mods/cnc/rules/defaults.yaml
@@ -187,6 +187,11 @@
AttackMove:
AssaultMoveCondition: assault-move
+^PlayerHandicaps:
+ HandicapFirepowerMultiplier:
+ HandicapDamageMultiplier:
+ HandicapProductionTimeMultiplier:
+
^AcceptsCloakCrate:
Cloak:
InitialDelay: 15
@@ -224,6 +229,7 @@
Inherits@1: ^ExistsInWorld
Inherits@3: ^ClassicFacingSpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -269,6 +275,7 @@
Inherits@1: ^ExistsInWorld
Inherits@3: ^ClassicFacingSpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -323,6 +330,7 @@
Inherits@1: ^ExistsInWorld
Inherits@3: ^SpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -492,6 +500,7 @@
Inherits@2: ^SpriteActor
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -546,6 +555,7 @@
Inherits@2: ^SpriteActor
Inherits@AUTOTARGET: ^AutoTargetGroundAssaultMove
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
Health:
HP: 40000
@@ -601,6 +611,7 @@
^Plane:
Inherits@1: ^ExistsInWorld
Inherits@2: ^ClassicFacingSpriteActor
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -626,6 +637,7 @@
Inherits@1: ^ExistsInWorld
Inherits@3: ^SpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -653,6 +665,7 @@
Inherits@2: ^SpriteActor
Inherits@shape: ^1x1Shape
Inherits@selection: ^SelectableBuilding
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
diff --git a/mods/common/chrome/lobby-players.yaml b/mods/common/chrome/lobby-players.yaml
index 5ed50a4963..dfbef77ee9 100644
--- a/mods/common/chrome/lobby-players.yaml
+++ b/mods/common/chrome/lobby-players.yaml
@@ -35,6 +35,13 @@ Container@LOBBY_PLAYER_BIN:
Text: Team
Align: Center
Font: Bold
+ Label@LABEL_LOBBY_HANDICAP:
+ X: 478
+ Width: 72
+ Height: 25
+ Text: Handicap
+ Align: Center
+ Font: Bold
Label@LABEL_LOBBY_SPAWN:
X: 560
Width: 48
@@ -139,6 +146,12 @@ Container@LOBBY_PLAYER_BIN:
Width: 48
Height: 25
Text: Team
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 478
+ Width: 72
+ Height: 25
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
DropDownButton@SPAWN_DROPDOWN:
X: 560
Width: 48
@@ -249,6 +262,17 @@ Container@LOBBY_PLAYER_BIN:
Width: 48
Height: 25
Visible: false
+ Label@HANDICAP:
+ X: 478
+ Width: 47
+ Height: 25
+ Align: Center
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 478
+ Width: 72
+ Height: 25
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
Label@SPAWN:
X: 560
Width: 23
diff --git a/mods/d2k/chrome/lobby-players.yaml b/mods/d2k/chrome/lobby-players.yaml
index 183fb0ac64..eb15f28c6e 100644
--- a/mods/d2k/chrome/lobby-players.yaml
+++ b/mods/d2k/chrome/lobby-players.yaml
@@ -35,6 +35,13 @@ Container@LOBBY_PLAYER_BIN:
Text: Team
Align: Center
Font: Bold
+ Label@LABEL_LOBBY_HANDICAP:
+ X: 478
+ Width: 72
+ Height: 25
+ Text: Handicap
+ Align: Center
+ Font: Bold
Label@LABEL_LOBBY_SPAWN:
X: 560
Width: 48
@@ -139,6 +146,12 @@ Container@LOBBY_PLAYER_BIN:
Width: 48
Height: 25
Text: Team
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 478
+ Width: 72
+ Height: 25
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
DropDownButton@SPAWN_DROPDOWN:
X: 560
Width: 48
@@ -249,6 +262,17 @@ Container@LOBBY_PLAYER_BIN:
Width: 48
Height: 25
Visible: false
+ Label@HANDICAP:
+ X: 478
+ Width: 47
+ Height: 25
+ Align: Center
+ DropDownButton@HANDICAP_DROPDOWN:
+ X: 478
+ Width: 72
+ Height: 25
+ TooltipContainer: TOOLTIP_CONTAINER
+ TooltipText: A handicap decreases the combat effectiveness of the player's forces
Label@SPAWN:
X: 560
Width: 23
diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml
index ccc579b053..249162af93 100644
--- a/mods/d2k/rules/defaults.yaml
+++ b/mods/d2k/rules/defaults.yaml
@@ -184,10 +184,16 @@
AttackMoveCondition: attack-move
AssaultMoveCondition: assault-move
+^PlayerHandicaps:
+ HandicapFirepowerMultiplier:
+ HandicapDamageMultiplier:
+ HandicapProductionTimeMultiplier:
+
^Vehicle:
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Tooltip:
GenericName: Unit
Huntable:
@@ -298,6 +304,7 @@
Inherits@2: ^GainsExperience
Inherits@3: ^SpriteActor
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Tooltip:
GenericName: Unit
Huntable:
@@ -366,6 +373,7 @@
^Plane:
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
+ Inherits@handicaps: ^PlayerHandicaps
Interactable:
Tooltip:
GenericName: Unit
@@ -391,6 +399,7 @@
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
Inherits@selection: ^SelectableBuilding
+ Inherits@handicaps: ^PlayerHandicaps
Tooltip:
GenericName: Structure
Huntable:
diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml
index 99f9f0f6ed..43e93785a3 100644
--- a/mods/ra/rules/defaults.yaml
+++ b/mods/ra/rules/defaults.yaml
@@ -221,6 +221,11 @@
AttackMove:
AssaultMoveCondition: assault-move
+^PlayerHandicaps:
+ HandicapFirepowerMultiplier:
+ HandicapDamageMultiplier:
+ HandicapProductionTimeMultiplier:
+
^GlobalBounty:
GrantConditionOnPrerequisite@GLOBALBOUNTY:
Condition: global-bounty
@@ -234,6 +239,7 @@
Inherits@3: ^ClassicFacingSpriteActor
Inherits@bounty: ^GlobalBounty
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -313,6 +319,7 @@
Inherits@4: ^SpriteActor
Inherits@bounty: ^GlobalBounty
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -483,6 +490,7 @@
Inherits@4: ^SpriteActor
Inherits@bounty: ^GlobalBounty
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -532,6 +540,7 @@
Inherits@4: ^SpriteActor
Inherits@bounty: ^GlobalBounty
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -632,6 +641,7 @@
Inherits@shape: ^1x1Shape
Inherits@bounty: ^GlobalBounty
Inherits@selection: ^SelectableBuilding
+ Inherits@handicaps: ^PlayerHandicaps
Targetable:
TargetTypes: GroundActor, C4, DetonateAttack, Structure
Building:
@@ -737,6 +747,7 @@
Inherits@1: ^ExistsInWorld
Inherits@2: ^SpriteActor
Inherits@shape: ^1x1Shape
+ Inherits@handicaps: ^PlayerHandicaps
Interactable:
Bounds: 24,24
OwnerLostAction:
diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml
index 50c7b1a5ef..a731b8f5e6 100644
--- a/mods/ts/rules/defaults.yaml
+++ b/mods/ts/rules/defaults.yaml
@@ -183,6 +183,11 @@
AttackMove:
AssaultMoveCondition: assault-move
+^PlayerHandicaps:
+ HandicapFirepowerMultiplier:
+ HandicapDamageMultiplier:
+ HandicapProductionTimeMultiplier:
+
^2x1Shape:
HitShape:
Type: Rectangle
@@ -302,6 +307,7 @@
Inherits@2: ^SpriteActor
Inherits@3: ^Cloakable
Inherits@selection: ^SelectableBuilding
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
Targetable:
TargetTypes: Ground, Building, C4
@@ -538,6 +544,7 @@
Inherits@4: ^Cloakable
Inherits@CRATESTATS: ^CrateStatModifiers
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -754,6 +761,7 @@
Inherits@5: ^DamagedByVeins
Inherits@CRATESTATS: ^CrateStatModifiers
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -860,6 +868,7 @@
Inherits@2: ^ExistsInWorld
Inherits@3: ^Cloakable
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
OwnerLostAction:
Action: Kill
@@ -958,6 +967,7 @@
Inherits@2: ^SpriteActor
Inherits@3: ^HealsOnTiberium
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
Health:
Armor:
@@ -1108,6 +1118,7 @@
Inherits@2: ^ExistsInWorld
Inherits@3: ^Cloakable
Inherits@selection: ^SelectableCombatUnit
+ Inherits@handicaps: ^PlayerHandicaps
Huntable:
RenderVoxels:
RenderSprites: