Skip to content

Commit 8d50e1e

Browse files
committed
Implement proper difficulty dropdown for co-op gamemodes
1 parent cf011e4 commit 8d50e1e

14 files changed

Lines changed: 223 additions & 20 deletions

ClientCore/ClientConfiguration.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public void RefreshSettings()
264264

265265
public string[] TrustedDomains => clientDefinitionsIni.GetStringListValue(SETTINGS, "TrustedDomains", string.Empty);
266266

267-
public string[] AlwaysTrustedDomains = {"cncnet.org", "gamesurge.net", "dronebl.org", "discord.com", "discord.gg", "youtube.com", "youtu.be"};
267+
public string[] AlwaysTrustedDomains = { "cncnet.org", "gamesurge.net", "dronebl.org", "discord.com", "discord.gg", "youtube.com", "youtu.be" };
268268

269269
public (string Name, string Path) GetThemeInfoFromIndex(int themeIndex) => clientDefinitionsIni.GetStringValue("Themes", themeIndex.ToString(), ",").Split(',').AsTuple2();
270270

@@ -287,6 +287,30 @@ public string GetThemePath(string themeName)
287287
return null;
288288
}
289289

290+
private string[] GetCoopDifficultyINIPaths()
291+
{
292+
List<string> paths = new List<string>();
293+
var pathString = clientDefinitionsIni.GetStringValue(SETTINGS, nameof(CoopDifficultyINIPaths), string.Empty);
294+
295+
if (string.IsNullOrEmpty(pathString))
296+
{
297+
paths.Add(SafePath.CombineFilePath("INI", "Map Code", "Coop Difficulty Easy.ini"));
298+
paths.Add(SafePath.CombineFilePath("INI", "Map Code", "Coop Difficulty Medium.ini"));
299+
paths.Add(SafePath.CombineFilePath("INI", "Map Code", "Coop Difficulty Hard.ini"));
300+
}
301+
else
302+
{
303+
var pathStrings = pathString.Split(',', StringSplitOptions.RemoveEmptyEntries);
304+
305+
foreach (var path in pathStrings)
306+
{
307+
paths.Add(SafePath.CombineFilePath(path));
308+
}
309+
}
310+
311+
return paths.ToArray();
312+
}
313+
290314
public string SettingsIniName => clientDefinitionsIni.GetStringValue(SETTINGS, "SettingsFile", "Settings.ini");
291315

292316
public string TranslationIniName => clientDefinitionsIni.GetStringValue(TRANSLATIONS, nameof(TranslationIniName), "Translation.ini");
@@ -348,6 +372,8 @@ private List<TranslationGameFile> ParseTranslationGameFiles()
348372

349373
public string MPMapsIniPath => SafePath.CombineFilePath(clientDefinitionsIni.GetStringValue(SETTINGS, "MPMapsPath", SafePath.CombineFilePath("INI", "MPMaps.ini")));
350374

375+
public string[] CoopDifficultyINIPaths => GetCoopDifficultyINIPaths();
376+
351377
public string KeyboardINI => clientDefinitionsIni.GetStringValue(SETTINGS, "KeyboardINI", "Keyboard.ini");
352378

353379
public int MinimumIngameWidth => clientDefinitionsIni.GetIntValue(SETTINGS, "MinimumIngameWidth", 640);

ClientCore/ProgramConstants.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ public static class ProgramConstants
2626

2727
public const string QRES_EXECUTABLE = "qres.dat";
2828

29-
public const string CNCNET_PROTOCOL_REVISION = "R12";
30-
public const string LAN_PROTOCOL_REVISION = "RL7";
29+
public const string CNCNET_PROTOCOL_REVISION = "R13";
30+
public const string LAN_PROTOCOL_REVISION = "RL8";
3131
public const int LAN_PORT = 1234;
3232
public const int LAN_INGAME_PORT = 1234;
3333
public const int LAN_LOBBY_PORT = 1232;
@@ -112,6 +112,7 @@ public static string GetAILevelName(int aiLevel)
112112

