Ejemplo n.º 1
0
        /// <summary>
        /// Adds a sub-service field group to the panel.
        /// </summary>
        /// <param name="panel">UI panel instance</param>
        /// <param name="subService">Subservice reference number</param>
        /// <param name="isExtract">Set this to true (and label to null) to add extractor/processor labels (default false, which is plain level labels)</param>
        /// <param name="label">Text label base for each row; null (default) to use level numbers or extractor/prcessor</param>
        protected void AddSubService(UIPanel panel, bool addLevels, int subService, bool isExtract = false, string label = null)
        {
            // Add a row for each level within this subservice.
            for (int i = 0; i < areaFields[subService].Length; i++)
            {
                // Row label.
                RowLabel(panel, currentY, label ?? (isExtract ? Translations.Translate(i == 0 ? "RPR_CAT_EXT" : "RPR_CAT_PRO") : Translations.Translate("RPR_OPT_LVL") + " " + (i + 1).ToString()));

                // Textfields.
                areaFields[subService][i]    = AddTextField(panel, Column1Width, Column1, currentY, areaLabel);
                floorFields[subService][i]   = AddTextField(panel, ColumnWidth, Column2, currentY, floorLabel);
                powerFields[subService][i]   = AddTextField(panel, ColumnWidth, Column4, currentY, powerLabel);
                waterFields[subService][i]   = AddTextField(panel, ColumnWidth, Column5, currentY, waterLabel);
                sewageFields[subService][i]  = AddTextField(panel, ColumnWidth, Column6, currentY, sewageLabel);
                garbageFields[subService][i] = AddTextField(panel, ColumnWidth, Column7, currentY, garbageLabel);
                incomeFields[subService][i]  = AddTextField(panel, Column8Width, Column8, currentY, wealthLabel);

                // Bonus levels.
                if (notResidential)
                {
                    extraFloorFields[subService][i] = AddTextField(panel, ColumnWidth, Column3, currentY, extraFloorLabel);
                }

                // Increment Y position.
                currentY += RowHeight;
            }

            // Add an extra bit of space at the end.
            currentY += Margin;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adds control bouttons the the panel.
        /// </summary>
        /// <param name="panel">UI panel instance</param>
        protected void AddButtons(UIPanel panel)
        {
            // Add extra space.
            currentY += RowHeight;

            // Reset button.
            UIButton resetButton = UIUtils.CreateButton(panel, 150);

            resetButton.text             = Translations.Translate("RPR_OPT_RTD");
            resetButton.relativePosition = new Vector3(Margin, currentY);
            resetButton.eventClicked    += (component, clickEvent) => ResetToDefaults();

            UIButton revertToSaveButton = UIUtils.CreateButton(panel, 150);

            revertToSaveButton.text             = Translations.Translate("RPR_OPT_RTS");
            revertToSaveButton.relativePosition = new Vector3((Margin * 2) + 150, currentY);

            revertToSaveButton.eventClicked += (component, clickEvent) => { XMLUtilsWG.ReadFromXML(); PopulateFields(); };

            UIButton saveButton = UIUtils.CreateButton(panel, 150);

            saveButton.text             = Translations.Translate("RPR_OPT_SAA");
            saveButton.relativePosition = new Vector3((Margin * 3) + 300, currentY);
            saveButton.eventClicked    += (component, clickEvent) => ApplyFields();
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Called whenever the currently selected building is changed to update the panel display.
        /// </summary>
        /// <param name="building"></param>
        public void SelectionChanged(BuildingInfo building)
        {
            // Hide message.
            messageLabel.isVisible = false;

            // Set current selecion.
            currentSelection = building;

            // Set text field to blank and disable buttons if no valid building is selected.
            if (building == null || building.name == null)
            {
                homeJobsCount.text = string.Empty;
                saveButton.Disable();
                deleteButton.Disable();
                return;
            }

            int homesJobs;

            if (building.GetService() == ItemClass.Service.Residential)
            {
                // See if a custom number of households applies to this building.
                homesJobs         = ExternalCalls.GetResidential(building);
                homeJobLabel.text = Translations.Translate("RPR_LBL_HOM");
            }
            else
            {
                // Workplace building; see if a custom number of jobs applies to this building.
                homesJobs         = ExternalCalls.GetWorker(building);
                homeJobLabel.text = Translations.Translate("RPR_LBL_JOB");
            }

            // If no custom settings have been found (return value was zero), then blank the text field, rename the save button, and disable the delete button.
            if (homesJobs == 0)
            {
                homeJobsCount.text = string.Empty;
                saveButton.text    = Translations.Translate("RPR_CUS_ADD");
                deleteButton.Disable();
            }
            else
            {
                // Valid custom settings found; display the result, rename the save button, and enable the delete button.
                homeJobsCount.text = homesJobs.ToString();
                saveButton.text    = Translations.Translate("RPR_CUS_UPD");
                deleteButton.Enable();
            }

            // We've got a valid building, so enable the save button.
            saveButton.Enable();
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Adds commercial options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        public CommercialPanel(UITabstrip tabStrip, int tabIndex)
        {
            // Add tab.
            UIPanel panel = PanelUtils.AddTab(tabStrip, Translations.Translate("RPR_CAT_COM"), tabIndex);

            // Initialise textfield array.
            SetupArrays(NumSubServices);

            for (int i = 0; i < NumSubServices; i++)
            {
                int levels = i < 2 ? NumLevels : 1;

                areaFields[i]       = new UITextField[levels];
                floorFields[i]      = new UITextField[levels];
                extraFloorFields[i] = new UITextField[levels];
                powerFields[i]      = new UITextField[levels];
                waterFields[i]      = new UITextField[levels];
                sewageFields[i]     = new UITextField[levels];
                garbageFields[i]    = new UITextField[levels];
                incomeFields[i]     = new UITextField[levels];
            }

            // Headings.
            AddHeadings(panel);

            // Create residential per-person area textfields and labels.
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[LowCom]), "ZoningCommercialLow", "Thumbnails");
            AddSubService(panel, true, LowCom);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[HighCom]), "ZoningCommercialHigh", "Thumbnails");
            AddSubService(panel, true, HighCom);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[EcoCom]), "IconPolicyOrganic", "Ingame");
            AddSubService(panel, false, EcoCom, label: Translations.Translate("RPR_CAT_ECO"));
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Leisure]), "IconPolicyLeisure", "Ingame");
            AddSubService(panel, false, Leisure, label: Translations.Translate(subServiceLables[Leisure]));
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Tourist]), "IconPolicyTourist", "Ingame");
            AddSubService(panel, false, Tourist, label: Translations.Translate(subServiceLables[Tourist]));

            // Populate initial values.
            PopulateFields();

            // Add command buttons.
            AddButtons(panel);
        }
        /// <summary>
        /// Adds industrial options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        public IndustrialPanel(UITabstrip tabStrip, int tabIndex)
        {
            // Add tab.
            UIPanel panel = PanelUtils.AddTab(tabStrip, Translations.Translate("RPR_CAT_IND"), tabIndex);

            // Initialise textfield array.
            SetupArrays(NumSubServices);

            for (int i = 0; i < NumSubServices; i++)
            {
                int levels = i == 0 ? NumLevels : 2;

                areaFields[i]       = new UITextField[levels];
                floorFields[i]      = new UITextField[levels];
                extraFloorFields[i] = new UITextField[levels];
                powerFields[i]      = new UITextField[levels];
                waterFields[i]      = new UITextField[levels];
                sewageFields[i]     = new UITextField[levels];
                garbageFields[i]    = new UITextField[levels];
                incomeFields[i]     = new UITextField[levels];
            }

            // Headings.
            AddHeadings(panel);

            // Create residential per-person area textfields and labels.
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Generic]), "ZoningIndustrial", "Thumbnails");
            AddSubService(panel, true, Generic);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Farming]), "IconPolicyFarming", "Ingame");
            AddSubService(panel, false, Farming, true);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Forestry]), "IconPolicyForest", "Ingame");
            AddSubService(panel, false, Forestry, true);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Oil]), "IconPolicyOil", "Ingame");
            AddSubService(panel, false, Oil, true);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Ore]), "IconPolicyOre", "Ingame");
            AddSubService(panel, false, Ore, true);

            // Populate initial values.
            PopulateFields();

            // Add command buttons.
            AddButtons(panel);
        }
        /// <summary>
        /// Adds mod options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        internal ModOptionsPanel(UITabstrip tabStrip, int tabIndex)
        {
            // Add tab and helper.
            UIPanel  panel  = PanelUtils.AddTab(tabStrip, Translations.Translate("RPR_OPT_MOD"), tabIndex);
            UIHelper helper = new UIHelper(panel);

            panel.autoLayout = true;

            // Language dropdown.
            UIDropDown languageDrop = PanelUtils.AddPlainDropDown(panel, Translations.Translate("TRN_CHOICE"), Translations.LanguageList, Translations.Index);

            languageDrop.eventSelectedIndexChanged += (control, index) =>
            {
                Translations.Index = index;
                SettingsUtils.SaveSettings();
            };

            // Hotkey control.
            panel.gameObject.AddComponent <OptionsKeymapping>();
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Adds residential options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        public ResidentialPanel(UITabstrip tabStrip, int tabIndex)
        {
            // Add tab.
            UIPanel panel = PanelUtils.AddTab(tabStrip, Translations.Translate("RPR_CAT_RES"), tabIndex);

            // Set residential flag.
            notResidential = false;

            // Initialise textfield array.
            SetupArrays(NumSubServices);

            for (int i = 0; i < NumSubServices; i++)
            {
                areaFields[i]    = new UITextField[NumLevels];
                floorFields[i]   = new UITextField[NumLevels];
                powerFields[i]   = new UITextField[NumLevels];
                waterFields[i]   = new UITextField[NumLevels];
                sewageFields[i]  = new UITextField[NumLevels];
                garbageFields[i] = new UITextField[NumLevels];
                incomeFields[i]  = new UITextField[NumLevels];
            }

            // Headings.
            AddHeadings(panel);

            // Create residential per-person area textfields and labels.
            RowHeaderIcon(panel, currentY, Translations.Translate("RPR_CAT_RLO"), "ZoningResidentialLow", "Thumbnails");
            AddSubService(panel, true, LowRes);
            RowHeaderIcon(panel, currentY, Translations.Translate("RPR_CAT_RHI"), "ZoningResidentialHigh", "Thumbnails");
            AddSubService(panel, true, HighRes);
            RowHeaderIcon(panel, currentY, Translations.Translate("RPR_CAT_ERL"), "IconPolicySelfsufficient", "Ingame");
            AddSubService(panel, true, LowEcoRes);
            RowHeaderIcon(panel, currentY, Translations.Translate("RPR_CAT_ERH"), "IconPolicySelfsufficient", "Ingame");
            AddSubService(panel, true, HighEcoRes);

            // Populate initial values.
            PopulateFields();

            // Add command buttons.
            AddButtons(panel);
        }
        /// <summary>
        /// Generates and displays a building row.
        /// </summary>
        /// <param name="data">Object to list</param>
        /// <param name="isRowOdd">If the row is an odd-numbered row (for background banding)</param>
        public void Display(object data, bool isRowOdd)
        {
            // Perform initial setup for new rows.
            if (buildingName == null)
            {
                isVisible     = true;
                canFocus      = true;
                isInteractive = true;
                width         = parent.width;
                height        = 40;

                buildingName       = AddUIComponent <UILabel>();
                buildingName.width = 200;

                // Checkbox to indicate which items have custom settings.
                hasCustom                  = AddUIComponent <UISprite>();
                hasCustom.size             = new Vector2(20, 20);
                hasCustom.relativePosition = new Vector3(340, 10);
                hasCustom.tooltip          = Translations.Translate("RPR_CUS_HAS");
            }

            // Set selected building.
            thisBuilding      = data as BuildingInfo;
            buildingName.text = UIBuildingDetails.GetDisplayName(thisBuilding.name);

            // Update custom settings checkbox to correct state.
            if (ExternalCalls.GetResidential(thisBuilding) > 0 || ExternalCalls.GetWorker(thisBuilding) > 0)
            {
                // Custom value found.
                hasCustom.spriteName = "AchievementCheckedTrue";
            }
            else
            {
                // No custom value.
                hasCustom.spriteName = "AchievementCheckedFalse";
            }

            // Set initial background as deselected state.
            Deselect(isRowOdd);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Adds column headings.
        /// </summary>
        /// <param name="panel">UI panel instance</param>
        protected void AddHeadings(UIPanel panel)
        {
            // Set string references (we'll reference these multiple times with the textfields, so this saves calling translate each time).
            areaLabel       = Translations.Translate(notResidential ? "RPR_OPT_APW" : "RPR_OPT_APH");
            floorLabel      = Translations.Translate("RPR_OPT_FLR");
            extraFloorLabel = Translations.Translate("RPR_CAL_FLR_M");
            powerLabel      = Translations.Translate("RPR_OPT_POW");
            waterLabel      = Translations.Translate("RPR_OPT_WAT");
            sewageLabel     = Translations.Translate("RPR_OPT_SEW");
            garbageLabel    = Translations.Translate("RPR_OPT_GAR");
            wealthLabel     = Translations.Translate("RPR_OPT_WEA");

            // Headings.
            ColumnLabel(panel, Column1, Column1Width, areaLabel, 1.0f);
            ColumnLabel(panel, Column2, ColumnWidth, floorLabel, 1.0f);
            ColumnIcon(panel, Column4, ColumnWidth, powerLabel, "ToolbarIconElectricity");
            ColumnIcon(panel, Column5, ColumnWidth, waterLabel, "ToolbarIconWaterAndSewage");
            ColumnIcon(panel, Column6, ColumnWidth, sewageLabel, "ToolbarIconWaterAndSewageDisabled");
            ColumnIcon(panel, Column7, ColumnWidth, garbageLabel, "InfoIconGarbage");
            ColumnIcon(panel, Column8, Column8Width, wealthLabel, "ToolbarIconMoney");

            // Bonus floors.
            if (notResidential)
            {
                ColumnLabel(panel, Column3, ColumnWidth, extraFloorLabel, 0.8f);
            }

            // Consumption heading
            UILabel headingLabel = panel.AddUIComponent <UILabel>();

            headingLabel.autoSize          = false;
            headingLabel.autoHeight        = true;
            headingLabel.wordWrap          = true;
            headingLabel.relativePosition  = new Vector3(Column4, 0);
            headingLabel.verticalAlignment = UIVerticalAlignment.Middle;
            headingLabel.textAlignment     = UIHorizontalAlignment.Center;
            headingLabel.width             = Column8 + Column8Width - Column4;
            headingLabel.text = Translations.Translate(notResidential ? "RPR_OPT_PERW" : "RPR_OPT_PERH");
        }
        /// <summary>
        /// Adds button to access building details from building info panels.
        /// </summary>
        internal static void AddInfoPanelButton()
        {
            // Get parent panel and apply button.
            ZonedBuildingWorldInfoPanel infoPanel = UIView.library.Get <ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name);
            UIButton panelButton = UIUtils.CreateButton(infoPanel.component, 133);

            // Basic setup.
            panelButton.height                = 19.5f;
            panelButton.textScale             = 0.65f;
            panelButton.textVerticalAlignment = UIVerticalAlignment.Bottom;
            panelButton.relativePosition      = new UnityEngine.Vector3(infoPanel.component.width - panelButton.width - 10, 120);
            panelButton.text = Translations.Translate("RPR_REALPOP");

            // Just in case other mods are interfering.
            panelButton.Enable();

            // Event handler.
            panelButton.eventClick += (control, clickEvent) =>
            {
                // Select current building in the building details panel and show.
                BuildingDetailsPanel.Open(InstanceManager.GetPrefabInfo(WorldInfoPanel.GetCurrentInstanceID()) as BuildingInfo);
            };
        }
        /// <summary>
        /// Adds commercial options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        public OfficePanel(UITabstrip tabStrip, int tabIndex)
        {
            // Add tab.
            UIPanel panel = PanelUtils.AddTab(tabStrip, Translations.Translate("RPR_CAT_OFF"), tabIndex);

            // Initialise textfield array.
            SetupArrays(NumSubServices);

            for (int i = 0; i < NumSubServices; i++)
            {
                int levels = i == 0 ? NumLevels : 1;

                areaFields[i]       = new UITextField[levels];
                floorFields[i]      = new UITextField[levels];
                extraFloorFields[i] = new UITextField[levels];
                powerFields[i]      = new UITextField[levels];
                waterFields[i]      = new UITextField[levels];
                sewageFields[i]     = new UITextField[levels];
                garbageFields[i]    = new UITextField[levels];
                incomeFields[i]     = new UITextField[levels];
            }

            // Headings.
            AddHeadings(panel);

            // Create residential per-person area textfields and labels.
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[Office]), "ZoningOffice", "Thumbnails");
            AddSubService(panel, true, Office);
            RowHeaderIcon(panel, currentY, Translations.Translate(subServiceLables[HighTech]), "IconPolicyHightech", "Ingame");
            AddSubService(panel, false, HighTech, label: Translations.Translate(subServiceLables[HighTech]));

            // Populate initial values.
            PopulateFields();

            // Add command buttons.
            AddButtons(panel);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Create the panel; 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()
        {
            const int marginPadding = 10;

            // Generic setup.
            isVisible               = true;
            canFocus                = true;
            isInteractive           = true;
            backgroundSprite        = "UnlockingPanel";
            autoLayout              = false;
            autoLayoutDirection     = LayoutDirection.Vertical;
            autoLayoutPadding.top   = 5;
            autoLayoutPadding.right = 5;
            builtinKeyNavigation    = true;
            clipChildren            = true;

            // Panel title.
            UILabel title = this.AddUIComponent <UILabel>();

            title.relativePosition = new Vector3(0, 5);
            title.textAlignment    = UIHorizontalAlignment.Center;
            title.text             = Translations.Translate("RPR_CUS_TITLE");
            title.textScale        = 1.2f;
            title.autoSize         = false;
            title.width            = this.width;
            title.height           = 30;

            // Text field label.
            homeJobLabel = this.AddUIComponent <UILabel>();
            homeJobLabel.relativePosition = new Vector3(marginPadding, 40);
            homeJobLabel.textAlignment    = UIHorizontalAlignment.Left;
            homeJobLabel.text             = Translations.Translate("RPR_LBL_HOM");

            // Home or jobs count text field.
            homeJobsCount = UIUtils.CreateTextField(this, this.width - (marginPadding * 3) - homeJobLabel.width, 20);
            homeJobsCount.relativePosition = new Vector3(marginPadding + homeJobLabel.width + marginPadding, 40);

            // Save button.
            saveButton = UIUtils.CreateButton(this, 200);
            saveButton.relativePosition = new Vector3(marginPadding, 70);
            saveButton.text             = Translations.Translate("RPR_CUS_ADD");
            saveButton.tooltip          = Translations.Translate("RPR_CUS_ADD_TIP");
            saveButton.Disable();

            // Delete button.
            deleteButton = UIUtils.CreateButton(this, 200);
            deleteButton.relativePosition = new Vector3(marginPadding, 110);
            deleteButton.text             = Translations.Translate("RPR_CUS_DEL");
            deleteButton.tooltip          = Translations.Translate("RPR_CUS_DEL_TIP");
            deleteButton.Disable();

            // Save button event handler.
            saveButton.eventClick += (component, clickEvent) =>
            {
                // Hide message.
                messageLabel.isVisible = false;

                // Don't do anything with invalid entries.
                if (currentSelection == null || currentSelection.name == null)
                {
                    return;
                }

                // Read textfield if possible.
                if (int.TryParse(homeJobsCount.text, out int homesJobs))
                {
                    // Minimum value of 1.
                    if (homesJobs < 1)
                    {
                        // Print warning message in red.
                        messageLabel.textColor = new Color32(255, 0, 0, 255);
                        messageLabel.text      = Translations.Translate("RPR_ERR_ZERO");
                        messageLabel.isVisible = true;
                    }
                    else
                    {
                        // Homes or jobs?
                        if (currentSelection.GetService() == ItemClass.Service.Residential)
                        {
                            Debugging.Message("adding custom household count of " + homesJobs + " for " + currentSelection.name);

                            // Residential building.
                            ExternalCalls.SetResidential(currentSelection, homesJobs);

                            // Update household counts for existing instances of this building - only needed for residential buildings.
                            // Workplace counts will update automatically with next call to CalculateWorkplaceCount; households require more work (tied to CitizenUnits).
                            UpdateHouseholds(currentSelection.name);
                        }
                        else
                        {
                            Debugging.Message("adding custom workplace count of " + homesJobs + " for " + currentSelection.name);

                            // Employment building.
                            ExternalCalls.SetWorker(currentSelection, homesJobs);
                        }

                        // Refresh the display so that all panels reflect the updated settings.
                        BuildingDetailsPanel.Panel.UpdateSelectedBuilding(currentSelection);
                        BuildingDetailsPanel.Panel.Refresh();
                    }
                }
                else
                {
                    // TryParse couldn't parse the data; print warning message in red.
                    messageLabel.textColor = new Color32(255, 0, 0, 255);
                    messageLabel.text      = Translations.Translate("RPR_ERR_INV");
                    messageLabel.isVisible = true;
                }
            };

            // Delete button event handler.
            deleteButton.eventClick += (component, clickEvent) =>
            {
                // Hide message.
                messageLabel.isVisible = false;

                // Don't do anything with invalid entries.
                if (currentSelection == null || currentSelection.name == null)
                {
                    return;
                }

                Debugging.Message("deleting custom entry for " + currentSelection.name);

                // Homes or jobs?  Remove custom entry as appropriate.
                if (currentSelection.GetService() == ItemClass.Service.Residential)
                {
                    // Residential building.
                    ExternalCalls.RemoveResidential(currentSelection);

                    // Update household counts for existing instances of this building - only needed for residential buildings.
                    // Workplace counts will update automatically with next call to CalculateWorkplaceCount; households require more work (tied to CitizenUnits).
                    UpdateHouseholds(currentSelection.name);
                }
                else
                {
                    // Employment building.
                    ExternalCalls.RemoveWorker(currentSelection);
                }

                // Refresh the display so that all panels reflect the updated settings.
                BuildingDetailsPanel.Panel.Refresh();
                homeJobsCount.text = string.Empty;
            };

            // Message label (initially hidden).
            messageLabel = this.AddUIComponent <UILabel>();
            messageLabel.relativePosition = new Vector3(marginPadding, 160);
            messageLabel.textAlignment    = UIHorizontalAlignment.Left;
            messageLabel.autoSize         = false;
            messageLabel.autoHeight       = true;
            messageLabel.wordWrap         = true;
            messageLabel.width            = this.width - (marginPadding * 2);
            messageLabel.isVisible        = false;
            messageLabel.text             = "No message to display";
        }
        /// <summary>
        /// Set up filter bar.
        /// We don't use Start() here as we need to access the category toggle states to set up the initial filtering list before Start() is called by UnityEngine.
        /// </summary>
        public void Setup()
        {
            // Catgegory buttons.
            categoryToggles = new UICheckBox[(int)BuildingCategories.numCategories];

            for (int i = 0; i < (int)BuildingCategories.numCategories; i++)
            {
                // Basic setup.
                categoryToggles[i]                  = UIUtils.CreateIconToggle(this, CategoryIcons.atlases[i], CategoryIcons.spriteNames[i], CategoryIcons.spriteNames[i] + "Disabled");
                categoryToggles[i].tooltip          = Translations.Translate(CategoryIcons.tooltips[i]);
                categoryToggles[i].relativePosition = new Vector3(40 * i, 0);
                categoryToggles[i].isChecked        = true;
                categoryToggles[i].readOnly         = true;

                // Single click event handler - toggle state of this button.
                categoryToggles[i].eventClick += (c, p) =>
                {
                    // If either shift or control is NOT held down, deselect all other toggles.
                    if (!(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift) || Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)))
                    {
                        for (int j = 0; j < (int)BuildingCategories.numCategories; j++)
                        {
                            categoryToggles[j].isChecked = false;
                        }
                    }

                    // Select this toggle.
                    ((UICheckBox)c).isChecked = true;

                    // Trigger an update.
                    eventFilteringChanged(this, 0);
                };
            }

            // 'All categories' button.
            allCategories                  = UIUtils.CreateButton(this, 120);
            allCategories.text             = Translations.Translate("RPR_CAT_ALL");
            allCategories.relativePosition = new Vector3(405, 5);

            // All categories event handler.
            allCategories.eventClick += (c, p) =>
            {
                // Select all category toggles.
                for (int i = 0; i < (int)BuildingCategories.numCategories; i++)
                {
                    categoryToggles[i].isChecked = true;
                }

                // Trigger an update.
                eventFilteringChanged(this, 0);
            };

            // Name filter.
            UILabel nameLabel = AddUIComponent <UILabel>();

            nameLabel.textScale        = 0.8f;
            nameLabel.padding          = new RectOffset(0, 0, 8, 0);
            nameLabel.relativePosition = new Vector3(width - 250, 0);
            nameLabel.text             = Translations.Translate("RPR_FIL_NAME");

            nameFilter = UIUtils.CreateTextField(this, 200f, 30f);
            nameFilter.relativePosition = new Vector3(width - nameFilter.width, 0);

            // Name filter event handling - update on any change.
            nameFilter.eventTextChanged   += (c, s) => eventFilteringChanged(this, 5);
            nameFilter.eventTextSubmitted += (c, s) => eventFilteringChanged(this, 5);
        }
        /// <summary>
        /// Called whenever the currently selected building is changed to update the panel display.
        /// </summary>
        /// <param name="building"></param>
        public void SelectionChanged(BuildingInfo building)
        {
            if ((building == null) || (building.name == null))
            {
                // If no valid building selected, then hide the calculations panel.
                detailsPanel.height    = 0;
                detailsPanel.isVisible = false;
                return;
            }

            // Variables to compare actual counts vs. mod count, to see if there's another mod overriding counts.
            int appliedCount;
            int modCount;

            // Building model size, not plot size.
            Vector3 buildingSize = building.m_size;
            int     floorCount;

            // Array used for calculations depending on building service/subservice (via DataStore).
            int[] array;
            // Default minimum number of homes or jobs is one; different service types will override this.
            int minHomesJobs = 1;
            int customHomeJobs;

            // Check for valid building AI.
            if (!(building.GetAI() is PrivateBuildingAI buildingAI))
            {
                Debugging.Message("invalid building AI type in building details");
                return;
            }

            // Residential vs. workplace AI.
            if (buildingAI is ResidentialBuildingAI)
            {
                // Get appropriate calculation array.
                array = ResidentialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());

                // Set calculated homes label.
                homesJobsCalcLabel.text = Translations.Translate("RPR_CAL_HOM_CALC");

                // Set customised homes label and get value (if any).
                homesJobsCustomLabel.text = Translations.Translate("RPR_CAL_HOM_CUST");
                customHomeJobs            = ExternalCalls.GetResidential(building);

                // Applied homes is what's actually being returned by the CaclulateHomeCount call to this building AI.
                // It differs from calculated homes if there's an override value for that building with this mod, or if another mod is overriding.
                appliedCount = buildingAI.CalculateHomeCount(building.GetClassLevel(), new Randomizer(0), building.GetWidth(), building.GetLength());
                homesJobsActualLabel.text = Translations.Translate("RPR_CAL_HOM_APPL") + appliedCount;
            }
            else
            {
                // Workplace AI.
                // Default minimum number of jobs is 4.
                minHomesJobs = 4;

                // Find the correct array for the relevant building AI.
                switch (building.GetService())
                {
                case ItemClass.Service.Commercial:
                    array = CommercialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    break;

                case ItemClass.Service.Office:
                    array = OfficeBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    break;

                case ItemClass.Service.Industrial:
                    if (buildingAI is IndustrialExtractorAI)
                    {
                        array = IndustrialExtractorAIMod.GetArray(building, (int)building.GetClassLevel());
                    }
                    else
                    {
                        array = IndustrialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    }
                    break;

                default:
                    Debugging.Message("invalid building service in building details");
                    return;
                }

                // Set calculated jobs label.
                homesJobsCalcLabel.text = Translations.Translate("RPR_CAL_JOB_CALC") + " ";

                // Set customised jobs label and get value (if any).
                homesJobsCustomLabel.text = Translations.Translate("RPR_CAL_JOB_CUST") + " ";
                customHomeJobs            = ExternalCalls.GetWorker(building);

                // Applied jobs is what's actually being returned by the CalculateWorkplaceCount call to this building AI.
                // It differs from calculated jobs if there's an override value for that building with this mod, or if another mod is overriding.
                int[] jobs = new int[4];
                buildingAI.CalculateWorkplaceCount(building.GetClassLevel(), new Randomizer(0), building.GetWidth(), building.GetLength(), out jobs[0], out jobs[1], out jobs[2], out jobs[3]);
                appliedCount = jobs[0] + jobs[1] + jobs[2] + jobs[3];
                homesJobsActualLabel.text = Translations.Translate("RPR_CAL_JOB_APPL") + " " + appliedCount;
            }

            // Reproduce CalcBase calculations to get building area.
            int calcWidth  = building.GetWidth();
            int calcLength = building.GetLength();

            floorCount = Mathf.Max(1, Mathf.FloorToInt(buildingSize.y / array[DataStore.LEVEL_HEIGHT]));

            // If CALC_METHOD is zero, then calculations are based on building model size, not plot size.
            if (array[DataStore.CALC_METHOD] == 0)
            {
                // If asset has small x dimension, then use plot width in squares x 6m (75% of standard width) instead.
                if (buildingSize.x <= 1)
                {
                    calcWidth *= 6;
                }
                else
                {
                    calcWidth = (int)buildingSize.x;
                }

                // If asset has small z dimension, then use plot length in squares x 6m (75% of standard length) instead.
                if (buildingSize.z <= 1)
                {
                    calcLength *= 6;
                }
                else
                {
                    calcLength = (int)buildingSize.z;
                }
            }
            else
            {
                // If CALC_METHOD is nonzero, then caluclations are based on plot size, not building size.
                // Plot size is 8 metres per square.
                calcWidth  *= 8;
                calcLength *= 8;
            }

            // Display calculated (and retrieved) details.
            detailLabels[(int)Details.width].text       = Translations.Translate("RPR_CAL_BLD_X") + " " + calcWidth;
            detailLabels[(int)Details.length].text      = Translations.Translate("RPR_CAL_BLD_Z") + " " + calcLength;
            detailLabels[(int)Details.height].text      = Translations.Translate("RPR_CAL_BLD_Y") + " " + (int)buildingSize.y;
            detailLabels[(int)Details.personArea].text  = Translations.Translate("RPR_CAL_BLD_M2") + " " + array[DataStore.PEOPLE];
            detailLabels[(int)Details.floorHeight].text = Translations.Translate("RPR_CAL_FLR_Y") + " " + array[DataStore.LEVEL_HEIGHT];
            detailLabels[(int)Details.floors].text      = Translations.Translate("RPR_CAL_FLR") + " " + floorCount;

            // Area calculation - will need this later.
            int calculatedArea = calcWidth * calcLength;

            detailLabels[(int)Details.area].text = Translations.Translate("RPR_CAL_M2") + " " + calculatedArea;

            // Show or hide extra floor modifier as appropriate (hide for zero or less, otherwise show).
            if (array[DataStore.DENSIFICATION] > 0)
            {
                detailLabels[(int)Details.extraFloors].text      = Translations.Translate("RPR_CAL_FLR_M") + " " + array[DataStore.DENSIFICATION];
                detailLabels[(int)Details.extraFloors].isVisible = true;
            }
            else
            {
                detailLabels[(int)Details.extraFloors].isVisible = false;
            }

            // Set minimum residences for high density.
            if ((building.GetSubService() == ItemClass.SubService.ResidentialHigh) || (building.GetSubService() == ItemClass.SubService.ResidentialHighEco))
            {
                // Minimum of 2, or 90% number of floors, whichever is greater. This helps the 1x1 high density.
                minHomesJobs = Mathf.Max(2, Mathf.CeilToInt(0.9f * floorCount));
            }

            // Perform actual household or workplace calculation.
            modCount = Mathf.Max(minHomesJobs, (calculatedArea * (floorCount + Mathf.Max(0, array[DataStore.DENSIFICATION]))) / array[DataStore.PEOPLE]);
            homesJobsCalcLabel.text += modCount;

            // Set customised homes/jobs label (leave blank if no custom setting retrieved).
            if (customHomeJobs > 0)
            {
                homesJobsCustomLabel.text += customHomeJobs.ToString();

                // Update modCount to reflect the custom figures.
                modCount = customHomeJobs;
            }

            // Check to see if Ploppable RICO Revisited is controlling this building's population.
            if (ModUtils.CheckRICO(building))
            {
                messageLabel.text = Translations.Translate("RPR_CAL_RICO");
                messageLabel.Show();
            }
            else
            {
                // Hide message text by default.
                messageLabel.Hide();
            }

            // We've got a valid building and results, so show panel.
            detailsPanel.height    = 270;
            detailsPanel.isVisible = true;
        }