/// <summary> /// Harmony postfix to perform actions require after the level has loaded. /// </summary> public static void Postfix() { // Don't do anything if mod hasn't activated for whatever reason (mod conflict, harmony error, something else). if (!Loading.isModEnabled) { return; } // Report any 'soft' mod conflicts. if (Loading.softModConflct) { // Soft conflict detected - display warning notification for each one. foreach (string mod in ModUtils.conflictingModNames) { if (mod.Equals("PTG") && ModSettings.dsaPTG == 0) { // Plop the Growables. DontShowAgainMessageBox softConflictBox = MessageBoxBase.ShowModal <DontShowAgainMessageBox>(); softConflictBox.AddParas(Translations.Translate("PRR_CON_PTG0"), Translations.Translate("PRR_CON_PTG1"), Translations.Translate("PRR_CON_PTG2")); softConflictBox.DSAButton.eventClicked += (component, clickEvent) => { ModSettings.dsaPTG = 1; SettingsUtils.SaveSettings(); }; } } } // Report any broken assets and remove from our prefab dictionary. foreach (BuildingInfo prefab in Loading.brokenPrefabs) { Logging.Error("broken prefab: ", prefab.name); Loading.xmlManager.prefabHash.Remove(prefab); } Loading.brokenPrefabs.Clear(); // Init Ploppable Tool panel. PloppableTool.Initialize(); // Add buttons to access building details from zoned building info panels. SettingsPanel.AddInfoPanelButtons(); // Display update notification. try { WhatsNew.ShowWhatsNew(); } catch (Exception e) { Logging.LogException(e, "exception showing WhatsNew panel"); } // Set up options panel event handler. try { OptionsPanel.OptionsEventHook(); } catch (Exception e) { Logging.LogException(e, "exception hooking options panel"); } Logging.KeyMessage("loading complete"); }
/// <summary> /// Called by the game when level loading is complete. /// </summary> /// <param name="mode">Loading mode (e.g. game, editor, scenario, etc.)</param> public override void OnLevelLoaded(LoadMode mode) { // Alert the user to any mod conflicts. ModUtils.NotifyConflict(); // Don't do anything further if mod hasn't activated (conflicting mod detected, or loading into editor instead of game). if (!isModEnabled) { return; } base.OnLevelLoaded(mode); // Don't do anything if in asset editor. if (mode == LoadMode.NewAsset || mode == LoadMode.LoadAsset) { return; } // Wait for loading to fully complete. while (!LoadingManager.instance.m_loadingComplete) { } // Check watchdog flag. if (!patchOperating) { // Patch wasn't operating; display warning notification and exit. HarmonyNotification notification = new HarmonyNotification(); notification.Create(); notification.Show(); return; } // Init Ploppable Tool panel. PloppableTool.Initialize(); // Add buttons to access building details from zoned building info panels. SettingsPanel.AddInfoPanelButtons(); // Deactivate the ploppable panel as it starts hidden. Don't need to deactivate the settings panel as it's not instantiated until first shown. PloppableTool.Instance.gameObject.SetActive(false); // Report any loading errors. Debugging.ReportErrors(); Debugging.Message("loading complete"); // Load settings file and check if we need to display update notification. settingsFile = Configuration <SettingsFile> .Load(); if (settingsFile.NotificationVersion != 2) { // No update notification "Don't show again" flag found; show the notification. UpdateNotification notification = new UpdateNotification(); notification.Create(); notification.Show(); } }
/// <summary> /// Create the titlebar; we no longer use Start() as that's not sufficiently reliable (race conditions), and is no longer needed, with the new create/destroy process. /// </summary> public void Setup() { // Basic setup. width = parent.width; height = RICOSettingsPanel.titleHeight; isVisible = true; canFocus = true; isInteractive = true; relativePosition = Vector3.zero; // Make it draggable. dragHandle = AddUIComponent <UIDragHandle>(); dragHandle.width = width - 50; dragHandle.height = height; dragHandle.relativePosition = Vector3.zero; dragHandle.target = parent; // Decorative icon (top-left). iconSprite = AddUIComponent <UISprite>(); iconSprite.relativePosition = new Vector3(10, 5); iconSprite.spriteName = "ToolbarIconZoomOutCity"; UIUtils.ResizeIcon(iconSprite, new Vector2(30, 30)); iconSprite.relativePosition = new Vector3(10, 5); // Titlebar label. titleLabel = AddUIComponent <UILabel>(); titleLabel.relativePosition = new Vector3(50, 13); titleLabel.text = "Ploppable RICO Revisited v" + PloppableRICOMod.Version; // Close button. closeButton = AddUIComponent <UIButton>(); closeButton.relativePosition = new Vector3(width - 35, 2); closeButton.normalBgSprite = "buttonclose"; closeButton.hoveredBgSprite = "buttonclosehover"; closeButton.pressedBgSprite = "buttonclosepressed"; closeButton.eventClick += (component, clickEvent) => { SettingsPanel.Close(); }; }
/// <summary> /// Draws the Ploppable Tool panel. /// </summary> private void DrawPloppablePanel() { // Check to make sure that we haven't already done this. if (PloppableButton == null) { // Set state flag; this is a new setup. hasShown = false; // Main button on ingame toolbar. PloppableButton = UIView.GetAView().FindUIComponent <UITabstrip>("MainToolstrip").AddUIComponent <UIButton>(); PloppableButton.size = new Vector2(43, 49); PloppableButton.normalBgSprite = "ToolbarIconGroup6Normal"; PloppableButton.normalFgSprite = "IconPolicyBigBusiness"; PloppableButton.focusedBgSprite = "ToolbarIconGroup6Focused"; PloppableButton.hoveredBgSprite = "ToolbarIconGroup6Hovered"; PloppableButton.pressedBgSprite = "ToolbarIconGroup6Pressed"; PloppableButton.disabledBgSprite = "ToolbarIconGroup6Disabled"; PloppableButton.relativePosition = new Vector2(800, 0); PloppableButton.name = "PloppableButton"; // Event handler - show the Ploppable Tool panel when the button is clicked. PloppableButton.eventClick += (component, clickEvent) => { component.Focus(); buildingPanel.isVisible = true; }; // Base panel. buildingPanel = UIView.GetAView().FindUIComponent("TSContainer").AddUIComponent <UIPanel>(); buildingPanel.backgroundSprite = "SubcategoriesPanel"; buildingPanel.isVisible = false; buildingPanel.name = "PloppableBuildingPanel"; buildingPanel.size = new Vector2(859, 109); buildingPanel.relativePosition = new Vector2(0, 0); // Tabstrip. Tabs = UIView.GetAView().FindUIComponent("PloppableBuildingPanel").AddUIComponent <UITabstrip>(); Tabs.size = new Vector2(832, 25); Tabs.relativePosition = new Vector2(13, -25); Tabs.pivot = UIPivotPoint.BottomCenter; Tabs.padding = new RectOffset(0, 3, 0, 0); // Get game sprite thumbnail atlas. UITextureAtlas gameIconAtlas = Resources.FindObjectsOfTypeAll <UITextureAtlas>().FirstOrDefault(a => a.name == "Thumbnails"); // Scroll panel. AddScrollPanel(); // Tabs. for (int i = 0; i <= NumTypes; i++) { // Draw tabs in tabstrip. TabButtons[i] = new UIButton(); TabButtons[i] = Tabs.AddUIComponent <UIButton>(); TabButtons[i].size = new Vector2(46, 25); TabButtons[i].normalBgSprite = "SubBarButtonBase"; TabButtons[i].disabledBgSprite = "SubBarButtonBaseDisabled"; TabButtons[i].pressedBgSprite = "SubBarButtonBasePressed"; TabButtons[i].hoveredBgSprite = "SubBarButtonBaseHovered"; TabButtons[i].focusedBgSprite = "SubBarButtonBaseFocused"; TabButtons[i].state = UIButton.ButtonState.Normal; TabButtons[i].name = Names[i] + "Button"; TabButtons[i].tabStrip = true; TabSprites[i] = new UISprite(); TabSprites[i] = TabButtons[i].AddUIComponent <UISprite>(); // Standard "Vanilla" categories (low and high residential, low and high commercial, and offices) - use standard zoning icons from original vanilla release. if (i <= 5) { TabSprites[i].atlas = gameIconAtlas; SetTabSprite(TabSprites[i], "Zoning" + Names[i]); } else { // Other types don't have standard zoning icons; use policy icons instead. SetTabSprite(TabSprites[i], "IconPolicy" + Names[i]); } } // This can't happen in a loop, because the loop index is undefined after setup has occured (i.e. when the function is actually called). TabButtons[0].eventClick += (component, clickEvent) => TabClicked(0, TabSprites[0]); TabButtons[1].eventClick += (component, clickEvent) => TabClicked(1, TabSprites[1]); TabButtons[2].eventClick += (component, clickEvent) => TabClicked(2, TabSprites[2]); TabButtons[3].eventClick += (component, clickEvent) => TabClicked(3, TabSprites[3]); TabButtons[4].eventClick += (component, clickEvent) => TabClicked(4, TabSprites[4]); TabButtons[5].eventClick += (component, clickEvent) => TabClicked(5, TabSprites[5]); TabButtons[6].eventClick += (component, clickEvent) => TabClicked(6, TabSprites[6]); TabButtons[7].eventClick += (component, clickEvent) => TabClicked(7, TabSprites[7]); TabButtons[8].eventClick += (component, clickEvent) => TabClicked(8, TabSprites[8]); TabButtons[9].eventClick += (component, clickEvent) => TabClicked(9, TabSprites[9]); // Below are DLC categories - AD for first two, then GC for next 3. Will be hidden if relevant DLC is not installed. TabButtons[10].eventClick += (component, clickEvent) => TabClicked(10, TabSprites[10]); TabButtons[11].eventClick += (component, clickEvent) => TabClicked(11, TabSprites[11]); TabButtons[12].eventClick += (component, clickEvent) => TabClicked(12, TabSprites[12]); TabButtons[13].eventClick += (component, clickEvent) => TabClicked(13, TabSprites[13]); TabButtons[14].eventClick += (component, clickEvent) => TabClicked(14, TabSprites[14]); // Activate low residential panel to start with (what the user sees on first opening the panel). //BuildingPanels[0].isVisible = true; // Hide AD tabs if AD is not installed. if (!Util.IsADinstalled()) { TabButtons[10].isVisible = false; TabButtons[11].isVisible = false; } // Hide GC tabs if GC is not installed. if (!Util.IsGCinstalled()) { TabButtons[12].isVisible = false; TabButtons[13].isVisible = false; TabButtons[14].isVisible = false; } // Settings tab. showSettings = UIUtils.CreateButton(Tabs); showSettings.size = new Vector2(100, 25); showSettings.normalBgSprite = "SubBarButtonBase"; showSettings.eventClick += (component, clickEvent) => { SettingsPanel.Open(scrollPanel?.selectedItem?.prefab); }; // Add UI text. SetText(); // Toggle active state on visibility changed if we're using the UI speed boost (deactivating when hidden to minimise UI workload and impact on performance). buildingPanel.eventVisibilityChanged += (component, isVisible) => { // Additional check to allow for the case where speedboost has been deactivated mid-game while the panel was deactivated. if ((ModSettings.speedBoost) || (isVisible && !buildingPanel.gameObject.activeSelf)) { buildingPanel.gameObject.SetActive(isVisible); } // Other checks. if (isVisible) { // If this is the first time we're visible, set the display to the initial default tab (low residential). if (!hasShown) { // Set initial default tab. TabClicked(0, _instance.TabSprites[0]); // Done! hasShown = true; } else { // Clear previous selection and refresh panel. scrollPanel.selectedItem = null; scrollPanel.Refresh(); } } else { // Destroy thumbnail renderer if we're no longer visible. ThumbnailManager.Close(); } }; } }
/// <summary> /// Called by the game when level loading is complete. /// </summary> /// <param name="mode">Loading mode (e.g. game, editor, scenario, etc.)</param> public override void OnLevelLoaded(LoadMode mode) { // Alert the user to any mod conflicts. ModUtils.NotifyConflict(); // Don't do anything further if mod hasn't activated (conflicting mod detected, or loading into editor instead of game). if (!isModEnabled) { return; } base.OnLevelLoaded(mode); // Don't do anything if in asset editor. if (mode == LoadMode.NewAsset || mode == LoadMode.LoadAsset) { return; } // Wait for loading to fully complete. while (!LoadingManager.instance.m_loadingComplete) { } // Check watchdog flag. if (!patchOperating) { // Patch wasn't operating; display warning notification and exit. HarmonyNotification notification = new HarmonyNotification(); notification.Create(); notification.Show(); return; } // Report any broken assets and remove from our prefab dictionary. foreach (BuildingInfo prefab in brokenPrefabs) { Debugging.Message("broken prefab: " + prefab.name); xmlManager.prefabHash.Remove(prefab); } brokenPrefabs.Clear(); // Init Ploppable Tool panel. PloppableTool.Initialize(); // Add buttons to access building details from zoned building info panels. SettingsPanel.AddInfoPanelButtons(); // Report any loading errors. Debugging.ReportErrors(); Debugging.Message("loading complete"); // Load settings file and check if we need to display update notification. if (UpdateNotification.notificationVersion != 3) { // No update notification "Don't show again" flag found; show the notification. UpdateNotification notification = new UpdateNotification(); notification.Create(); notification.Show(); } // Set up options panel event handler. OptionsPanel.OptionsEventHook(); }
/// <summary> /// Initialises the individual display item (as blank). /// </summary> public void Init() { component.text = string.Empty; // Basic layout. component.tooltipAnchor = UITooltipAnchor.Anchored; component.horizontalAlignment = UIHorizontalAlignment.Center; component.verticalAlignment = UIVerticalAlignment.Middle; component.pivot = UIPivotPoint.TopCenter; component.foregroundSpriteMode = UIForegroundSpriteMode.Fill; component.group = component.parent; // Hide the "can't afford" big red crossout that's shown by default. UIComponent uIComponent = (component.childCount <= 0) ? null : component.components[0]; if (uIComponent != null) { uIComponent.isVisible = false; } // Information label - building name. nameLabel = component.AddUIComponent <UILabel>(); nameLabel.textScale = 0.6f; nameLabel.useDropShadow = true; nameLabel.dropShadowColor = new Color32(80, 80, 80, 255); nameLabel.dropShadowOffset = new Vector2(2, -2); nameLabel.autoSize = false; nameLabel.autoHeight = true; nameLabel.wordWrap = true; nameLabel.width = component.width - 10; nameLabel.isVisible = true; nameLabel.relativePosition = new Vector3(5, 5); // Information label - building level. levelLabel = component.AddUIComponent <UILabel>(); levelLabel.textScale = 0.6f; levelLabel.useDropShadow = true; levelLabel.dropShadowColor = new Color32(80, 80, 80, 255); levelLabel.dropShadowOffset = new Vector2(2, -2); levelLabel.autoSize = true; levelLabel.isVisible = true; levelLabel.anchor = UIAnchorStyle.Bottom | UIAnchorStyle.Left; levelLabel.relativePosition = new Vector3(5, component.height - 10); // Information label - building size. sizeLabel = component.AddUIComponent <UILabel>(); sizeLabel.textScale = 0.6f; sizeLabel.useDropShadow = true; sizeLabel.dropShadowColor = new Color32(80, 80, 80, 255); sizeLabel.dropShadowOffset = new Vector2(2, -2); sizeLabel.autoSize = true; sizeLabel.isVisible = true; sizeLabel.anchor = UIAnchorStyle.Bottom | UIAnchorStyle.Left; // Tooltip. component.eventMouseHover += (component, mouseEvent) => { // Reset the tooltip before showing each time, as sometimes it gets clobbered either by the game or another mod. component.tooltip = BuildingTooltip(currentData); }; // Double-click to open building's settings. component.eventDoubleClick += (component, mouseEvent) => { SettingsPanel.Open(currentData.prefab); }; }
/// <summary> /// Draws the Ploppable Tool panel. /// </summary> private void DrawPloppablePanel() { // Check to make sure that we haven't already done this. if (PloppableButton == null) { // Main button on ingame toolbar. PloppableButton = UIView.GetAView().FindUIComponent <UITabstrip>("MainToolstrip").AddUIComponent <UIButton>(); PloppableButton.size = new Vector2(43, 49); PloppableButton.normalBgSprite = "ToolbarIconGroup6Normal"; PloppableButton.normalFgSprite = "IconPolicyBigBusiness"; PloppableButton.focusedBgSprite = "ToolbarIconGroup6Focused"; PloppableButton.hoveredBgSprite = "ToolbarIconGroup6Hovered"; PloppableButton.pressedBgSprite = "ToolbarIconGroup6Pressed"; PloppableButton.disabledBgSprite = "ToolbarIconGroup6Disabled"; PloppableButton.relativePosition = new Vector2(800, 0); PloppableButton.name = "PloppableButton"; PloppableButton.tooltip = Translations.Translate("PRR_NAME"); // Event handler - show the Ploppable Tool panel when the button is clicked. PloppableButton.eventClick += (component, clickEvent) => { component.Focus(); BuildingPanel.isVisible = true; }; // Base panel. BuildingPanel = UIView.GetAView().FindUIComponent("TSContainer").AddUIComponent <UIPanel>(); BuildingPanel.backgroundSprite = "SubcategoriesPanel"; BuildingPanel.isVisible = false; BuildingPanel.name = "PloppableBuildingPanel"; BuildingPanel.size = new Vector2(859, 109); BuildingPanel.relativePosition = new Vector2(0, 0); // Tabstrip. Tabs = UIView.GetAView().FindUIComponent("PloppableBuildingPanel").AddUIComponent <UITabstrip>(); Tabs.size = new Vector2(832, 25); Tabs.relativePosition = new Vector2(13, -25); Tabs.pivot = UIPivotPoint.BottomCenter; Tabs.padding = new RectOffset(0, 3, 0, 0); // Get game sprite thumbnail atlas. UITextureAtlas gameIconAtlas = Resources.FindObjectsOfTypeAll <UITextureAtlas>().FirstOrDefault(a => a.name == "Thumbnails"); // Scrollable panels. for (int i = 0; i <= NumTypes; i++) { // Basic setup. BuildingPanels[i] = new UIScrollablePanel(); BuildingPanels[i] = BuildingPanel.AddUIComponent <UIScrollablePanel>(); BuildingPanels[i].size = new Vector2(763, 109); BuildingPanels[i].relativePosition = new Vector2(50, 0); BuildingPanels[i].name = Names[i] + "Panel"; BuildingPanels[i].isVisible = false; BuildingPanels[i].autoLayout = true; BuildingPanels[i].autoLayoutStart = LayoutStart.BottomLeft; BuildingPanels[i].builtinKeyNavigation = true; BuildingPanels[i].autoLayoutDirection = LayoutDirection.Horizontal; BuildingPanels[i].clipChildren = true; BuildingPanels[i].freeScroll = false; BuildingPanels[i].horizontalScrollbar = new UIScrollbar(); BuildingPanels[i].scrollWheelAmount = 109; BuildingPanels[i].horizontalScrollbar.stepSize = 1f; BuildingPanels[i].horizontalScrollbar.incrementAmount = 109f; BuildingPanels[i].scrollWithArrowKeys = true; // Draw tabs in tabstrip. TabButtons[i] = new UIButton(); TabButtons[i] = Tabs.AddUIComponent <UIButton>(); TabButtons[i].size = new Vector2(46, 25); TabButtons[i].normalBgSprite = "SubBarButtonBase"; TabButtons[i].disabledBgSprite = "SubBarButtonBaseDisabled"; TabButtons[i].pressedBgSprite = "SubBarButtonBasePressed"; TabButtons[i].hoveredBgSprite = "SubBarButtonBaseHovered"; TabButtons[i].focusedBgSprite = "SubBarButtonBaseFocused"; TabButtons[i].state = UIButton.ButtonState.Normal; TabButtons[i].name = Names[i] + "Button"; TabButtons[i].tabStrip = true; TabSprites[i] = new UISprite(); TabSprites[i] = TabButtons[i].AddUIComponent <UISprite>(); // Standard "Vanilla" categories (low and high residential, low and high commercial, and offices) - use standard zoning icons from original vanilla release. if (i <= 5) { TabSprites[i].atlas = gameIconAtlas; SetTabSprite(TabSprites[i], "Zoning" + Names[i]); } else { // Other types don't have standard zoning icons; use policy icons instead. SetTabSprite(TabSprites[i], "IconPolicy" + Names[i]); } } // 'Left' and 'Right' buttons to croll panel. LeftButton = BuildingPanel.AddUIComponent <UIButton>(); RightButton = BuildingPanel.AddUIComponent <UIButton>(); LeftButton.size = new Vector2(32, 32); RightButton.size = new Vector2(32, 32); LeftButton.normalBgSprite = "ArrowLeft"; LeftButton.pressedBgSprite = "ArrowLeftPressed"; LeftButton.hoveredBgSprite = "ArrowLeftHovered"; LeftButton.disabledBgSprite = "ArrowLeftDisabled"; LeftButton.relativePosition = new Vector3(16, 33); RightButton.relativePosition = new Vector3(812, 33); RightButton.normalBgSprite = "ArrowRight"; RightButton.pressedBgSprite = "ArrowRightPressed"; RightButton.hoveredBgSprite = "ArrowRightHovered"; RightButton.disabledBgSprite = "ArrowRightDisabled"; // Initialise current selection to first panel. currentSelection = BuildingPanels[0]; // Event handlers. RightButton.eventClick += (component, clickEvent) => ArrowClicked(component); LeftButton.eventClick += (component, clickEvent) => ArrowClicked(component); // Show left/right scroll buttons if we've got more than seven buttons, otherwise hide. if (BuildingPanels[0].childCount > 7) { LeftButton.isVisible = true; RightButton.isVisible = true; } else { LeftButton.isVisible = false; RightButton.isVisible = false; } // This can't happen in a loop, because the loop index is undefined after setup has occured (i.e. when the function is actually called). TabButtons[0].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[0], TabSprites[0]); TabButtons[1].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[1], TabSprites[1]); TabButtons[2].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[2], TabSprites[2]); TabButtons[3].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[3], TabSprites[3]); TabButtons[4].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[4], TabSprites[4]); TabButtons[5].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[5], TabSprites[5]); TabButtons[6].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[6], TabSprites[6]); TabButtons[7].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[7], TabSprites[7]); TabButtons[8].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[8], TabSprites[8]); TabButtons[9].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[9], TabSprites[9]); // Below are DLC categories - AD for first two, then GC for next 3. Will be hidden if relevant DLC is not installed. TabButtons[10].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[10], TabSprites[10]); TabButtons[11].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[11], TabSprites[11]); TabButtons[12].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[12], TabSprites[12]); TabButtons[13].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[13], TabSprites[13]); TabButtons[14].eventClick += (component, clickEvent) => TabClicked(BuildingPanels[14], TabSprites[14]); // Activate low residential panel to start with (what the user sees on first opening the panel). BuildingPanels[0].isVisible = true; // Hide AD tabs if AD is not installed. if (!Util.isADinstalled()) { TabButtons[10].isVisible = false; TabButtons[11].isVisible = false; } // Hide GC tabs if GC is not installed. if (!Util.isGCinstalled()) { TabButtons[12].isVisible = false; TabButtons[13].isVisible = false; TabButtons[14].isVisible = false; } // Settings tab. showSettings = UIUtils.CreateButton(Tabs); showSettings.size = new Vector2(80, 25); showSettings.normalBgSprite = "SubBarButtonBase"; showSettings.eventClick += (component, clickEvent) => SettingsPanel.Open(); // Add UI text. SetText(); // Toggle active state on visibility changed (deactivating when hidden to minimise UI workload and impact on performance). BuildingPanel.eventVisibilityChanged += (component, isVisible) => { BuildingPanel.gameObject.SetActive(isVisible); }; } }