113113
// Static fields might be initialized before the translation file is loaded. Change to readonly properties here.
114114
public static List<string> AI_PLAYER_NAMES => new List<string> { "Easy AI".L10N("Client:Main:EasyAIName"), "Medium AI".L10N("Client:Main:MediumAIName"), "Hard AI".L10N("Client:Main:HardAIName") };
115+
public static List<string> DIFFICULTY_NAMES => new List<string> { "Easy".L10N("Client:Main:DifficultyEasy"), "Medium".L10N("Client:Main:DifficultyMedium"), "Hard".L10N("Client:Main:DifficultyHard") };
115116

116117
public static string LogFileName { get; set; }
117118

DXMainClient/DXGUI/Generic/CampaignSelector.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ public class CampaignSelector : XNAWindow
1919
private const int DEFAULT_WIDTH = 650;
2020
private const int DEFAULT_HEIGHT = 600;
2121

22-
private static string[] DifficultyNames = new string[] { "Easy", "Medium", "Hard" };
23-
2422
private static string[] DifficultyIniPaths = new string[]
2523
{
2624
"INI/Map Code/Difficulty Easy.ini",
@@ -310,7 +308,7 @@ private void LaunchMission(Mission mission)
310308
}
311309

312310
var difficultyIni = new IniFile(SafePath.CombineFilePath(ProgramConstants.GamePath, DifficultyIniPaths[trbDifficultySelector.Value]));
313-
string difficultyName = DifficultyNames[trbDifficultySelector.Value];
311+
string difficultyName = ProgramConstants.DIFFICULTY_NAMES[trbDifficultySelector.Value];
314312

