Add a per-player handicap option to the lobby.
Handicaps reduce unit health, firepower, and build speed.
This commit is contained in:
@@ -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; } }
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<HandicapProductionTimeMultiplier>, 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 { }
|
||||
}
|
||||
@@ -106,6 +106,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Player Owner { get { return player; } }
|
||||
|
||||
class Watcher
|
||||
{
|
||||
public readonly string Key;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<int, ScrollItemWidget, ScrollItemWidget> 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<LabelWidget>("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<int> 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<DropDownButtonWidget>("HANDICAP_DROPDOWN");
|
||||
dropdown.IsVisible = () => true;
|
||||
dropdown.IsDisabled = () => s.LockTeam || orderManager.LocalClient.IsReady;
|
||||
dropdown.OnMouseDown = _ => ShowHandicapDropDown(dropdown, c, orderManager);
|
||||
|
||||
var handicapLabel = new CachedTransform<int, string>(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<LabelWidget>("HANDICAP");
|
||||
team.IsVisible = () => true;
|
||||
|
||||
var handicapLabel = new CachedTransform<int, string>(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<DropDownButtonWidget>("SPAWN_DROPDOWN");
|
||||
|
||||
Reference in New Issue
Block a user