public IngameMenuLogic(Widget widget, ModData modData, World world, Action onExit, WorldRenderer worldRenderer, IngameInfoPanel activePanel, Dictionary <string, MiniYaml> logicArgs) { this.modData = modData; this.world = world; this.worldRenderer = worldRenderer; this.onExit = onExit; var buttonHandlers = new Dictionary <string, Action> { { "ABORT_MISSION", CreateAbortMissionButton }, { "RESTART", CreateRestartButton }, { "SURRENDER", CreateSurrenderButton }, { "LOAD_GAME", CreateLoadGameButton }, { "SAVE_GAME", CreateSaveGameButton }, { "MUSIC", CreateMusicButton }, { "SETTINGS", CreateSettingsButton }, { "RESUME", CreateResumeButton }, { "SAVE_MAP", CreateSaveMapButton }, { "EXIT_EDITOR", CreateExitEditorButton } }; isSinglePlayer = !world.LobbyInfo.GlobalSettings.Dedicated && world.LobbyInfo.NonBotClients.Count() == 1; menu = widget.Get("INGAME_MENU"); mpe = world.WorldActor.TraitOrDefault <MenuPaletteEffect>(); if (mpe != null) { mpe.Fade(mpe.Info.MenuEffect); } menu.Get <LabelWidget>("VERSION_LABEL").Text = modData.Manifest.Metadata.Version; buttonContainer = menu.Get("MENU_BUTTONS"); buttonTemplate = buttonContainer.Get <ButtonWidget>("BUTTON_TEMPLATE"); buttonContainer.RemoveChild(buttonTemplate); buttonContainer.IsVisible = () => !hideMenu; MiniYaml buttonStrideNode; if (logicArgs.TryGetValue("ButtonStride", out buttonStrideNode)) { buttonStride = FieldLoader.GetValue <int2>("ButtonStride", buttonStrideNode.Value); } var scriptContext = world.WorldActor.TraitOrDefault <LuaScript>(); hasError = scriptContext != null && scriptContext.FatalErrorOccurred; MiniYaml buttonsNode; if (logicArgs.TryGetValue("Buttons", out buttonsNode)) { Action createHandler; var buttonIds = FieldLoader.GetValue <string[]>("Buttons", buttonsNode.Value); foreach (var button in buttonIds) { if (buttonHandlers.TryGetValue(button, out createHandler)) { createHandler(); } } } // Recenter the button container if (buttons.Count > 0) { var expand = (buttons.Count - 1) * buttonStride; buttonContainer.Bounds.X -= expand.X / 2; buttonContainer.Bounds.Y -= expand.Y / 2; buttonContainer.Bounds.Width += expand.X; buttonContainer.Bounds.Height += expand.Y; } var panelRoot = widget.GetOrNull("PANEL_ROOT"); if (panelRoot != null && world.Type != WorldType.Editor) { Action <bool> requestHideMenu = h => hideMenu = h; var gameInfoPanel = Game.LoadWidget(world, "GAME_INFO_PANEL", panelRoot, new WidgetArgs() { { "activePanel", activePanel }, { "hideMenu", requestHideMenu } }); gameInfoPanel.IsVisible = () => !hideMenu; } }
static void Main(string[] args) { var arguments = new Arguments(args); Log.AddChannel("debug", "dedicated-debug.log"); Log.AddChannel("perf", "dedicated-perf.log"); Log.AddChannel("server", "dedicated-server.log"); Log.AddChannel("nat", "dedicated-nat.log"); // Special case handling of Game.Mod argument: if it matches a real filesystem path // then we use this to override the mod search path, and replace it with the mod id var modID = arguments.GetValue("Game.Mod", null); var explicitModPaths = new string[0]; if (modID != null && (File.Exists(modID) || Directory.Exists(modID))) { explicitModPaths = new[] { modID }; modID = Path.GetFileNameWithoutExtension(modID); } if (modID == null) { throw new InvalidOperationException("Game.Mod argument missing or mod could not be found."); } // HACK: The engine code assumes that Game.Settings is set. // This isn't nearly as bad as ModData, but is still not very nice. Game.InitializeSettings(arguments); var settings = Game.Settings.Server; var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS"); var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ? FieldLoader.GetValue <string[]>("MOD_SEARCH_PATHS", envModSearchPaths) : new[] { Path.Combine(".", "mods") }; var mods = new InstalledMods(modSearchPaths, explicitModPaths); // HACK: The engine code *still* assumes that Game.ModData is set var modData = Game.ModData = new ModData(mods[modID], mods); modData.MapCache.LoadMaps(); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); Console.WriteLine("[{0}] Starting dedicated server for mod: {1}", DateTime.Now.ToString(settings.TimestampFormat), modID); while (true) { var server = new Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, modData, true); while (true) { Thread.Sleep(1000); if (server.State == ServerState.GameStarted && server.Conns.Count < 1) { Console.WriteLine("[{0}] No one is playing, shutting down...", DateTime.Now.ToString(settings.TimestampFormat)); server.Shutdown(); break; } } Console.WriteLine("[{0}] Starting a new server instance...", DateTime.Now.ToString(settings.TimestampFormat)); } }
public static T NodeValue <T>(this MiniYamlNode node) { return(FieldLoader.GetValue <T>(node.Key, node.Value.Value)); }
public D2AssetBrowserLogic(Widget widget, Action onExit, ModData modData, World world, Dictionary <string, MiniYaml> logicArgs) { this.world = world; this.modData = modData; panel = widget; var ticker = panel.GetOrNull <LogicTickerWidget>("ANIMATION_TICKER"); if (ticker != null) { ticker.OnTick = () => { if (animateFrames) { SelectNextFrame(); } }; } var sourceDropdown = panel.GetOrNull <DropDownButtonWidget>("SOURCE_SELECTOR"); if (sourceDropdown != null) { sourceDropdown.OnMouseDown = _ => ShowSourceDropdown(sourceDropdown); sourceDropdown.GetText = () => { var name = assetSource != null?Platform.UnresolvePath(assetSource.Name) : "All Packages"; if (name.Length > 15) { name = "..." + name.Substring(name.Length - 15); } return(name); }; } var spriteWidget = panel.GetOrNull <SpriteWidget>("SPRITE"); if (spriteWidget != null) { spriteWidget.GetSprite = () => currentSprites != null ? currentSprites[currentFrame] : null; currentPalette = spriteWidget.Palette; spriteWidget.GetPalette = () => currentPalette; spriteWidget.IsVisible = () => !isVideoLoaded && !isLoadError; } var playerWidget = panel.GetOrNull <WsaPlayerWidget>("PLAYER"); if (playerWidget != null) { playerWidget.IsVisible = () => isVideoLoaded && !isLoadError; } var errorLabelWidget = panel.GetOrNull("ERROR"); if (errorLabelWidget != null) { errorLabelWidget.IsVisible = () => isLoadError; } var paletteDropDown = panel.GetOrNull <DropDownButtonWidget>("PALETTE_SELECTOR"); if (paletteDropDown != null) { paletteDropDown.OnMouseDown = _ => ShowPaletteDropdown(paletteDropDown, world); paletteDropDown.GetText = () => currentPalette; } var colorPreview = panel.GetOrNull <ColorPreviewManagerWidget>("COLOR_MANAGER"); if (colorPreview != null) { colorPreview.Color = Game.Settings.Player.Color; } var colorDropdown = panel.GetOrNull <DropDownButtonWidget>("COLOR"); if (colorDropdown != null) { colorDropdown.IsDisabled = () => currentPalette != colorPreview.PaletteName; colorDropdown.OnMouseDown = _ => ColorPickerLogic.ShowColorDropDown(colorDropdown, colorPreview, world); panel.Get <ColorBlockWidget>("COLORBLOCK").GetColor = () => Game.Settings.Player.Color; } filenameInput = panel.Get <TextFieldWidget>("FILENAME_INPUT"); filenameInput.OnTextEdited = () => ApplyFilter(); filenameInput.OnEscKey = filenameInput.YieldKeyboardFocus; var frameContainer = panel.GetOrNull("FRAME_SELECTOR"); if (frameContainer != null) { frameContainer.IsVisible = () => (currentSprites != null && currentSprites.Length > 1) || (isVideoLoaded && player != null && player.Video != null && player.Video.Length > 1); } frameSlider = panel.Get <SliderWidget>("FRAME_SLIDER"); if (frameSlider != null) { frameSlider.OnChange += x => { if (!isVideoLoaded) { currentFrame = (int)Math.Round(x); } }; frameSlider.GetValue = () => isVideoLoaded ? player.Video.CurrentFrame : currentFrame; frameSlider.IsDisabled = () => isVideoLoaded; } var frameText = panel.GetOrNull <LabelWidget>("FRAME_COUNT"); if (frameText != null) { frameText.GetText = () => isVideoLoaded ? "{0} / {1}".F(player.Video.CurrentFrame + 1, player.Video.Length) : "{0} / {1}".F(currentFrame, currentSprites.Length - 1); } var playButton = panel.GetOrNull <ButtonWidget>("BUTTON_PLAY"); if (playButton != null) { playButton.OnClick = () => { if (isVideoLoaded) { player.Play(); } else { animateFrames = true; } }; playButton.IsVisible = () => isVideoLoaded ? player.Paused : !animateFrames; } var pauseButton = panel.GetOrNull <ButtonWidget>("BUTTON_PAUSE"); if (pauseButton != null) { pauseButton.OnClick = () => { if (isVideoLoaded) { player.Pause(); } else { animateFrames = false; } }; pauseButton.IsVisible = () => isVideoLoaded ? !player.Paused : animateFrames; } var stopButton = panel.GetOrNull <ButtonWidget>("BUTTON_STOP"); if (stopButton != null) { stopButton.OnClick = () => { if (isVideoLoaded) { player.Stop(); } else { frameSlider.Value = 0; currentFrame = 0; animateFrames = false; } }; } var nextButton = panel.GetOrNull <ButtonWidget>("BUTTON_NEXT"); if (nextButton != null) { nextButton.OnClick = () => { if (!isVideoLoaded) { nextButton.OnClick = SelectNextFrame; } }; nextButton.IsVisible = () => !isVideoLoaded; } var prevButton = panel.GetOrNull <ButtonWidget>("BUTTON_PREV"); if (prevButton != null) { prevButton.OnClick = () => { if (!isVideoLoaded) { SelectPreviousFrame(); } }; prevButton.IsVisible = () => !isVideoLoaded; } if (logicArgs.ContainsKey("SupportedFormats")) { allowedExtensions = FieldLoader.GetValue <string[]>("SupportedFormats", logicArgs["SupportedFormats"].Value); } else { allowedExtensions = new string[0]; } acceptablePackages = modData.ModFiles.MountedPackages.Where(p => p.Contents.Any(c => allowedExtensions.Contains(Path.GetExtension(c).ToLowerInvariant()))); assetList = panel.Get <ScrollPanelWidget>("ASSET_LIST"); template = panel.Get <ScrollItemWidget>("ASSET_TEMPLATE"); PopulateAssetList(); var closeButton = panel.GetOrNull <ButtonWidget>("CLOSE_BUTTON"); if (closeButton != null) { closeButton.OnClick = () => { if (isVideoLoaded) { player.Stop(); } Ui.CloseWindow(); onExit(); } } ; } void SelectNextFrame() { currentFrame++; if (currentFrame >= currentSprites.Length) { currentFrame = 0; } } void SelectPreviousFrame() { currentFrame--; if (currentFrame < 0) { currentFrame = currentSprites.Length - 1; } } Dictionary <string, bool> assetVisByName = new Dictionary <string, bool>(); bool FilterAsset(string filename) { var filter = filenameInput.Text; if (string.IsNullOrWhiteSpace(filter)) { return(true); } if (filename.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0) { return(true); } return(false); } void ApplyFilter() { assetVisByName.Clear(); assetList.Layout.AdjustChildren(); assetList.ScrollToTop(); // Select the first visible var firstVisible = assetVisByName.FirstOrDefault(kvp => kvp.Value); IReadOnlyPackage package; string filename; if (firstVisible.Key != null && modData.DefaultFileSystem.TryGetPackageContaining(firstVisible.Key, out package, out filename)) { LoadAsset(package, filename); } } void AddAsset(ScrollPanelWidget list, string filepath, IReadOnlyPackage package, ScrollItemWidget template) { var item = ScrollItemWidget.Setup(template, () => currentFilename == filepath && currentPackage == package, () => { LoadAsset(package, filepath); }); item.Get <LabelWidget>("TITLE").GetText = () => filepath; item.IsVisible = () => { bool visible; if (assetVisByName.TryGetValue(filepath, out visible)) { return(visible); } visible = FilterAsset(filepath); assetVisByName.Add(filepath, visible); return(visible); }; list.AddChild(item); } bool LoadAsset(IReadOnlyPackage package, string filename) { if (isVideoLoaded) { player.Stop(); player = null; isVideoLoaded = false; } if (string.IsNullOrEmpty(filename)) { return(false); } if (!package.Contains(filename)) { return(false); } isLoadError = false; try { currentPackage = package; currentFilename = filename; var prefix = ""; var fs = modData.DefaultFileSystem as OpenRA.FileSystem.FileSystem; if (fs != null) { prefix = fs.GetPrefix(package); if (prefix != null) { prefix += "|"; } } if (Path.GetExtension(filename.ToLowerInvariant()) == ".wsa") { player = panel.Get <WsaPlayerWidget>("PLAYER"); player.Load(prefix + filename); isVideoLoaded = true; frameSlider.MaximumValue = (float)player.Video.Length - 1; frameSlider.Ticks = 0; return(true); } currentSprites = world.Map.Rules.Sequences.SpriteCache[prefix + filename]; currentFrame = 0; frameSlider.MaximumValue = (float)currentSprites.Length - 1; frameSlider.Ticks = currentSprites.Length; } catch (Exception ex) { isLoadError = true; Log.AddChannel("assetbrowser", "assetbrowser.log"); Log.Write("assetbrowser", "Error reading {0}:{3} {1}{3}{2}", filename, ex.Message, ex.StackTrace, Environment.NewLine); return(false); } return(true); } bool ShowSourceDropdown(DropDownButtonWidget dropdown) { Func <IReadOnlyPackage, ScrollItemWidget, ScrollItemWidget> setupItem = (source, itemTemplate) => { var item = ScrollItemWidget.Setup(itemTemplate, () => assetSource == source, () => { assetSource = source; PopulateAssetList(); }); item.Get <LabelWidget>("LABEL").GetText = () => source != null?Platform.UnresolvePath(source.Name) : "All Packages"; return(item); }; var sources = new[] { (IReadOnlyPackage)null }.Concat(acceptablePackages); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, sources, setupItem); return(true); } void PopulateAssetList() { assetList.RemoveChildren(); var files = new SortedList <string, List <IReadOnlyPackage> >(); if (assetSource != null) { foreach (var content in assetSource.Contents) { files.Add(content, new List <IReadOnlyPackage> { assetSource }); } } else { foreach (var mountedPackage in modData.ModFiles.MountedPackages) { foreach (var content in mountedPackage.Contents) { if (!files.ContainsKey(content)) { files.Add(content, new List <IReadOnlyPackage> { mountedPackage }); } else { files[content].Add(mountedPackage); } } } } foreach (var file in files.OrderBy(s => s.Key)) { if (!allowedExtensions.Any(ext => file.Key.EndsWith(ext, true, CultureInfo.InvariantCulture))) { continue; } foreach (var package in file.Value) { AddAsset(assetList, file.Key, package, template); } } } bool ShowPaletteDropdown(DropDownButtonWidget dropdown, World world) { Func <string, ScrollItemWidget, ScrollItemWidget> setupItem = (name, itemTemplate) => { var item = ScrollItemWidget.Setup(itemTemplate, () => currentPalette == name, () => currentPalette = name); item.Get <LabelWidget>("LABEL").GetText = () => name; return(item); }; var palettes = world.WorldActor.TraitsImplementing <IProvidesAssetBrowserPalettes>() .SelectMany(p => p.PaletteNames); dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, palettes, setupItem); return(true); } }
public ButtonTooltipWithDescHighlightLogic(Widget widget, ButtonWidget button, Dictionary <string, MiniYaml> logicArgs) { var label = widget.Get <LabelWidget>("LABEL"); var font = Game.Renderer.Fonts[label.Font]; var text = button.GetTooltipText(); var labelWidth = font.Measure(text).X; var key = button.Key.GetValue(); label.GetText = () => text; label.Bounds.Width = labelWidth; widget.Bounds.Width = 2 * label.Bounds.X + labelWidth; if (key.IsValid()) { var hotkey = widget.Get <LabelWidget>("HOTKEY"); hotkey.Visible = true; var hotkeyLabel = "({0})".F(key.DisplayString()); hotkey.GetText = () => hotkeyLabel; hotkey.Bounds.X = labelWidth + 2 * label.Bounds.X; widget.Bounds.Width = hotkey.Bounds.X + label.Bounds.X + font.Measure(hotkeyLabel).X; } var desc = button.GetTooltipDesc(); if (!string.IsNullOrEmpty(desc)) { var descTemplate = widget.Get <LabelWidget>("DESC"); var highlightColor = FieldLoader.GetValue <Color>("Highlight", logicArgs["Highlight"].Value); widget.RemoveChild(descTemplate); var descFont = Game.Renderer.Fonts[descTemplate.Font]; var descWidth = 0; var descOffset = descTemplate.Bounds.Y; foreach (var l in desc.Split(new[] { "\\n" }, StringSplitOptions.None)) { var line = l; var lineWidth = 0; var xOffset = descTemplate.Bounds.X; while (line.Length > 0) { var highlightStart = line.IndexOf('{'); var highlightEnd = line.IndexOf('}', 0); if (highlightStart > 0 && highlightEnd > highlightStart) { if (highlightStart > 0) { // Normal line segment before highlight var lineNormal = line.Substring(0, highlightStart); var lineNormalWidth = descFont.Measure(lineNormal).X; var lineNormalLabel = (LabelWidget)descTemplate.Clone(); lineNormalLabel.GetText = () => lineNormal; lineNormalLabel.Bounds.X = descTemplate.Bounds.X + lineWidth; lineNormalLabel.Bounds.Y = descOffset; lineNormalLabel.Bounds.Width = lineNormalWidth; widget.AddChild(lineNormalLabel); lineWidth += lineNormalWidth; } // Highlight line segment var lineHighlight = line.Substring(highlightStart + 1, highlightEnd - highlightStart - 1); var lineHighlightWidth = descFont.Measure(lineHighlight).X; var lineHighlightLabel = (LabelWidget)descTemplate.Clone(); lineHighlightLabel.GetText = () => lineHighlight; lineHighlightLabel.GetColor = () => highlightColor; lineHighlightLabel.Bounds.X = descTemplate.Bounds.X + lineWidth; lineHighlightLabel.Bounds.Y = descOffset; lineHighlightLabel.Bounds.Width = lineHighlightWidth; widget.AddChild(lineHighlightLabel); lineWidth += lineHighlightWidth; line = line.Substring(highlightEnd + 1); } else { // Final normal line segment var lineLabel = (LabelWidget)descTemplate.Clone(); var width = descFont.Measure(line).X; lineLabel.GetText = () => line; lineLabel.Bounds.X = descTemplate.Bounds.X + lineWidth; lineLabel.Bounds.Y = descOffset; widget.AddChild(lineLabel); lineWidth += width; break; } } descWidth = Math.Max(descWidth, lineWidth); descOffset += descTemplate.Bounds.Height; } widget.Bounds.Width = Math.Max(widget.Bounds.Width, descTemplate.Bounds.X * 2 + descWidth); widget.Bounds.Height += descOffset - descTemplate.Bounds.Y + descTemplate.Bounds.X; } }
static void Run(string[] args) { var arguments = new Arguments(args); var engineDirArg = arguments.GetValue("Engine.EngineDir", null); if (!string.IsNullOrEmpty(engineDirArg)) { Platform.OverrideEngineDir(engineDirArg); } var supportDirArg = arguments.GetValue("Engine.SupportDir", null); if (!string.IsNullOrEmpty(supportDirArg)) { Platform.OverrideSupportDir(supportDirArg); } Log.AddChannel("debug", "dedicated-debug.log", true); Log.AddChannel("perf", "dedicated-perf.log", true); Log.AddChannel("server", "dedicated-server.log", true); Log.AddChannel("nat", "dedicated-nat.log", true); Log.AddChannel("geoip", "dedicated-geoip.log", true); // Special case handling of Game.Mod argument: if it matches a real filesystem path // then we use this to override the mod search path, and replace it with the mod id var modID = arguments.GetValue("Game.Mod", null); var explicitModPaths = Array.Empty <string>(); if (modID != null && (File.Exists(modID) || Directory.Exists(modID))) { explicitModPaths = new[] { modID }; modID = Path.GetFileNameWithoutExtension(modID); } if (modID == null) { throw new InvalidOperationException("Game.Mod argument missing or mod could not be found."); } // HACK: The engine code assumes that Game.Settings is set. // This isn't nearly as bad as ModData, but is still not very nice. Game.InitializeSettings(arguments); var settings = Game.Settings.Server; Nat.Initialize(); var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS"); var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ? FieldLoader.GetValue <string[]>("MOD_SEARCH_PATHS", envModSearchPaths) : new[] { Path.Combine(Platform.EngineDir, "mods") }; var mods = new InstalledMods(modSearchPaths, explicitModPaths); WriteLineWithTimeStamp($"Starting dedicated server for mod: {modID}"); while (true) { // HACK: The engine code *still* assumes that Game.ModData is set var modData = Game.ModData = new ModData(mods[modID], mods); modData.MapCache.LoadMaps(); settings.Map = modData.MapCache.ChooseInitialMap(settings.Map, new MersenneTwister()); var endpoints = new List <IPEndPoint> { new IPEndPoint(IPAddress.IPv6Any, settings.ListenPort), new IPEndPoint(IPAddress.Any, settings.ListenPort) }; var server = new Server(endpoints, settings, modData, ServerType.Dedicated); GC.Collect(); while (true) { Thread.Sleep(1000); if (server.State == ServerState.GameStarted && server.Conns.Count < 1) { WriteLineWithTimeStamp("No one is playing, shutting down..."); server.Shutdown(); break; } } modData.Dispose(); WriteLineWithTimeStamp("Starting a new server instance..."); } }
internal static void UpgradeMapFormat(ModData modData, IReadWritePackage package) { if (package == null) { return; } var yamlStream = package.GetStream("map.yaml"); if (yamlStream == null) { return; } var yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, package.Name)); var nd = yaml.ToDictionary(); var mapFormat = FieldLoader.GetValue <int>("MapFormat", nd["MapFormat"].Value); if (mapFormat < 6) { throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(mapFormat, package.Name)); } // Format 6 -> 7 combined the Selectable and UseAsShellmap flags into the Class enum if (mapFormat < 7) { MiniYaml useAsShellmap; if (nd.TryGetValue("UseAsShellmap", out useAsShellmap) && bool.Parse(useAsShellmap.Value)) { yaml.Nodes.Add(new MiniYamlNode("Visibility", new MiniYaml("Shellmap"))); } else if (nd["Type"].Value == "Mission" || nd["Type"].Value == "Campaign") { yaml.Nodes.Add(new MiniYamlNode("Visibility", new MiniYaml("MissionSelector"))); } } // Format 7 -> 8 replaced normalized HSL triples with rgb(a) hex colors if (mapFormat < 8) { var players = yaml.Nodes.FirstOrDefault(n => n.Key == "Players"); if (players != null) { bool noteHexColors = false; bool noteColorRamp = false; foreach (var player in players.Value.Nodes) { var colorRampNode = player.Value.Nodes.FirstOrDefault(n => n.Key == "ColorRamp"); if (colorRampNode != null) { Color dummy; var parts = colorRampNode.Value.Value.Split(','); if (parts.Length == 3 || parts.Length == 4) { // Try to convert old normalized HSL value to a rgb hex color try { HSLColor color = new HSLColor( (byte)Exts.ParseIntegerInvariant(parts[0].Trim()).Clamp(0, 255), (byte)Exts.ParseIntegerInvariant(parts[1].Trim()).Clamp(0, 255), (byte)Exts.ParseIntegerInvariant(parts[2].Trim()).Clamp(0, 255)); colorRampNode.Value.Value = FieldSaver.FormatValue(color); noteHexColors = true; } catch (Exception) { throw new InvalidDataException("Invalid ColorRamp value.\n File: " + package.Name); } } else if (parts.Length != 1 || !HSLColor.TryParseRGB(parts[0], out dummy)) { throw new InvalidDataException("Invalid ColorRamp value.\n File: " + package.Name); } colorRampNode.Key = "Color"; noteColorRamp = true; } } if (noteHexColors) { Console.WriteLine("ColorRamp is now called Color and uses rgb(a) hex value - rrggbb[aa]."); } else if (noteColorRamp) { Console.WriteLine("ColorRamp is now called Color."); } } } // Format 8 -> 9 moved map options and videos from the map file itself to traits if (mapFormat < 9) { var rules = yaml.Nodes.FirstOrDefault(n => n.Key == "Rules"); var worldNode = rules.Value.Nodes.FirstOrDefault(n => n.Key == "World"); if (worldNode == null) { worldNode = new MiniYamlNode("World", new MiniYaml("", new List <MiniYamlNode>())); } var playerNode = rules.Value.Nodes.FirstOrDefault(n => n.Key == "Player"); if (playerNode == null) { playerNode = new MiniYamlNode("Player", new MiniYaml("", new List <MiniYamlNode>())); } var visibilityNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Visibility"); if (visibilityNode != null) { var visibility = FieldLoader.GetValue <MapVisibility>("Visibility", visibilityNode.Value.Value); if (visibility.HasFlag(MapVisibility.MissionSelector)) { var missionData = new MiniYamlNode("MissionData", new MiniYaml("", new List <MiniYamlNode>())); worldNode.Value.Nodes.Add(missionData); var description = yaml.Nodes.FirstOrDefault(n => n.Key == "Description"); if (description != null) { missionData.Value.Nodes.Add(new MiniYamlNode("Briefing", description.Value.Value)); } var videos = yaml.Nodes.FirstOrDefault(n => n.Key == "Videos"); if (videos != null && videos.Value.Nodes.Any()) { var backgroundVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "BackgroundInfo"); if (backgroundVideo != null) { missionData.Value.Nodes.Add(new MiniYamlNode("BackgroundVideo", backgroundVideo.Value.Value)); } var briefingVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "Briefing"); if (briefingVideo != null) { missionData.Value.Nodes.Add(new MiniYamlNode("BriefingVideo", briefingVideo.Value.Value)); } var startVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameStart"); if (startVideo != null) { missionData.Value.Nodes.Add(new MiniYamlNode("StartVideo", startVideo.Value.Value)); } var winVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameWon"); if (winVideo != null) { missionData.Value.Nodes.Add(new MiniYamlNode("WinVideo", winVideo.Value.Value)); } var lossVideo = videos.Value.Nodes.FirstOrDefault(n => n.Key == "GameLost"); if (lossVideo != null) { missionData.Value.Nodes.Add(new MiniYamlNode("LossVideo", lossVideo.Value.Value)); } } } } var mapOptions = yaml.Nodes.FirstOrDefault(n => n.Key == "Options"); if (mapOptions != null) { var cheats = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Cheats"); if (cheats != null) { worldNode.Value.Nodes.Add(new MiniYamlNode("DeveloperMode", new MiniYaml("", new List <MiniYamlNode>() { new MiniYamlNode("Locked", "True"), new MiniYamlNode("Enabled", cheats.Value.Value) }))); } var crates = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Crates"); if (crates != null && !worldNode.Value.Nodes.Any(n => n.Key == "-CrateSpawner")) { if (!FieldLoader.GetValue <bool>("crates", crates.Value.Value)) { worldNode.Value.Nodes.Add(new MiniYamlNode("-CrateSpawner", new MiniYaml(""))); } } var creeps = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Creeps"); if (creeps != null) { worldNode.Value.Nodes.Add(new MiniYamlNode("MapCreeps", new MiniYaml("", new List <MiniYamlNode>() { new MiniYamlNode("Locked", "True"), new MiniYamlNode("Enabled", creeps.Value.Value) }))); } var fog = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Fog"); var shroud = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Shroud"); if (fog != null || shroud != null) { var shroudNode = new MiniYamlNode("Shroud", new MiniYaml("", new List <MiniYamlNode>())); playerNode.Value.Nodes.Add(shroudNode); if (fog != null) { shroudNode.Value.Nodes.Add(new MiniYamlNode("FogLocked", "True")); shroudNode.Value.Nodes.Add(new MiniYamlNode("FogEnabled", fog.Value.Value)); } if (shroud != null) { var enabled = FieldLoader.GetValue <bool>("shroud", shroud.Value.Value); shroudNode.Value.Nodes.Add(new MiniYamlNode("ExploredMapLocked", "True")); shroudNode.Value.Nodes.Add(new MiniYamlNode("ExploredMapEnabled", FieldSaver.FormatValue(!enabled))); } } var allyBuildRadius = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "AllyBuildRadius"); if (allyBuildRadius != null) { worldNode.Value.Nodes.Add(new MiniYamlNode("MapBuildRadius", new MiniYaml("", new List <MiniYamlNode>() { new MiniYamlNode("AllyBuildRadiusLocked", "True"), new MiniYamlNode("AllyBuildRadiusEnabled", allyBuildRadius.Value.Value) }))); } var startingCash = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "StartingCash"); if (startingCash != null) { playerNode.Value.Nodes.Add(new MiniYamlNode("PlayerResources", new MiniYaml("", new List <MiniYamlNode>() { new MiniYamlNode("DefaultCashLocked", "True"), new MiniYamlNode("DefaultCash", startingCash.Value.Value) }))); } var startingUnits = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "ConfigurableStartingUnits"); if (startingUnits != null && !worldNode.Value.Nodes.Any(n => n.Key == "-SpawnMPUnits")) { worldNode.Value.Nodes.Add(new MiniYamlNode("SpawnMPUnits", new MiniYaml("", new List <MiniYamlNode>() { new MiniYamlNode("Locked", "True"), }))); } var techLevel = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "TechLevel"); var difficulties = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulties"); var shortGame = mapOptions.Value.Nodes.FirstOrDefault(n => n.Key == "ShortGame"); if (techLevel != null || difficulties != null || shortGame != null) { var optionsNode = new MiniYamlNode("MapOptions", new MiniYaml("", new List <MiniYamlNode>())); worldNode.Value.Nodes.Add(optionsNode); if (techLevel != null) { optionsNode.Value.Nodes.Add(new MiniYamlNode("TechLevelLocked", "True")); optionsNode.Value.Nodes.Add(new MiniYamlNode("TechLevel", techLevel.Value.Value)); } if (difficulties != null) { optionsNode.Value.Nodes.Add(new MiniYamlNode("Difficulties", difficulties.Value.Value)); } if (shortGame != null) { optionsNode.Value.Nodes.Add(new MiniYamlNode("ShortGameLocked", "True")); optionsNode.Value.Nodes.Add(new MiniYamlNode("ShortGameEnabled", shortGame.Value.Value)); } } } if (worldNode.Value.Nodes.Any() && !rules.Value.Nodes.Contains(worldNode)) { rules.Value.Nodes.Add(worldNode); } if (playerNode.Value.Nodes.Any() && !rules.Value.Nodes.Contains(playerNode)) { rules.Value.Nodes.Add(playerNode); } } // Format 9 -> 10 moved smudges to SmudgeLayer, and uses map.png for all maps if (mapFormat < 10) { ExtractSmudges(yaml); if (package.Contains("map.png")) { yaml.Nodes.Add(new MiniYamlNode("LockPreview", new MiniYaml("True"))); } } // Format 10 -> 11 replaced the single map type field with a list of categories if (mapFormat < 11) { var type = yaml.Nodes.First(n => n.Key == "Type"); yaml.Nodes.Add(new MiniYamlNode("Categories", type.Value)); yaml.Nodes.Remove(type); } if (mapFormat < Map.SupportedMapFormat) { yaml.Nodes.First(n => n.Key == "MapFormat").Value = new MiniYaml(Map.SupportedMapFormat.ToString()); Console.WriteLine("Converted {0} to MapFormat {1}.", package.Name, Map.SupportedMapFormat); } package.Update("map.yaml", Encoding.UTF8.GetBytes(yaml.Nodes.WriteToString())); }
public static T Get <T>(string key) { return(FieldLoader.GetValue <T>(key, data[key])); }
internal static void UpgradeWeaponRules(ModData modData, int engineVersion, ref List <MiniYamlNode> nodes, MiniYamlNode parent, int depth) { foreach (var node in nodes) { // Refactor Missile RangeLimit from ticks to WDist if (engineVersion < 20160509) { var weapRange = node.Value.Nodes.FirstOrDefault(n => n.Key == "Range"); var projectile = node.Value.Nodes.FirstOrDefault(n => n.Key == "Projectile"); if (projectile != null && weapRange != null && projectile.Value.Value == "Missile") { var oldWDist = FieldLoader.GetValue <WDist>("Range", weapRange.Value.Value); var rangeLimitNode = projectile.Value.Nodes.FirstOrDefault(x => x.Key == "RangeLimit"); // RangeLimit is now a WDist value, so for the conversion, we take weapon range and add 20% on top. // Overly complicated calculations using Range, Speed and the old RangeLimit value would be rather pointless, // because currently most mods have somewhat arbitrary, usually too high and in a few cases too low RangeLimits anyway. var newValue = oldWDist.Length * 120 / 100; var newCells = newValue / 1024; var newCellPart = newValue % 1024; if (rangeLimitNode != null) { rangeLimitNode.Value.Value = newCells.ToString() + "c" + newCellPart.ToString(); } else { // Since the old default was 'unlimited', we're using weapon range * 1.2 for missiles not defining a custom RangeLimit as well projectile.Value.Nodes.Add(new MiniYamlNode("RangeLimit", newCells.ToString() + "c" + newCellPart.ToString())); } } } // Streamline some projectile property names and functionality if (engineVersion < 20160601) { if (node.Key == "Sequence") { node.Key = "Sequences"; } if (node.Key == "TrailSequence") { node.Key = "TrailSequences"; } if (node.Key == "Trail") { node.Key = "TrailImage"; } if (node.Key == "Velocity") { node.Key = "Speed"; } } UpgradeWeaponRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } }
internal static void UpgradeActorRules(ModData modData, int engineVersion, ref List <MiniYamlNode> nodes, MiniYamlNode parent, int depth) { var addNodes = new List <MiniYamlNode>(); foreach (var node in nodes) { if (engineVersion < 20160515) { // Use generic naming for building demolition using explosives. if (node.Key == "C4Demolition") { node.Key = "Demolition"; } foreach (var n in node.Value.Nodes) { if (n.Key == "C4Delay") { n.Key = "DetonationDelay"; } } } // WithSmoke was refactored to become more generic and Sequence/Image notation has been unified. if (engineVersion < 20160528) { if (depth == 1 && node.Key.StartsWith("WithSmoke")) { var s = node.Value.Nodes.FirstOrDefault(n => n.Key == "Sequence"); if (s != null) { s.Key = "Image"; } var parts = node.Key.Split('@'); node.Key = "WithDamageOverlay"; if (parts.Length > 1) { node.Key += "@" + parts[1]; } } } if (engineVersion < 20160604 && node.Key.StartsWith("ProvidesTechPrerequisite")) { var name = node.Value.Nodes.First(n => n.Key == "Name"); var id = name.Value.Value.ToLowerInvariant().Replace(" ", ""); node.Value.Nodes.Add(new MiniYamlNode("Id", id)); } if (engineVersion < 20160611) { // Deprecated WithSpriteRotorOverlay if (depth == 1 && node.Key.StartsWith("WithSpriteRotorOverlay")) { var parts = node.Key.Split('@'); node.Key = "WithIdleOverlay"; if (parts.Length > 1) { node.Key += "@" + parts[1]; } Console.WriteLine("The 'WithSpriteRotorOverlay' trait has been removed."); Console.WriteLine("Its functionality can be fully replicated with 'WithIdleOverlay' + upgrades."); Console.WriteLine("Look at the helicopters in our RA / C&C1 mods for implementation details."); } } // Map difficulty configuration was split to a generic trait if (engineVersion < 20160614 && node.Key.StartsWith("MapOptions")) { var difficultiesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulties"); if (difficultiesNode != null) { var difficulties = FieldLoader.GetValue <string[]>("Difficulties", difficultiesNode.Value.Value) .ToDictionary(d => d.Replace(" ", "").ToLowerInvariant(), d => d); node.Value.Nodes.Remove(difficultiesNode); var childNodes = new List <MiniYamlNode>() { new MiniYamlNode("ID", "difficulty"), new MiniYamlNode("Label", "Difficulty"), new MiniYamlNode("Values", new MiniYaml("", difficulties.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList())) }; var difficultyNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Difficulty"); if (difficultyNode != null) { childNodes.Add(new MiniYamlNode("Default", difficultyNode.Value.Value.Replace(" ", "").ToLowerInvariant())); node.Value.Nodes.Remove(difficultyNode); } else { childNodes.Add(new MiniYamlNode("Default", difficulties.Keys.First())); } var lockedNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "DifficultyLocked"); if (lockedNode != null) { childNodes.Add(new MiniYamlNode("Locked", lockedNode.Value.Value)); node.Value.Nodes.Remove(lockedNode); } addNodes.Add(new MiniYamlNode("ScriptLobbyDropdown@difficulty", new MiniYaml("", childNodes))); } } if (engineVersion < 20160702) { if (node.Key.StartsWith("GivesExperience")) { var ff = "FriendlyFire"; var ffNode = node.Value.Nodes.FirstOrDefault(n => n.Key == ff); if (ffNode != null) { var newStanceStr = ""; if (FieldLoader.GetValue <bool>(ff, ffNode.Value.Value)) { newStanceStr = "Neutral, Enemy, Ally"; } else { newStanceStr = "Neutral, Enemy"; } node.Value.Nodes.Add(new MiniYamlNode("ValidStances", newStanceStr)); } node.Value.Nodes.Remove(ffNode); } else if (node.Key.StartsWith("GivesBounty")) { var stancesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Stances"); if (stancesNode != null) { stancesNode.Key = "ValidStances"; } } } if (engineVersion < 20160703) { if (node.Key.StartsWith("WithDecoration") || node.Key.StartsWith("WithRankDecoration") || node.Key.StartsWith("WithDecorationCarryable")) { var stancesNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Stances"); if (stancesNode != null) { stancesNode.Key = "ValidStances"; } } } if (engineVersion < 20160704) { if (node.Key.Contains("PoisonedByTiberium")) { node.Key = node.Key.Replace("PoisonedByTiberium", "DamagedByTerrain"); if (!node.Key.StartsWith("-")) { if (node.Value.Nodes.Any(a => a.Key == "Resources")) { node.Value.Nodes.Where(n => n.Key == "Resources").Do(n => n.Key = "Terrain"); } else { node.Value.Nodes.Add(new MiniYamlNode("Terrain", new MiniYaml("Tiberium, BlueTiberium"))); } Console.WriteLine("PoisonedByTiberium: Weapon isn't converted. Copy out the appropriate"); Console.WriteLine("weapon's Damage, ReloadDelay and DamageTypes to DamagedByTerrain's Damage,"); Console.WriteLine("DamageInterval and DamageTypes, respectively, then remove the Weapon tag."); } } if (node.Key.Contains("DamagedWithoutFoundation")) { node.Key = node.Key.Replace("DamagedWithoutFoundation", "DamagedByTerrain"); if (!node.Key.StartsWith("-")) { Console.WriteLine("DamagedWithoutFoundation: Weapon isn't converted. Copy out the appropriate"); Console.WriteLine("weapon's Damage, ReloadDelay and DamageTypes to DamagedByTerrain's Damage,"); Console.WriteLine("DamageInterval and DamageTypes, respectively, then remove the Weapon tag."); Console.WriteLine("SafeTerrain isn't converted. Setup an inverted check using Terrain."); node.Value.Nodes.Add(new MiniYamlNode("StartOnThreshold", new MiniYaml("true"))); if (!node.Value.Nodes.Any(a => a.Key == "DamageThreshold")) { node.Value.Nodes.Add(new MiniYamlNode("DamageThreshold", new MiniYaml("50"))); } } } } // ParticleDensityFactor was converted from a float to an int if (engineVersion < 20160713 && node.Key == "WeatherOverlay") { var density = node.Value.Nodes.FirstOrDefault(n => n.Key == "ParticleDensityFactor"); if (density != null) { var value = float.Parse(density.Value.Value, CultureInfo.InvariantCulture); value = (int)Math.Round(value * 10000, 0); density.Value.Value = value.ToString(); } } UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } foreach (var a in addNodes) { nodes.Add(a); } }
void IGameSaveTraitData.ResolveTraitData(Actor self, List <MiniYamlNode> data) { if (self.World.IsReplay) { return; } var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); if (initialBaseCenterNode != null) { initialBaseCenter = FieldLoader.GetValue <CPos>("InitialBaseCenter", initialBaseCenterNode.Value.Value); } var unitsHangingAroundTheBaseNode = data.FirstOrDefault(n => n.Key == "UnitsHangingAroundTheBase"); if (unitsHangingAroundTheBaseNode != null) { unitsHangingAroundTheBase.Clear(); unitsHangingAroundTheBase.AddRange(FieldLoader.GetValue <uint[]>("UnitsHangingAroundTheBase", unitsHangingAroundTheBaseNode.Value.Value) .Select(a => self.World.GetActorById(a)).Where(a => a != null)); } var activeUnitsNode = data.FirstOrDefault(n => n.Key == "ActiveUnits"); if (activeUnitsNode != null) { activeUnits.Clear(); activeUnits.AddRange(FieldLoader.GetValue <uint[]>("ActiveUnits", activeUnitsNode.Value.Value) .Select(a => self.World.GetActorById(a)).Where(a => a != null)); } var rushTicksNode = data.FirstOrDefault(n => n.Key == "RushTicks"); if (rushTicksNode != null) { rushTicks = FieldLoader.GetValue <int>("RushTicks", rushTicksNode.Value.Value); } var assignRolesTicksNode = data.FirstOrDefault(n => n.Key == "AssignRolesTicks"); if (assignRolesTicksNode != null) { assignRolesTicks = FieldLoader.GetValue <int>("AssignRolesTicks", assignRolesTicksNode.Value.Value); } var attackForceTicksNode = data.FirstOrDefault(n => n.Key == "AttackForceTicks"); if (attackForceTicksNode != null) { attackForceTicks = FieldLoader.GetValue <int>("AttackForceTicks", attackForceTicksNode.Value.Value); } var minAttackForceDelayTicksNode = data.FirstOrDefault(n => n.Key == "MinAttackForceDelayTicks"); if (minAttackForceDelayTicksNode != null) { minAttackForceDelayTicks = FieldLoader.GetValue <int>("MinAttackForceDelayTicks", minAttackForceDelayTicksNode.Value.Value); } var squadsNode = data.FirstOrDefault(n => n.Key == "Squads"); if (squadsNode != null) { Squads.Clear(); foreach (var n in squadsNode.Value.Nodes) { Squads.Add(Squad.Deserialize(bot, this, n.Value)); } } }
static void ExtractFromISCab(string path, MiniYaml actionYaml, List <string> extractedFiles, Action <string> updateMessage) { // Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : Path.Combine(path, actionYaml.Value); var volumeNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Volumes"); if (volumeNode == null) { throw new InvalidDataException("extract-iscab entry doesn't define a Volumes node"); } var extractNode = actionYaml.Nodes.FirstOrDefault(n => n.Key == "Extract"); if (extractNode == null) { throw new InvalidDataException("extract-iscab entry doesn't define an Extract node"); } var volumes = new Dictionary <int, Stream>(); try { foreach (var node in volumeNode.Value.Nodes) { var volume = FieldLoader.GetValue <int>("(key)", node.Key); var stream = File.OpenRead(Path.Combine(path, node.Value.Value)); volumes.Add(volume, stream); } using (var source = File.OpenRead(sourcePath)) { var reader = new InstallShieldCABCompression(source, volumes); foreach (var node in extractNode.Value.Nodes) { var targetPath = Platform.ResolvePath(node.Key); if (File.Exists(targetPath)) { Log.Write("install", "Skipping installed file " + targetPath); continue; } extractedFiles.Add(targetPath); Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); using (var target = File.OpenWrite(targetPath)) { Log.Write("install", $"Extracting {sourcePath} -> {targetPath}"); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); Action <int> onProgress = percent => updateMessage($"Extracting {displayFilename} ({percent}%)"); reader.ExtractFile(node.Value.Value, target, onProgress); } } } } finally { foreach (var kv in volumes) { kv.Value.Dispose(); } } }
static void ExtractFromPackage(ExtractionType type, string path, MiniYaml actionYaml, List <string> extractedFiles, Action <string> updateMessage) { // Yaml path may be specified relative to a named directory (e.g. ^SupportDir) or the detected disc path var sourcePath = actionYaml.Value.StartsWith("^") ? Platform.ResolvePath(actionYaml.Value) : Path.Combine(path, actionYaml.Value); using (var source = File.OpenRead(sourcePath)) { foreach (var node in actionYaml.Nodes) { var targetPath = Platform.ResolvePath(node.Key); if (File.Exists(targetPath)) { Log.Write("install", "Skipping installed file " + targetPath); continue; } var offsetNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Offset"); if (offsetNode == null) { Log.Write("install", "Skipping entry with missing Offset definition " + targetPath); continue; } var lengthNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Length"); if (lengthNode == null) { Log.Write("install", "Skipping entry with missing Length definition " + targetPath); continue; } var length = FieldLoader.GetValue <int>("Length", lengthNode.Value.Value); source.Position = FieldLoader.GetValue <int>("Offset", offsetNode.Value.Value); extractedFiles.Add(targetPath); Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); var displayFilename = Path.GetFileName(Path.GetFileName(targetPath)); Action <long> onProgress = null; if (length < ShowPercentageThreshold) { updateMessage("Extracting " + displayFilename); } else { onProgress = b => updateMessage($"Extracting {displayFilename} ({100 * b / length}%)"); } using (var target = File.OpenWrite(targetPath)) { Log.Write("install", $"Extracting {sourcePath} -> {targetPath}"); if (type == ExtractionType.Blast) { Blast.Decompress(source, target, (read, _) => onProgress?.Invoke(read)); } else { CopyStream(source, target, length, onProgress); } } } } }