315313
if (copyMapsToSpawnmapINI)
316314
{

DXMainClient/DXGUI/Multiplayer/CnCNet/CnCNetLobby.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1507,7 +1507,7 @@ private void GameBroadcastChannel_CTCPReceived(object sender, ChannelCTCPEventAr
15071507
string msg = e.Message.Substring(5); // Cut out GAME part
15081508
string[] splitMessage = msg.Split(new char[] { ';' });
15091509

1510-
if (splitMessage.Length != 12)
1510+
if (splitMessage.Length != 13)
15111511
{
15121512
Logger.Log("Ignoring CTCP game message because of an invalid amount of parameters.");
15131513
return;
@@ -1538,6 +1538,7 @@ private void GameBroadcastChannel_CTCPReceived(object sender, ChannelCTCPEventAr
15381538

15391539
string loadedGameId = splitMessage[10];
15401540
int skillLevel = int.Parse(splitMessage[11]);
1541+
int difficulty = int.Parse(splitMessage[12]);
15411542

15421543
CnCNetGame cncnetGame = gameCollection.GameList.Find(g => g.GameBroadcastChannel == channel.ChannelName);
15431544

@@ -1561,6 +1562,7 @@ private void GameBroadcastChannel_CTCPReceived(object sender, ChannelCTCPEventAr
15611562
game.Incompatible = cncnetGame == localGame && game.GameVersion != ProgramConstants.GAME_VERSION;
15621563
game.TunnelServer = tunnel;
15631564
game.SkillLevel = skillLevel;
1565+
game.Difficulty = difficulty;
15641566

15651567
if (isClosed)
15661568
{

DXMainClient/DXGUI/Multiplayer/GameInformationPanel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ public void SetInfo(GenericHostedGame game)
152152
string translatedGameModeName = string.IsNullOrEmpty(game.GameMode)
153153
? "Unknown".L10N("Client:Main:Unknown") : game.GameMode.L10N($"INI:GameModes:{game.GameMode}:UIName", notify: false);
154154

155+
if (game.Difficulty >= 0 && game.Difficulty < ProgramConstants.DIFFICULTY_NAMES.Count)
156+
translatedGameModeName += $" ({ProgramConstants.DIFFICULTY_NAMES[game.Difficulty]})";
157+
155158
lblGameMode.Text = Renderer.GetStringWithLimitedWidth("Game mode:".L10N("Client:Main:GameInfoGameMode") + " " + Renderer.GetSafeString(translatedGameModeName, lblGameMode.FontIndex),
156159
lblGameMode.FontIndex, Width - lblGameMode.X);
157160
lblGameMode.Visible = true;

DXMainClient/DXGUI/Multiplayer/GameLobby/CnCNetGameLobby.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Xml.Linq;
7+
18
using ClientCore;
29
using ClientCore.CnCNet5;
10+
using ClientCore.Extensions;
11+
312
using ClientGUI;
4-
using DTAClient.Domain.Multiplayer;
13+
514
using DTAClient.Domain;
15+
using DTAClient.Domain.Multiplayer;
16+
using DTAClient.Domain.Multiplayer.CnCNet;
617
using DTAClient.DXGUI.Generic;
718
using DTAClient.DXGUI.Multiplayer.CnCNet;
819
using DTAClient.DXGUI.Multiplayer.GameLobby.CommandHandlers;
920
using DTAClient.Online;
1021
using DTAClient.Online.EventArguments;
22+
1123
using Microsoft.Xna.Framework;
24+
1225
using Rampastring.Tools;
1326
using Rampastring.XNAUI;
1427
using Rampastring.XNAUI.XNAControls;
15-
using System;
16-
using System.Collections.Generic;
17-
using System.IO;
18-
using System.Linq;
19-
using System.Text;
20-
using DTAClient.Domain.Multiplayer.CnCNet;
21-
using ClientCore.Extensions;
2228

2329
namespace DTAClient.DXGUI.Multiplayer.GameLobby
2430
{
@@ -1046,6 +1052,7 @@ protected override void OnGameOptionChanged()
10461052
sb.Append(RandomSeed);
10471053
sb.Append(Convert.ToInt32(RemoveStartingLocations));
10481054
sb.Append(Map?.UntranslatedName ?? string.Empty);
1055+
sb.Append(ddDifficulty?.SelectedIndex ?? -1);
10491056

10501057
channel.SendCTCPMessage(sb.ToString(), QueuedMessageType.GAME_SETTINGS_MESSAGE, 11);
10511058
}
@@ -1220,6 +1227,15 @@ private void ApplyGameOptions(string sender, string message)
12201227
Convert.ToInt32(RemoveStartingLocations)));
12211228
SetRandomStartingLocations(removeStartingLocations);
12221229

1230+
if (GameMode != null && GameMode.UseDifficultyDropDown && ddDifficulty != null)
1231+
{
1232+
int newIndex = Conversions.IntFromString(parts[partIndex + 9], ddDifficulty.SelectedIndex);
1233+
ddDifficulty.SelectedIndex = newIndex;
1234+
1235+
if (ddDifficulty.SelectedIndex >= 0)
1236+
AddNotice(string.Format("The game host has set {0} to {1}".L10N("Client:Main:HostSetOption"), "Difficulty".L10N("Client:Main:DifficultyOptionName"), ddDifficulty.SelectedItem.Text));
1237+
}
1238+
12231239
RandomSeed = randomSeed;
12241240
}
12251241

@@ -2002,10 +2018,13 @@ private void BroadcastGame()
20022018
sb.Append(0); // LoadedGameId
20032019
sb.Append(";");
20042020
sb.Append(skillLevel); // SkillLevel
2021+
sb.Append(";");
2022+
sb.Append(ddDifficulty?.SelectedIndex ?? -1);
20052023

20062024
broadcastChannel.SendCTCPMessage(sb.ToString(), QueuedMessageType.SYSTEM_MESSAGE, 20);
20072025
}
20082026

2027+
20092028
#endregion
20102029

20112030
public override string GetSwitchName() => "Game Lobby".L10N("Client:Main:GameLobby");

DXMainClient/DXGUI/Multiplayer/GameLobby/GameLobbyBase.cs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ protected GameModeMap GameModeMap
143143
protected ToolTip mapListTooltip;
144144
protected XNAClientDropDown ddGameModeMapFilter;
145145
protected XNALabel lblGameModeSelect;
146+
protected XNAClientDropDown ddDifficulty;
147+
protected XNALabel lblDifficulty;
146148
protected XNAContextMenu mapContextMenu;
147149
private XNAContextMenuItem toggleFavoriteItem;
148150

@@ -170,6 +172,7 @@ protected GameModeMap GameModeMap
170172
protected int UniqueGameID { get; set; }
171173
protected int SideCount { get; private set; }
172174
protected int RandomSelectorCount { get; private set; } = 1;
175+
protected int PreviousSelectedDifficulty { get; set; }
173176

174177
protected List<int[]> RandomSelectors = new List<int[]>();
175178

@@ -296,6 +299,21 @@ public override void Initialize()
296299

297300
lblGameModeSelect = FindChild<XNALabel>(nameof(lblGameModeSelect));
298301

302+
ddDifficulty = FindChild<XNAClientDropDown>(nameof(ddDifficulty), true);
303+
lblDifficulty = FindChild<XNALabel>(nameof(lblDifficulty), true);
304+
305+
if (ddDifficulty != null)
306+
{
307+
// Hardcode difficulty items just in case someone tries something stupid.
308+
ddDifficulty.Items.Clear();
309+
310+
foreach (string difficultyName in ProgramConstants.DIFFICULTY_NAMES)
311+
ddDifficulty.AddItem(difficultyName);
312+
313+
ddDifficulty.SelectedIndex = -1;
314+
ddDifficulty.SelectedIndexChanged += DdDifficulty_SelectedIndexChanged;
315+
}
316+
299317
InitBtnMapSort();
300318

301319
tbMapSearch = FindChild<XNASuggestionTextBox>(nameof(tbMapSearch));
@@ -479,9 +497,23 @@ protected void DdGameModeMapFilter_SelectedIndexChanged(object sender, EventArgs
479497
ListMaps();
480498

481499
if (lbGameModeMapList.SelectedIndex == -1)
500+
{
482501
lbGameModeMapList.SelectedIndex = 0; // Select default GameModeMap
502+
UpdateDifficultyDropdown();
503+
}
483504
else
505+
{
484506
ChangeMap(GameModeMap);
507+
}
508+
}
509+
510+
protected void DdDifficulty_SelectedIndexChanged(object sender, EventArgs e)
511+
{
512+
if (ddDifficulty.SelectedIndex >= 0)
513+
PreviousSelectedDifficulty = ddDifficulty.SelectedIndex;
514+
515+
OnGameOptionChanged();
516+
SetMapLabels();
485517
}
486518

487519
protected void BtnPlayerExtraOptions_LeftClick(object sender, EventArgs e)
@@ -604,7 +636,7 @@ protected void ListMaps()
604636
if (gameModeMap.Map.IsCoop)
605637
{
606638
if (StatisticsManager.Instance.HasBeatCoOpMap(gameModeMap.Map.UntranslatedName, gameModeMap.GameMode.UntranslatedUIName))
607-
rankItem.Texture = RankTextures[Math.Abs(2 - gameModeMap.GameMode.CoopDifficultyLevel) + 1];
639+
rankItem.Texture = RankTextures[Math.Abs(2 - GetCoopDifficultyLevel()) + 1];
608640
else
609641
rankItem.Texture = RankTextures[0];
610642
}
@@ -657,6 +689,45 @@ protected void ListMaps()
657689
LbGameModeMapList_SelectedIndexChanged();
658690
}
659691

692+
protected int GetCoopDifficultyLevel()
693+
{
694+
var gameMode = GameModeMap?.GameMode;
695+
696+
if (gameMode != null)
697+
{
698+
if (gameMode.UseDifficultyDropDown && ddDifficulty != null && ddDifficulty.SelectedIndex >= 0)
699+
{
700+
return ddDifficulty.Items.Count - ddDifficulty.SelectedIndex - 1;
701+
}
702+
else
703+
{
704+
return gameMode.CoopDifficultyLevel;
705+
}
706+
}
707+
708+
return 0;
709+
}
710+
711+
protected virtual void UpdateDifficultyDropdown()
712+
{
713+
bool enabled = GameModeMap != null && GameModeMap.GameMode != null && GameModeMap.GameMode.UseDifficultyDropDown;
714+
715+
if (ddDifficulty != null)
716+
{
717+
ddDifficulty.AllowDropDown = enabled;
718+
719+
if (enabled && ddDifficulty.SelectedIndex < 0)
720+
ddDifficulty.SelectedIndex = PreviousSelectedDifficulty;
721+
else if (!enabled)
722+
ddDifficulty.SelectedIndex = -1;
723+
}
724+
725+
if (lblDifficulty != null)
726+
{
727+
lblDifficulty.TextColor = enabled ? UISettings.ActiveSettings.TextColor : UISettings.ActiveSettings.DisabledItemColor;
728+
}
729+
}
730+
660731
protected abstract int GetDefaultMapRankIndex(GameModeMap gameModeMap);
661732

662733
private void LbGameModeMapList_RightClick(object sender, EventArgs e)
@@ -1476,7 +1547,7 @@ private PlayerHouseInfo[] WriteSpawnIni()
14761547

14771548
GameMode.ApplySpawnIniCode(spawnIni); // Forced options from the game mode
14781549
Map.ApplySpawnIniCode(spawnIni, Players.Count + AIPlayers.Count,
1479-
AIPlayers.Count, GameMode.CoopDifficultyLevel); // Forced options from the map
1550+
AIPlayers.Count, GetCoopDifficultyLevel()); // Forced options from the map
14801551

