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: