/// <summary>
        /// Adds panel footer controls (pack name textfield and buttons).
        /// </summary>
        /// <param name="yPos">Reference Y position</param>
        protected void PanelFooter(float yPos)
        {
            // Additional space before name textfield.
            float currentY = yPos + RowHeight;

            // Pack name textfield.
            PackNameField           = UIControls.BigTextField(panel, 140f, currentY);
            PackNameField.isEnabled = false;
            UILabel packNameLabel = UIControls.AddLabel(PackNameField, -100f, (PackNameField.height - 18f) / 2, Translations.Translate("RPR_OPT_EDT_NAM"));

            // Space for buttons.
            currentY += 50f;

            // 'Add new' button.
            UIButton addNewButton = UIControls.AddButton(panel, 20f, currentY, Translations.Translate("RPR_OPT_NEW"));

            addNewButton.eventClicked += AddPack;

            // Save pack button.
            saveButton = UIControls.AddButton(panel, 250f, currentY, Translations.Translate("RPR_OPT_SAA"));
            saveButton.eventClicked += Save;

            // Delete pack button.
            deleteButton = UIControls.AddButton(panel, 480f, currentY, Translations.Translate("RPR_OPT_DEL"));
            deleteButton.eventClicked += DeletePack;
        }
Example #2
0
        /// <summary>
        /// Harmony Postfix patch to ZonedBuildingWorldInfoPanel.UpdateBindings to display visitor counts for commercial buildings.
        /// </summary>
        public static void Postfix()
        {
            // Currently selected building.
            ushort building = WorldInfoPanel.GetCurrentInstanceID().Building;

            // Create visit label if it isn't already set up.
            if (visitLabel == null)
            {
                // Get info panel.
                ZonedBuildingWorldInfoPanel infoPanel = UIView.library.Get <ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name);

                // Add current visitor count label.
                visitLabel           = UIControls.AddLabel(infoPanel.component, 65f, 280f, Translations.Translate("RPR_INF_VIS"), textScale: 0.75f);
                visitLabel.textColor = new Color32(185, 221, 254, 255);
                visitLabel.font      = Resources.FindObjectsOfTypeAll <UIFont>().FirstOrDefault((UIFont f) => f.name == "OpenSans-Regular");

                // Position under existing Highly Educated workers count row in line with total workplace count label.
                UIComponent situationLabel = infoPanel.Find("WorkSituation");
                UIComponent workerLabel    = infoPanel.Find("HighlyEducatedWorkers");
                if (situationLabel != null && workerLabel != null)
                {
                    visitLabel.absolutePosition = new Vector2(situationLabel.absolutePosition.x, workerLabel.absolutePosition.y + 25f);
                }
                else
                {
                    Logging.Error("couldn't find ZonedBuildingWorldInfoPanel components");
                }
            }

            // Local references.
            Building[]   buildingBuffer = Singleton <BuildingManager> .instance.m_buildings.m_buffer;
            BuildingInfo buildingInfo   = buildingBuffer[building].Info;

            // Is this a commercial building?
            CommercialBuildingAI commercialAI = buildingInfo.GetAI() as CommercialBuildingAI;

            if (commercialAI == null)
            {
                // Not a commercial building - hide the label.
                visitLabel.Hide();
            }
            else
            {
                // Commercial building - show the label.
                visitLabel.Show();

                // Get current visitor count.
                int aliveCount = 0, totalCount = 0;
                Citizen.BehaviourData behaviour = new Citizen.BehaviourData();
                GetVisitBehaviour(commercialAI, building, ref buildingBuffer[building], ref behaviour, ref aliveCount, ref totalCount);

                // Display visitor count.
                visitLabel.text = totalCount.ToString() + " / " + commercialAI.CalculateVisitplaceCount((ItemClass.Level)buildingBuffer[building].m_level, new ColossalFramework.Math.Randomizer(building), buildingBuffer[building].Width, buildingBuffer[building].Length).ToString() + " " + Translations.Translate("RPR_INF_VIS");
            }
        }
        /// <summary>
        /// Adds header controls to the panel.
        /// </summary>
        /// <param name="yPos">Relative Y position for buttons</param>
        /// <returns>Relative Y coordinate below the finished setup</returns>
        protected override float PanelHeader(float yPos)
        {
            // Y position reference.
            float currentY = yPos + Margin;

            // Add 'Use legacy by default' header.

            // Label.
            UILabel legacyLabel = UIControls.AddLabel(panel, Margin, currentY, Translations.Translate(LegacyCheckLabel), panel.width - Margin, textScale: 0.9f);

            currentY += legacyLabel.height + 5f;

            // Use legacy by default for this save check.
            UICheckBox legacyThisSaveCheck = UIControls.LabelledCheckBox(panel, Margin * 2, currentY, Translations.Translate("RPR_DEF_LTS"));

            legacyThisSaveCheck.label.wordWrap     = true;
            legacyThisSaveCheck.label.autoSize     = false;
            legacyThisSaveCheck.label.width        = 710f;
            legacyThisSaveCheck.label.autoHeight   = true;
            legacyThisSaveCheck.isChecked          = ThisLegacyCategory;
            legacyThisSaveCheck.eventCheckChanged += (control, isChecked) =>
            {
                ThisLegacyCategory = isChecked;
                UpdateControls();
                SettingsUtils.SaveSettings();
            };

            // Use legacy by default for new saves check.
            currentY += 20f;
            UICheckBox legacyNewSaveCheck = UIControls.LabelledCheckBox(panel, Margin * 2, currentY, Translations.Translate("RPR_DEF_LAS"));

            legacyNewSaveCheck.label.wordWrap     = true;
            legacyNewSaveCheck.label.autoSize     = false;
            legacyNewSaveCheck.label.width        = 710f;
            legacyNewSaveCheck.label.autoHeight   = true;
            legacyNewSaveCheck.isChecked          = NewLegacyCategory;
            legacyNewSaveCheck.eventCheckChanged += (control, isChecked) =>
            {
                NewLegacyCategory = isChecked;
                UpdateControls();
                SettingsUtils.SaveSettings();
            };

            // Spacer bar.
            currentY += 25f;
            UIControls.OptionsSpacer(panel, Margin, currentY, panel.width - (Margin * 2f));

            return(currentY + 10f);
        }
        /// <summary>
        /// Adds a title label across the top of the specified UIComponent.
        /// </summary>
        /// <param name="parent">Parent component</param>
        /// <param name="titleKey">Title translation key</param>
        /// <returns>Y position below title</returns>
        internal static float TitleLabel(UIComponent parent, string titleKey)
        {
            // Margin.
            const float Margin = 5f;

            // Add title.
            UILabel titleLabel = UIControls.AddLabel(parent, 0f, Margin, Translations.Translate(titleKey), parent.width, 1.5f);

            titleLabel.textAlignment = UIHorizontalAlignment.Center;
            titleLabel.font          = Resources.FindObjectsOfTypeAll <UIFont>().FirstOrDefault((UIFont f) => f.name == "OpenSans-Semibold");

            UIControls.OptionsSpacer(parent, Margin, titleLabel.height + (Margin * 2f), parent.width - (Margin * 2f));

            return(Margin + titleLabel.height + Margin + 5f + Margin);
        }
        /// <summary>
        /// Create the mod calcs 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>
        internal void Setup()
        {
            // Basic setup.
            clipChildren = true;

            // Title.
            title = this.AddUIComponent <UILabel>();
            title.relativePosition = new Vector3(0, 0);
            title.textAlignment    = UIHorizontalAlignment.Center;
            title.text             = Translations.Translate("RPR_CAL_MOD");
            title.textScale        = 1.2f;
            title.autoSize         = false;
            title.width            = this.width;

            // Column titles.
            UILabel densityTitle = ColumnLabel(this, Translations.Translate("RPR_CAL_DEN"), Margin, ColumnLabelY);
            UILabel floorTitle   = ColumnLabel(this, Translations.Translate("RPR_CAL_BFL"), RightColumnX, ColumnLabelY);

            // Volumetric calculations panel.
            volumetricPanel = this.AddUIComponent <UIVolumetricPanel>();
            volumetricPanel.relativePosition = new Vector2(0f, BaseCalcY);
            volumetricPanel.height           = this.height - title.height + 80f;
            volumetricPanel.width            = this.width;
            volumetricPanel.Setup();

            // Legacy calculations panel - copy volumetric calculations panel.
            legacyPanel = this.AddUIComponent <UILegacyCalcs>();
            legacyPanel.relativePosition = volumetricPanel.relativePosition;
            legacyPanel.height           = volumetricPanel.height;
            legacyPanel.width            = volumetricPanel.width;
            legacyPanel.Setup();
            legacyPanel.Hide();

            // Floor dropdown panel - set size manually to avoid invisible overlap of calculations panel (preventing e.g. tooltips).
            floorPanel = this.AddUIComponent <UIPanel>();
            floorPanel.relativePosition = new Vector2(RightColumnX, MenuY);
            floorPanel.autoSize         = false;
            floorPanel.width            = RightColumnX - this.width;
            floorPanel.height           = BaseCalcY - MenuY;
            floorPanel.autoLayout       = false;
            floorPanel.clipChildren     = false;
            floorPanel.Show();

            // Floor override label (for when floor dropdown menu is hidden).
            floorOverrideLabel = UIControls.AddLabel(this, RightColumnX, MenuY, Translations.Translate("RPR_CAL_FOV"), this.width - RightColumnX, 0.7f);
            floorOverrideLabel.Hide();

            // Pack dropdowns.
            popMenu   = UIControls.AddDropDown(this, Margin, MenuY, ComponentWidth);
            floorMenu = UIControls.AddDropDown(floorPanel, 0f, 0f, ComponentWidth);

            // School dropdown panel.
            schoolPanel = this.AddUIComponent <UIPanel>();
            schoolPanel.relativePosition = new Vector2(Margin, Row2LabelY);
            schoolPanel.autoSize         = false;
            schoolPanel.autoLayout       = false;
            schoolPanel.clipChildren     = false;
            schoolPanel.height           = ApplyX - Row2LabelY;
            schoolPanel.width            = this.width - (Margin * 2);

            // School panel title and dropdown menu.
            UILabel schoolTitle = ColumnLabel(schoolPanel, Translations.Translate("RPR_CAL_SCH_PRO"), 0, 0);

            schoolMenu = UIControls.AddDropDown(schoolPanel, 0f, LabelHeight, ComponentWidth);
            schoolPanel.Hide();

            // Pack descriptions.
            popDescription    = Description(this, Margin, DescriptionY);
            floorDescription  = Description(floorPanel, 0f, DescriptionY - MenuY);
            schoolDescription = Description(schoolPanel, 0f, LabelHeight + DescriptionY - MenuY);

            // Apply button.
            applyButton = UIControls.AddButton(this, ApplyX, BaseSaveY, Translations.Translate("RPR_OPT_SAA"), ButtonWidth);
            applyButton.eventClicked += (control, clickEvent) => ApplySettings();

            // Dropdown event handlers.
            popMenu.eventSelectedIndexChanged    += (component, index) => UpdatePopSelection(index);
            floorMenu.eventSelectedIndexChanged  += (component, index) => UpdateFloorSelection(index);
            schoolMenu.eventSelectedIndexChanged += (component, index) => UpdateSchoolSelection(index);

            // Add school multiplier slider (starts hidden).
            multSlider = AddSliderWithMultipler(schoolPanel, string.Empty, 1f, 5f, 0.5f, ModSettings.DefaultSchoolMult, (value) => UpdateMultiplier(value), ComponentWidth);
            multSlider.parent.relativePosition = new Vector2(RightColumnX, 10f);
            multSlider.parent.Hide();

            // Muliplier checkbox.
            multCheck = UIControls.LabelledCheckBox(schoolPanel, RightColumnX, 18f, Translations.Translate("RPR_CAL_CAP_OVR"));

            // Multiplier default label.
            multDefaultLabel = UIControls.AddLabel(schoolPanel, RightColumnX + 21f, 40f, Translations.Translate("RPR_CAL_CAP_DEF") + " x" + ModSettings.DefaultSchoolMult, textScale: 0.8f);

            // Multplier checkbox event handler.
            multCheck.eventCheckChanged += (control, isChecked) => MultiplierCheckChanged(isChecked);
        }
        /// <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>
        internal void Setup()
        {
            // Generic setup.
            isVisible            = true;
            canFocus             = true;
            isInteractive        = true;
            backgroundSprite     = "UnlockingPanel";
            autoLayout           = false;
            autoSize             = false;
            width                = parent.width;
            height               = 395f;
            builtinKeyNavigation = true;
            clipChildren         = true;

            // Labels.
            floorHeightLabel   = AddVolumetricLabel(this, "RPR_CAL_VOL_FLH", RightColumn, Row1, "RPR_CAL_VOL_FLH_TIP");
            firstMinLabel      = AddVolumetricLabel(this, "RPR_CAL_VOL_FMN", RightColumn, Row2, "RPR_CAL_VOL_FMN_TIP");
            firstExtraLabel    = AddVolumetricLabel(this, "RPR_CAL_VOL_FMX", RightColumn, Row3, "RPR_CAL_VOL_FMX_TIP");
            numFloorsLabel     = AddVolumetricLabel(this, "RPR_CAL_VOL_FLR", RightColumn, Row5, "RPR_CAL_VOL_FLR_TIP");
            floorAreaLabel     = AddVolumetricLabel(this, "RPR_CAL_VOL_TFA", RightColumn, Row6, "RPR_CAL_VOL_TFA_TIP");
            totalHomesLabel    = AddVolumetricLabel(this, "RPR_CAL_VOL_HOU", RightColumn, Row7, "RPR_CAL_VOL_UTS_TIP");
            totalJobsLabel     = AddVolumetricLabel(this, "RPR_CAL_VOL_WOR", RightColumn, Row7, "RPR_CAL_VOL_UTS_TIP");
            totalStudentsLabel = AddVolumetricLabel(this, "RPR_CAL_VOL_STU", RightColumn, Row7, "RPR_CAL_VOL_UTS_TIP");
            visitCountLabel    = AddVolumetricLabel(this, "RPR_CAL_VOL_VIS", RightColumn, Row8, "RPR_CAL_VOL_VIS_TIP");
            productionLabel    = AddVolumetricLabel(this, "RPR_CAL_VOL_PRD", RightColumn, Row8, "RPR_CAL_VOL_PRD_TIP");
            unitsLabel         = AddVolumetricLabel(this, "RPR_CAL_VOL_UNI", LeftColumn, Row2, "RPR_CAL_VOL_UNI_TIP");
            emptyPercentLabel  = AddVolumetricLabel(this, "RPR_CAL_VOL_EPC", LeftColumn, Row2, "RPR_CAL_VOL_EPC_TIP");
            emptyAreaLabel     = AddVolumetricLabel(this, "RPR_CAL_VOL_EMP", LeftColumn, Row3, "RPR_CAL_VOL_EMP_TIP");
            perLabel           = AddVolumetricLabel(this, "RPR_CAL_VOL_APU", LeftColumn, Row4, "RPR_CAL_VOL_APU_TIP");
            schoolWorkerLabel  = AddVolumetricLabel(this, "RPR_CAL_SCH_WKR", LeftColumn, Row7, "RPR_CAL_SCH_WKR_TIP");
            costLabel          = AddVolumetricLabel(this, "RPR_CAL_SCH_CST", LeftColumn, Row8, "RPR_CAL_SCH_CST_TIP");

            // Intially hidden (just to avoid ugliness if no building is selected).
            totalHomesLabel.Hide();
            totalStudentsLabel.Hide();

            // Fixed population checkbox.
            fixedPopCheckBox = CalcCheckBox(this, "RPR_CAL_VOL_FXP", LeftColumn, Row1, "RPR_CAL_VOL_FXP_TIP");
            fixedPopCheckBox.isInteractive = false;
            fixedPopCheckBox.Disable();

            // Multi-floor units checkbox.
            multiFloorCheckBox = CalcCheckBox(this, "RPR_CAL_VOL_MFU", LeftColumn, Row5, "RPR_CAL_VOL_MFU_TIP");
            multiFloorCheckBox.isInteractive = false;
            multiFloorCheckBox.Disable();

            // Ignore first floor checkbox.
            ignoreFirstCheckBox = CalcCheckBox(this, "RPR_CAL_VOL_IGF", RightColumn, Row4, "RPR_CAL_VOL_IGF_TIP");
            ignoreFirstCheckBox.isInteractive = false;
            ignoreFirstCheckBox.Disable();

            // Message label.
            messageLabel = UIControls.AddLabel(this, Margin, MessageY, string.Empty);

            // Floor list - attached to root panel as scrolling and interactivity can be unreliable otherwise.
            floorsList = UIFastList.Create <UIFloorRow>(BuildingDetailsPanel.Panel);

            // Size, appearance and behaviour.
            floorsList.backgroundSprite  = "UnlockingPanel";
            floorsList.width             = this.width;
            floorsList.isInteractive     = true;
            floorsList.canSelect         = false;
            floorsList.rowHeight         = 20;
            floorsList.autoHideScrollbar = true;;
            ResetFloorListPosition();

            // Data.
            floorsList.rowsData      = new FastList <object>();
            floorsList.selectedIndex = -1;

            // Toggle floorsList visibility on this panel's visibility change (because floorsList is attached to root panel).
            this.eventVisibilityChanged += (control, isVisible) => floorsList.isVisible = isVisible;
        }