14811552
// Player options
14821553

@@ -1696,6 +1767,23 @@ private void WriteMap(PlayerHouseInfo[] houseInfos)
16961767
mapIni.SetStringValue("Basic", "OriginalFilename", mapIniFileName);
16971768
}
16981769

1770+
if (GameMode.UseDifficultyDropDown && ddDifficulty != null && ddDifficulty.SelectedIndex >= 0)
1771+
{
1772+
var filePath = string.Empty;
1773+
1774+
try
1775+
{
1776+
filePath = ClientConfiguration.Instance.CoopDifficultyINIPaths[ddDifficulty.SelectedIndex];
1777+
}
1778+
catch (IndexOutOfRangeException)
1779+
{
1780+
Logger.Log($"CoopDifficultyINIPaths does not contain file path for difficulty index {ddDifficulty.SelectedIndex}");
1781+
}
1782+
1783+
IniFile difficultyIni = new IniFile(filePath);
1784+
MapCodeHelper.ApplyMapCode(mapIni, difficultyIni);
1785+
}
1786+
16991787
foreach (GameLobbyCheckBox checkBox in CheckBoxes)
17001788
checkBox.ApplyMapCode(mapIni, GameMode);
17011789

@@ -2197,6 +2285,11 @@ protected virtual void SetMapLabels()
21972285
lblMapAuthor.Text = "By".L10N("Client:Main:AuthorBy") + " " + Renderer.GetSafeString(Map.Author, lblMapAuthor.FontIndex);
21982286
lblGameMode.Text = "Game mode:".L10N("Client:Main:GameModeLabel") + " " + GameMode.UIName;
21992287
lblMapSize.Text = "Size:".L10N("Client:Main:MapSize") + " " + Map.GetSizeString();
2288+
2289+
if (GameMode.UseDifficultyDropDown && ddDifficulty != null && ddDifficulty.SelectedIndex >= 0)
2290+
{
2291+
lblGameMode.Text += " (" + "Difficulty: ".L10N("Client:Main:DifficultyLabel") + ddDifficulty.SelectedItem.Text + ")";
2292+
}
22002293
}
22012294

22022295
/// <summary>
@@ -2215,6 +2308,7 @@ protected virtual void ChangeMap(GameModeMap gameModeMap)
22152308
{
22162309
MapPreviewBox.GameModeMap = null;
22172310
OnGameOptionChanged();
2311+
UpdateDifficultyDropdown();
22182312
return;
22192313
}
22202314

@@ -2227,6 +2321,8 @@ protected virtual void ChangeMap(GameModeMap gameModeMap)
22272321
foreach (var checkBox in CheckBoxes)
22282322
checkBox.AllowChecking = true;
22292323

2324+
UpdateDifficultyDropdown();
2325+
22302326
// We could either pass the CheckBoxes and DropDowns of this class
22312327
// to the Map and GameMode instances and let them apply their forced
22322328
// options, or we could do it in this class with helper functions.

0 commit comments

Comments
 (0)