Example #1
0
        void AddChatLine(string name, Color nameColor, string text, Color textColor)
        {
            var template = (ContainerWidget)chatTemplate.Clone();

            LobbyUtils.SetupChatLine(template, DateTime.Now, name, nameColor, text, textColor);

            var scrolledToBottom = lobbyChatPanel.ScrolledToBottom;

            lobbyChatPanel.AddChild(template);
            if (scrolledToBottom)
            {
                lobbyChatPanel.ScrollToBottom(smooth: true);
            }

            Game.Sound.PlayNotification(modRules, null, "Sounds", chatLineSound, null);
        }
Example #2
0
        void UpdatePlayerList()
        {
            if (orderManager.LocalClient == null)
            {
                return;
            }

            // Check if we are not assigned to any team, and are no spectator
            // If we are a spectator, check if there are more and enable spectator chat
            // Otherwise check if our assigned team has more players
            if (orderManager.LocalClient.Team == 0 && !orderManager.LocalClient.IsObserver)
            {
                disableTeamChat = true;
            }
            else if (orderManager.LocalClient.IsObserver)
            {
                disableTeamChat = !orderManager.LobbyInfo.Clients.Any(c => c != orderManager.LocalClient && c.IsObserver);
            }
            else
            {
                disableTeamChat = !orderManager.LobbyInfo.Clients.Any(c =>
                                                                      c != orderManager.LocalClient &&
                                                                      c.Bot == null &&
                                                                      c.Team == orderManager.LocalClient.Team);
            }

            if (disableTeamChat)
            {
                teamChat = false;
            }

            var isHost = Game.IsHost;
            var idx    = 0;

            foreach (var kv in orderManager.LobbyInfo.Slots)
            {
                var    key      = kv.Key;
                var    slot     = kv.Value;
                var    client   = orderManager.LobbyInfo.ClientInSlot(key);
                Widget template = null;

                // get template for possible reuse
                if (idx < players.Children.Count)
                {
                    template = players.Children[idx];
                }

                if (client == null)
                {
                    // Empty slot
                    if (template == null || template.Id != emptySlotTemplate.Id)
                    {
                        template = emptySlotTemplate.Clone();
                    }

                    if (isHost)
                    {
                        LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, worldRenderer, map);
                    }
                    else
                    {
                        LobbyUtils.SetupSlotWidget(template, slot, client);
                    }

                    var join = template.Get <ButtonWidget>("JOIN");
                    join.IsVisible  = () => !slot.Closed;
                    join.IsDisabled = () => orderManager.LocalClient.IsReady;
                    join.OnClick    = () => orderManager.IssueOrder(Order.Command("slot " + key));
                }
                else if ((client.Index == orderManager.LocalClient.Index) ||
                         (client.Bot != null && isHost))
                {
                    // Editable player in slot
                    if (template == null || template.Id != editablePlayerTemplate.Id)
                    {
                        template = editablePlayerTemplate.Clone();
                    }

                    LobbyUtils.SetupLatencyWidget(template, client, orderManager);

                    if (client.Bot != null)
                    {
                        LobbyUtils.SetupEditableSlotWidget(template, slot, client, orderManager, worldRenderer, map);
                    }
                    else
                    {
                        LobbyUtils.SetupEditableNameWidget(template, slot, client, orderManager, worldRenderer);
                    }

                    LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, shellmapWorld, colorPreview);
                    LobbyUtils.SetupEditableGameWidget(template, slot, client, orderManager, factions);
                    LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, factions);
                    LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, map);
                    LobbyUtils.SetupEditableSpawnWidget(template, slot, client, orderManager, map);
                    LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, map);
                }
                else
                {
                    // Non-editable player in slot
                    if (template == null || template.Id != nonEditablePlayerTemplate.Id)
                    {
                        template = nonEditablePlayerTemplate.Clone();
                    }

                    LobbyUtils.SetupLatencyWidget(template, client, orderManager);
                    LobbyUtils.SetupColorWidget(template, slot, client);
                    LobbyUtils.SetupGameWidget(template, slot, client, factions);
                    LobbyUtils.SetupFactionWidget(template, slot, client, factions);

                    if (isHost)
                    {
                        LobbyUtils.SetupEditableTeamWidget(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);
                    }
                    else
                    {
                        LobbyUtils.SetupNameWidget(template, slot, client, orderManager, worldRenderer);
                        LobbyUtils.SetupTeamWidget(template, slot, client);
                        LobbyUtils.SetupSpawnWidget(template, slot, client);
                    }

                    LobbyUtils.SetupReadyWidget(template, slot, client);
                }

                template.IsVisible = () => true;

                if (idx >= players.Children.Count)
                {
                    players.AddChild(template);
                }
                else if (players.Children[idx].Id != template.Id)
                {
                    players.ReplaceChild(players.Children[idx], template);
                }

                idx++;
            }

            // Add spectators
            foreach (var client in orderManager.LobbyInfo.Clients.Where(client => client.Slot == null))
            {
                Widget template = null;
                var    c        = client;

                // get template for possible reuse
                if (idx < players.Children.Count)
                {
                    template = players.Children[idx];
                }

                // Editable spectator
                if (c.Index == orderManager.LocalClient.Index)
                {
                    if (template == null || template.Id != editableSpectatorTemplate.Id)
                    {
                        template = editableSpectatorTemplate.Clone();
                    }

                    LobbyUtils.SetupEditableNameWidget(template, null, c, orderManager, worldRenderer);

                    if (client.IsAdmin)
                    {
                        LobbyUtils.SetupEditableReadyWidget(template, null, client, orderManager, map);
                    }
                    else
                    {
                        LobbyUtils.HideReadyWidgets(template);
                    }
                }
                else
                {
                    // Non-editable spectator
                    if (template == null || template.Id != nonEditableSpectatorTemplate.Id)
                    {
                        template = nonEditableSpectatorTemplate.Clone();
                    }

                    if (isHost)
                    {
                        LobbyUtils.SetupPlayerActionWidget(template, null, client, orderManager, worldRenderer,
                                                           lobby, () => panel = PanelType.Kick, () => panel = PanelType.Players);
                    }
                    else
                    {
                        LobbyUtils.SetupNameWidget(template, null, client, orderManager, worldRenderer);
                    }

                    if (client.IsAdmin)
                    {
                        LobbyUtils.SetupReadyWidget(template, null, client);
                    }
                    else
                    {
                        LobbyUtils.HideReadyWidgets(template);
                    }
                }

                LobbyUtils.SetupLatencyWidget(template, c, orderManager);
                template.IsVisible = () => true;

                if (idx >= players.Children.Count)
                {
                    players.AddChild(template);
                }
                else if (players.Children[idx].Id != template.Id)
                {
                    players.ReplaceChild(players.Children[idx], template);
                }

                idx++;
            }

            // Spectate button
            if (orderManager.LocalClient.Slot != null)
            {
                Widget spec = null;
                if (idx < players.Children.Count)
                {
                    spec = players.Children[idx];
                }
                if (spec == null || spec.Id != newSpectatorTemplate.Id)
                {
                    spec = newSpectatorTemplate.Clone();
                }

                LobbyUtils.SetupKickSpectatorsWidget(spec, orderManager, lobby,
                                                     () => panel = PanelType.Kick, () => panel = PanelType.Players, skirmishMode);

                var btn = spec.Get <ButtonWidget>("SPECTATE");
                btn.OnClick    = () => orderManager.IssueOrder(Order.Command("spectate"));
                btn.IsDisabled = () => orderManager.LocalClient.IsReady;
                btn.IsVisible  = () => orderManager.LobbyInfo.GlobalSettings.AllowSpectators ||
                                 orderManager.LocalClient.IsAdmin;

                spec.IsVisible = () => true;

                if (idx >= players.Children.Count)
                {
                    players.AddChild(spec);
                }
                else if (players.Children[idx].Id != spec.Id)
                {
                    players.ReplaceChild(players.Children[idx], spec);
                }

                idx++;
            }

            while (players.Children.Count > idx)
            {
                players.RemoveChild(players.Children[idx]);
            }

            tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
        }
Example #3
0
        internal LobbyLogicCA(Widget widget, ModData modData, WorldRenderer worldRenderer, OrderManager orderManager,
                              Action onExit, Action onStart, bool skirmishMode, Dictionary <string, MiniYaml> logicArgs)
        {
            map                = MapCache.UnknownMap;
            lobby              = widget;
            this.modData       = modData;
            this.orderManager  = orderManager;
            this.worldRenderer = worldRenderer;
            this.onStart       = onStart;
            this.onExit        = onExit;
            this.skirmishMode  = skirmishMode;

            // TODO: This needs to be reworked to support per-map tech levels, bots, etc.
            modRules      = modData.DefaultRules;
            shellmapWorld = worldRenderer.World;

            services = modData.Manifest.Get <WebServices>();

            orderManager.AddChatLine    += AddChatLine;
            Game.LobbyInfoChanged       += UpdateCurrentMap;
            Game.LobbyInfoChanged       += UpdatePlayerList;
            Game.BeforeGameStart        += OnGameStart;
            Game.ConnectionStateChanged += ConnectionStateChanged;

            var name = lobby.GetOrNull <LabelWidget>("SERVER_NAME");

            if (name != null)
            {
                name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName;
            }

            var mapContainer = Ui.LoadWidget("MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
            {
                { "orderManager", orderManager },
                { "getMap", (Func <MapPreview>)(() => map) },
                {
                    "onMouseDown", (Action <MapPreviewWidget, MapPreview, MouseInput>)((preview, mapPreview, mi) =>
                                                                                       LobbyUtils.SelectSpawnPoint(orderManager, preview, mapPreview, mi))
                },
                {
                    "getSpawnOccupants", (Func <MapPreview, Dictionary <CPos, SpawnOccupant> >)(mapPreview =>
                                                                                                LobbyUtils.GetSpawnOccupants(orderManager.LobbyInfo, mapPreview))
                },
                { "showUnoccupiedSpawnpoints", true },
            });

            mapContainer.IsVisible = () => panel != PanelType.Servers;

            UpdateCurrentMap();

            var playerBin = Ui.LoadWidget("LOBBY_PLAYER_BIN", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs());

            playerBin.IsVisible = () => panel == PanelType.Players;

            players = playerBin.Get <ScrollPanelWidget>("LOBBY_PLAYERS");
            editablePlayerTemplate       = players.Get("TEMPLATE_EDITABLE_PLAYER");
            nonEditablePlayerTemplate    = players.Get("TEMPLATE_NONEDITABLE_PLAYER");
            emptySlotTemplate            = players.Get("TEMPLATE_EMPTY");
            editableSpectatorTemplate    = players.Get("TEMPLATE_EDITABLE_SPECTATOR");
            nonEditableSpectatorTemplate = players.Get("TEMPLATE_NONEDITABLE_SPECTATOR");
            newSpectatorTemplate         = players.Get("TEMPLATE_NEW_SPECTATOR");
            colorPreview       = lobby.Get <ColorPreviewManagerWidget>("COLOR_MANAGER");
            colorPreview.Color = Game.Settings.Player.Color;

            foreach (var f in modRules.Actors["world"].TraitInfos <FactionCAInfo>())
            {
                factions.Add(f.InternalName, new LobbyFaction {
                    Id = f.InternalName, Selectable = f.Selectable, Name = f.Name, Side = f.Side, Description = f.Description, Game = f.Game
                });
            }

            var         gameStarting          = false;
            Func <bool> configurationDisabled = () => !Game.IsHost || gameStarting ||
                                                panel == PanelType.Kick || panel == PanelType.ForceStart ||
                                                !map.RulesLoaded || map.InvalidCustomRules ||
                                                orderManager.LocalClient == null || orderManager.LocalClient.IsReady;

            var mapButton = lobby.GetOrNull <ButtonWidget>("CHANGEMAP_BUTTON");

            if (mapButton != null)
            {
                mapButton.IsVisible  = () => panel != PanelType.Servers;
                mapButton.IsDisabled = () => gameStarting || panel == PanelType.Kick || panel == PanelType.ForceStart ||
                                       orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
                mapButton.OnClick = () =>
                {
                    var onSelect = new Action <string>(uid =>
                    {
                        // Don't select the same map again
                        if (uid == map.Uid)
                        {
                            return;
                        }

                        orderManager.IssueOrder(Order.Command("map " + uid));
                        Game.Settings.Server.Map = uid;
                        Game.Settings.Save();
                    });

                    Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
                    {
                        { "initialMap", map.Uid },
                        { "initialTab", MapClassification.System },
                        { "onExit", DoNothing },
                        { "onSelect", Game.IsHost ? onSelect : null },
                        { "filter", MapVisibility.Lobby },
                    });
                };
            }

            var slotsButton = lobby.GetOrNull <DropDownButtonWidget>("SLOTS_DROPDOWNBUTTON");

            if (slotsButton != null)
            {
                slotsButton.IsVisible  = () => panel != PanelType.Servers;
                slotsButton.IsDisabled = () => configurationDisabled() || panel != PanelType.Players ||
                                         (orderManager.LobbyInfo.Slots.Values.All(s => !s.AllowBots) &&
                                          orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) == 0);

                slotsButton.OnMouseDown = _ =>
                {
                    var botTypes = map.Rules.Actors["player"].TraitInfos <IBotInfo>().Select(t => t.Type);
                    var options  = new Dictionary <string, IEnumerable <DropDownOption> >();

                    var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
                    if (orderManager.LobbyInfo.Slots.Values.Any(s => s.AllowBots))
                    {
                        var botOptions = new List <DropDownOption>()
                        {
                            new DropDownOption()
                            {
                                Title      = "Add",
                                IsSelected = () => false,
                                OnClick    = () =>
                                {
                                    foreach (var slot in orderManager.LobbyInfo.Slots)
                                    {
                                        var bot = botTypes.Random(Game.CosmeticRandom);
                                        var c   = orderManager.LobbyInfo.ClientInSlot(slot.Key);
                                        if (slot.Value.AllowBots == true && (c == null || c.Bot != null))
                                        {
                                            orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot.Key, botController.Index, bot)));
                                        }
                                    }
                                }
                            }
                        };

                        if (orderManager.LobbyInfo.Clients.Any(c => c.Bot != null))
                        {
                            botOptions.Add(new DropDownOption()
                            {
                                Title      = "Remove",
                                IsSelected = () => false,
                                OnClick    = () =>
                                {
                                    foreach (var slot in orderManager.LobbyInfo.Slots)
                                    {
                                        var c = orderManager.LobbyInfo.ClientInSlot(slot.Key);
                                        if (c != null && c.Bot != null)
                                        {
                                            orderManager.IssueOrder(Order.Command("slot_open " + slot.Value.PlayerReference));
                                        }
                                    }
                                }
                            });
                        }

                        options.Add("Configure Bots", botOptions);
                    }

                    var teamCount = (orderManager.LobbyInfo.Slots.Count(s => !s.Value.LockTeam && orderManager.LobbyInfo.ClientInSlot(s.Key) != null) + 1) / 2;
                    if (teamCount >= 1)
                    {
                        var teamOptions = Enumerable.Range(2, teamCount - 1).Reverse().Select(d => new DropDownOption
                        {
                            Title      = "{0} Teams".F(d),
                            IsSelected = () => false,
                            OnClick    = () => orderManager.IssueOrder(Order.Command("assignteams {0}".F(d.ToString())))
                        }).ToList();

                        if (orderManager.LobbyInfo.Slots.Any(s => s.Value.AllowBots))
                        {
                            teamOptions.Add(new DropDownOption
                            {
                                Title      = "Humans vs Bots",
                                IsSelected = () => false,
                                OnClick    = () => orderManager.IssueOrder(Order.Command("assignteams 1"))
                            });
                        }

                        teamOptions.Add(new DropDownOption
                        {
                            Title      = "Free for all",
                            IsSelected = () => false,
                            OnClick    = () => orderManager.IssueOrder(Order.Command("assignteams 0"))
                        });

                        options.Add("Configure Teams", teamOptions);
                    }

                    Func <DropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
                    {
                        var item = ScrollItemWidget.Setup(template, option.IsSelected, option.OnClick);
                        item.Get <LabelWidget>("LABEL").GetText = () => option.Title;
                        return(item);
                    };
                    slotsButton.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 175, options, setupItem);
                };
            }

            var optionsBin = Ui.LoadWidget("LOBBY_OPTIONS_BIN", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs()
            {
                { "orderManager", orderManager },
                { "getMap", (Func <MapPreview>)(() => map) },
                { "configurationDisabled", configurationDisabled }
            });

            optionsBin.IsVisible = () => panel == PanelType.Options;

            var musicBin = Ui.LoadWidget("LOBBY_MUSIC_BIN", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs
            {
                { "onExit", DoNothing },
                { "world", worldRenderer.World }
            });

            musicBin.IsVisible = () => panel == PanelType.Music;

            ServerListLogic serverListLogic = null;

            if (!skirmishMode)
            {
                Action <GameServer> doNothingWithServer = _ => { };

                var serversBin = Ui.LoadWidget("LOBBY_SERVERS_BIN", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs
                {
                    { "onJoin", doNothingWithServer },
                });

                serverListLogic      = serversBin.LogicObjects.Select(l => l as ServerListLogic).FirstOrDefault(l => l != null);
                serversBin.IsVisible = () => panel == PanelType.Servers;
            }

            var tabContainer = skirmishMode ? lobby.Get("SKIRMISH_TABS") : lobby.Get("MULTIPLAYER_TABS");

            tabContainer.IsVisible = () => true;

            var optionsTab = tabContainer.Get <ButtonWidget>("OPTIONS_TAB");

            optionsTab.IsHighlighted = () => panel == PanelType.Options;
            optionsTab.IsDisabled    = OptionsTabDisabled;
            optionsTab.OnClick       = () => panel = PanelType.Options;
            optionsTab.GetText       = () => !map.RulesLoaded ? "Loading..." : optionsTab.Text;

            var playersTab = tabContainer.Get <ButtonWidget>("PLAYERS_TAB");

            playersTab.IsHighlighted = () => panel == PanelType.Players;
            playersTab.IsDisabled    = () => panel == PanelType.Kick || panel == PanelType.ForceStart;
            playersTab.OnClick       = () => panel = PanelType.Players;

            var musicTab = tabContainer.Get <ButtonWidget>("MUSIC_TAB");

            musicTab.IsHighlighted = () => panel == PanelType.Music;
            musicTab.IsDisabled    = () => panel == PanelType.Kick || panel == PanelType.ForceStart;
            musicTab.OnClick       = () => panel = PanelType.Music;

            var serversTab = tabContainer.GetOrNull <ButtonWidget>("SERVERS_TAB");

            if (serversTab != null)
            {
                serversTab.IsHighlighted = () => panel == PanelType.Servers;
                serversTab.IsDisabled    = () => panel == PanelType.Kick || panel == PanelType.ForceStart;
                serversTab.OnClick       = () =>
                {
                    // Refresh the list when switching to the servers tab
                    if (serverListLogic != null && panel != PanelType.Servers)
                    {
                        serverListLogic.RefreshServerList();
                    }

                    panel = PanelType.Servers;
                };
            }

            // Force start panel
            Action startGame = () =>
            {
                gameStarting = true;
                orderManager.IssueOrder(Order.Command("startgame"));
            };

            var startGameButton = lobby.GetOrNull <ButtonWidget>("START_GAME_BUTTON");

            if (startGameButton != null)
            {
                startGameButton.IsDisabled = () => configurationDisabled() || map.Status != MapStatus.Available ||
                                             orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) ||
                                             (!orderManager.LobbyInfo.GlobalSettings.EnableSingleplayer && orderManager.LobbyInfo.NonBotPlayers.Count() < 2);

                startGameButton.OnClick = () =>
                {
                    // Bots and admins don't count
                    if (orderManager.LobbyInfo.Clients.Any(c => c.Slot != null && !c.IsAdmin && c.Bot == null && !c.IsReady))
                    {
                        panel = PanelType.ForceStart;
                    }
                    else
                    {
                        startGame();
                    }
                };
            }

            var forceStartBin = Ui.LoadWidget("FORCE_START_DIALOG", lobby.Get("TOP_PANELS_ROOT"), new WidgetArgs());

            forceStartBin.IsVisible = () => panel == PanelType.ForceStart;
            forceStartBin.Get("KICK_WARNING").IsVisible               = () => orderManager.LobbyInfo.Clients.Any(c => c.IsInvalid);
            forceStartBin.Get <ButtonWidget>("OK_BUTTON").OnClick     = startGame;
            forceStartBin.Get <ButtonWidget>("CANCEL_BUTTON").OnClick = () => panel = PanelType.Players;

            var disconnectButton = lobby.Get <ButtonWidget>("DISCONNECT_BUTTON");

            disconnectButton.OnClick = () => { Ui.CloseWindow(); onExit(); };

            if (skirmishMode)
            {
                disconnectButton.Text = "Back";
            }

            var chatMode = lobby.Get <ButtonWidget>("CHAT_MODE");

            chatMode.GetText    = () => teamChat ? "Team" : "All";
            chatMode.OnClick    = () => teamChat ^= true;
            chatMode.IsDisabled = () => disableTeamChat;

            var chatTextField = lobby.Get <TextFieldWidget>("CHAT_TEXTFIELD");

            chatTextField.MaxLength = UnitOrders.ChatMessageMaxLength;

            chatTextField.TakeKeyboardFocus();
            chatTextField.OnEnterKey = () =>
            {
                if (chatTextField.Text.Length == 0)
                {
                    return(true);
                }

                // Always scroll to bottom when we've typed something
                lobbyChatPanel.ScrollToBottom();

                var teamNumber = 0U;
                if (teamChat && orderManager.LocalClient != null)
                {
                    teamNumber = orderManager.LocalClient.IsObserver ? uint.MaxValue : (uint)orderManager.LocalClient.Team;
                }

                orderManager.IssueOrder(Order.Chat(chatTextField.Text, teamNumber));
                chatTextField.Text = "";
                return(true);
            };

            chatTextField.OnTabKey = () =>
            {
                var previousText = chatTextField.Text;
                chatTextField.Text           = tabCompletion.Complete(chatTextField.Text);
                chatTextField.CursorPosition = chatTextField.Text.Length;

                if (chatTextField.Text == previousText)
                {
                    return(SwitchTeamChat());
                }
                else
                {
                    return(true);
                }
            };

            chatTextField.OnEscKey = () => { chatTextField.Text = ""; return(true); };

            lobbyChatPanel = lobby.Get <ScrollPanelWidget>("CHAT_DISPLAY");
            chatTemplate   = lobbyChatPanel.Get("CHAT_TEMPLATE");
            lobbyChatPanel.RemoveChildren();

            var settingsButton = lobby.GetOrNull <ButtonWidget>("SETTINGS_BUTTON");

            if (settingsButton != null)
            {
                settingsButton.OnClick = () => Ui.OpenWindow("SETTINGS_PANEL", new WidgetArgs
                {
                    { "onExit", DoNothing },
                    { "worldRenderer", worldRenderer }
                });
            }

            // Add a bot on the first lobbyinfo update
            if (skirmishMode)
            {
                addBotOnMapLoad = true;
            }

            MiniYaml yaml;

            if (logicArgs.TryGetValue("ChatLineSound", out yaml))
            {
                chatLineSound = yaml.Value;
            }
        }