Beispiel #1
0
        /// <summary>
        /// Adds footer buttons to the panel.
        /// </summary>
        /// <param name="yPos">Relative Y position for buttons</param>
        protected virtual void FooterButtons(float yPos)
        {
            // Reset button.
            UIButton resetButton = UIControls.AddButton(panel, Margin, yPos, Translations.Translate("RPR_OPT_RTD"), 150f);

            resetButton.eventClicked += ResetDefaults;

            // Revert button.
            UIButton revertToSaveButton = UIControls.AddButton(panel, (Margin * 2) + 150f, yPos, Translations.Translate("RPR_OPT_RTS"), 150f);

            revertToSaveButton.eventClicked += ResetSaved;
        }
        /// <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)
        {
            base.OnLevelLoaded(mode);

            // Check to see that Harmony 2 was properly loaded.
            if (!harmonyLoaded)
            {
                // Harmony 2 wasn't loaded; display warning notification and exit.
                ListMessageBox harmonyBox = MessageBoxBase.ShowModal <ListMessageBox>();

                // Key text items.
                harmonyBox.AddParas(Translations.Translate("ERR_HAR0"), Translations.Translate("RPR_ERR_HAR"), Translations.Translate("RPR_ERR_FAT"), Translations.Translate("ERR_HAR1"));

                // List of dot points.
                harmonyBox.AddList(Translations.Translate("ERR_HAR2"), Translations.Translate("ERR_HAR3"));

                // Closing para.
                harmonyBox.AddParas(Translations.Translate("MES_PAGE"));
            }

            // Check to see if a conflicting mod has been detected.
            if (conflictingMod)
            {
                // Mod conflict detected - display warning notification and exit.
                ListMessageBox modConflictBox = MessageBoxBase.ShowModal <ListMessageBox>();

                // Key text items.
                modConflictBox.AddParas(Translations.Translate("ERR_CON0"), Translations.Translate("RPR_ERR_CON0"), Translations.Translate("RPR_ERR_FAT"), Translations.Translate("ERR_CON1"));

                // Add conflicting mod name(s).
                modConflictBox.AddList(ModUtils.conflictingModNames.ToArray());

                // Closing para.
                modConflictBox.AddParas(Translations.Translate("RPR_ERR_CON1"));
            }

            // Don't do anything further if mod hasn't activated for whatever reason (mod conflict, harmony error, something else).
            if (!isModEnabled)
            {
                // Disable keystrokes.
                UIThreading.operating = false;

                return;
            }

            // Show legacy choice message box if this save hasn't been flagged as being from Realistic Population 2.
            if (!ModSettings.isRealPop2Save)
            {
                MessageBoxBase.ShowModal <LegacyChoiceMessageBox>();
            }

            // Record initial (default) school settings and apply ours over the top.
            SchoolData.instance.OnLoad();

            // IF a legacy file exists, flag it for writing.
            if (File.Exists(DataStore.currentFileLocation))
            {
                Logging.KeyMessage("found legacy settings file");
                XMLUtilsWG.writeToLegacy = true;
            }

            // Add button to building info panels.
            BuildingDetailsPanel.AddInfoPanelButton();

            Logging.KeyMessage("loading complete");

            // Display update notification.
            WhatsNew.ShowWhatsNew();

            // Set up options panel event handler.
            OptionsPanel.OptionsEventHook();

            // Check and record CitizenUnits count.
            Logging.KeyMessage("citizen unit count is currently ", ColossalFramework.Singleton <CitizenManager> .instance.m_unitCount.ToString());
        }
Beispiel #3
0
        /// <summary>
        /// Adds a slider.
        /// </summary>
        /// <param name="parent">Parent component</param>
        /// <param name="xPos">Relative X position</param>
        /// <param name="yPos">Relative Y position</param>
        /// <param name="width">Slider width</param>
        /// <param name="tooltipKey">Tooltip translation key</param>
        /// <param name="isPercent">True if this slider should display percentage values, false to display absolute values</param>
        /// <returns>New slider</returns>
        protected UISlider AddSlider(UIComponent parent, float xPos, float yPos, float width, string tooltipKey, bool isPercent = true)
        {
            // Layout constants.
            const float SliderPanelHeight = 20f;
            const float SliderHeight      = 6f;
            const float ValueLabelWidth   = 45f;
            const float OffsetX           = (SliderPanelHeight - SliderHeight) / 2f;

            // Mutiplier slider panel.
            UIPanel sliderPanel = parent.AddUIComponent <UIPanel>();

            sliderPanel.autoSize         = false;
            sliderPanel.autoLayout       = false;
            sliderPanel.size             = new Vector2(width, SliderPanelHeight);
            sliderPanel.relativePosition = new Vector2(xPos, yPos);

            // Mutiplier slider value label.
            UILabel valueLabel = sliderPanel.AddUIComponent <UILabel>();

            valueLabel.name = "ValueLabel";
            valueLabel.verticalAlignment = UIVerticalAlignment.Middle;
            valueLabel.textAlignment     = UIHorizontalAlignment.Center;
            valueLabel.textScale         = 0.7f;
            valueLabel.autoSize          = false;
            valueLabel.color             = new Color32(91, 97, 106, 255);
            valueLabel.size             = new Vector2(ValueLabelWidth, 15f);
            valueLabel.relativePosition = new Vector2(sliderPanel.width - ValueLabelWidth - Margin, (SliderPanelHeight - valueLabel.height) / 2f);

            // Mutiplier slider control.
            UISlider newSlider = sliderPanel.AddUIComponent <UISlider>();

            newSlider.size             = new Vector2(sliderPanel.width - ValueLabelWidth - (Margin * 3), SliderHeight);
            newSlider.relativePosition = new Vector2(0f, OffsetX);

            // Mutiplier slider track.
            UISlicedSprite sliderSprite = newSlider.AddUIComponent <UISlicedSprite>();

            sliderSprite.autoSize         = false;
            sliderSprite.size             = new Vector2(newSlider.width, newSlider.height);
            sliderSprite.relativePosition = new Vector2(0f, 0f);
            sliderSprite.atlas            = TextureUtils.InGameAtlas;
            sliderSprite.spriteName       = "ScrollbarTrack";

            // Mutiplier slider thumb.
            UISlicedSprite sliderThumb = newSlider.AddUIComponent <UISlicedSprite>();

            sliderThumb.atlas            = TextureUtils.InGameAtlas;
            sliderThumb.spriteName       = "ScrollbarThumb";
            sliderThumb.height           = 20f;
            sliderThumb.width            = 10f;
            sliderThumb.relativePosition = new Vector2(0f, -OffsetX);
            newSlider.thumbObject        = sliderThumb;

            // Tooltip.
            newSlider.tooltipBox = TooltipUtils.TooltipBox;
            newSlider.tooltip    = Translations.Translate(tooltipKey);

            // Mutiplier slider value range.
            newSlider.stepSize = 1f;
            newSlider.minValue = 1f;
            newSlider.maxValue = 100f;

            // Event handler to update text.
            if (isPercent)
            {
                newSlider.eventValueChanged += PercentSliderText;
            }
            else
            {
                newSlider.eventValueChanged += AbsSliderText;
            }

            return(newSlider);
        }
        /// <summary>
        /// Called whenever the currently selected building is changed to update the panel display.
        /// </summary>
        /// <param name="building">Newly selected building</param>
        internal void SelectionChanged(BuildingInfo building)
        {
            // Set current building.
            currentBuilding = building;

            // Safety first!
            if (currentBuilding != null)
            {
                string buildingName = building.name;

                // Get available calculation packs for this building.
                popPacks   = PopData.instance.GetPacks(building);
                floorPacks = FloorData.instance.Packs;

                // Get current and default packs for this item.
                currentPopPack   = (PopDataPack)PopData.instance.ActivePack(building);
                currentFloorPack = (FloorDataPack)FloorData.instance.ActivePack(building);
                PopDataPack   defaultPopPack   = (PopDataPack)PopData.instance.CurrentDefaultPack(building);
                FloorDataPack defaultFloorPack = (FloorDataPack)FloorData.instance.CurrentDefaultPack(building);

                // Update multiplier before we do any other calcs.
                multCheck.isChecked = Multipliers.instance.HasOverride(buildingName);
                currentMult         = Multipliers.instance.ActiveMultiplier(building);


                // Build pop pack menu.
                popMenu.items = new string[popPacks.Length];
                for (int i = 0; i < popMenu.items.Length; ++i)
                {
                    popMenu.items[i] = popPacks[i].displayName;

                    // Check for default name match,
                    if (popPacks[i].name.Equals(defaultPopPack.name))
                    {
                        popMenu.items[i] += Translations.Translate("RPR_PCK_DEF");
                    }

                    // Set menu selection to current pack if it matches.
                    if (popPacks[i].name.Equals(currentPopPack.name))
                    {
                        popMenu.selectedIndex = i;
                    }
                }

                // Set population pack to current pack.
                UpdatePopSelection(currentPopPack);

                // Build floor pack menu.
                floorMenu.items = new string[floorPacks.Length];
                for (int i = 0; i < floorPacks.Length; ++i)
                {
                    floorMenu.items[i] = floorPacks[i].displayName;

                    // Check for default name match,
                    if (floorPacks[i].name.Equals(defaultFloorPack.name))
                    {
                        floorMenu.items[i] += Translations.Translate("RPR_PCK_DEF");
                    }

                    // Set menu selection to current pack if it matches.
                    if (floorPacks[i].name.Equals(currentFloorPack.name))
                    {
                        floorMenu.selectedIndex = i;

                        // Force pack selection update.
                        UpdateFloorSelection(i);
                    }
                }

                // Update legacy panel for private building AIs (volumetric panel is updated by menu selection change above).
                if (building.GetAI() is PrivateBuildingAI)
                {
                    legacyPanel.SelectionChanged(building);
                }

                // Is this a school building (need to do school building after pop and floor packs are updated)?
                if (building.GetAI() is SchoolAI)
                {
                    // Yes - school building.  Set current pack.
                    currentSchoolPack = (SchoolDataPack)SchoolData.instance.ActivePack(building);

                    // Are we using custom school settings?
                    if (ModSettings.enableSchoolProperties)
                    {
                        // Yes - extend panel height and show school panel.
                        volumetricPanel.relativePosition = new Vector2(0f, SchoolCalcY);
                        applyButton.relativePosition     = new Vector2(ApplyX, SchoolSaveY);

                        // Get available school packs for this building.
                        schoolPacks = SchoolData.instance.GetPacks(building);

                        // Get current and default packs for this item.
                        currentSchoolPack = (SchoolDataPack)SchoolData.instance.ActivePack(building);
                        SchoolDataPack defaultSchoolPack = (SchoolDataPack)SchoolData.instance.CurrentDefaultPack(building);

                        // Build school pack menu.
                        schoolMenu.items = new string[schoolPacks.Length];
                        for (int i = 0; i < schoolMenu.items.Length; ++i)
                        {
                            schoolMenu.items[i] = schoolPacks[i].displayName;

                            // Check for default name match,
                            if (schoolPacks[i].name.Equals(defaultSchoolPack.name))
                            {
                                schoolMenu.items[i] += Translations.Translate("RPR_PCK_DEF");
                            }

                            // Set menu selection to current pack if it matches.
                            if (schoolPacks[i].name.Equals(currentSchoolPack.name))
                            {
                                schoolMenu.selectedIndex = i;

                                // Force pack selection update.
                                UpdateSchoolSelection(i);
                            }
                        }

                        // Set multiplier value.
                        multSlider.value = currentMult;

                        schoolPanel.Show();
                    }
                    else
                    {
                        // It's a school, but we're not using custom school settings, so use the non-school layout.
                        volumetricPanel.relativePosition = new Vector2(0f, BaseCalcY);
                        applyButton.relativePosition     = new Vector2(ApplyX, BaseSaveY);
                        schoolPanel.Hide();
                    }
                }
                else
                {
                    // Not a school building - use non-school layout.
                    currentSchoolPack = null;
                    volumetricPanel.relativePosition = new Vector2(0f, BaseCalcY);
                    applyButton.relativePosition     = new Vector2(ApplyX, BaseSaveY);
                    schoolPanel.Hide();
                }
            }
        }
        /// <summary>
        /// Updates the population calculation pack selection to the selected pack.
        /// </summary>
        /// <param name="index">Index number (from menu) of selection pack</param>
        private void UpdatePopSelection(PopDataPack popPack)
        {
            // Update selected pack.
            currentPopPack = popPack;

            // Update description.
            popDescription.text = currentPopPack.description;

            // Check if we're using legacy or volumetric data.
            if (currentPopPack is VolumetricPopPack)
            {
                // Volumetric pack.  Are we coming from a legacy setting?
                if (usingLegacy)
                {
                    // Reset flag.
                    usingLegacy = false;

                    // Restore floor rendering.
                    BuildingDetailsPanel.Panel.HideFloors = false;

                    // Update override label text.
                    floorOverrideLabel.text = Translations.Translate("RPR_CAL_FOV");

                    // Set visibility.
                    legacyPanel.Hide();
                    volumetricPanel.Show();
                }

                // Update panel with new calculations.
                LevelData thisLevel = CurrentLevelData;
                volumetricPanel.UpdatePopText(thisLevel);
                volumetricPanel.CalculateVolumetric(currentBuilding, thisLevel, currentFloorOverride ?? currentFloorPack, currentSchoolPack, currentMult);

                // Set visibility.
                if (currentFloorOverride == null)
                {
                    floorOverrideLabel.Hide();
                }
                else
                {
                    floorOverrideLabel.Show();
                }

                floorPanel.Show();
            }
            else
            {
                // Using legacy calcs = set flag.
                usingLegacy = true;

                // Set visibility.
                volumetricPanel.Hide();
                floorPanel.Hide();
                legacyPanel.Show();

                // Set override label and show.
                floorOverrideLabel.text = Translations.Translate("RPR_CAL_FLG");
                floorOverrideLabel.Show();

                // Cancel any floor rendering.
                BuildingDetailsPanel.Panel.HideFloors = true;
            }
        }
Beispiel #6
0
        /// <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 = UIControls.AddButton(this, 445f, 5f, Translations.Translate("RPR_CAT_ALL"), 120f);

            // 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.
            nameFilter = UIControls.BigLabelledTextField(this, width - 200f, 0, Translations.Translate("RPR_FIL_NAME"));

            // Name filter event handling - update on any change.
            nameFilter.eventTextChanged   += (control, text) => EventFilteringChanged(this, 5);
            nameFilter.eventTextSubmitted += (control, text) => EventFilteringChanged(this, 5);

            // Settings filter label.
            UILabel filterLabel = SettingsFilterLabel(55f, Translations.Translate("RPR_FIL_SET"));

            // Settings filter checkboxes.
            settingsFilter = new UICheckBox[(int)FilterCategories.NumCategories];
            for (int i = 0; i < (int)FilterCategories.NumCategories; ++i)
            {
                settingsFilter[i]                  = this.AddUIComponent <UICheckBox>();
                settingsFilter[i].width            = 20f;
                settingsFilter[i].height           = 20f;
                settingsFilter[i].clipChildren     = true;
                settingsFilter[i].relativePosition = new Vector3(AnyX + (FilterSpacing * i), 45f);

                // Checkbox sprites.
                UISprite sprite = settingsFilter[i].AddUIComponent <UISprite>();
                sprite.spriteName       = "ToggleBase";
                sprite.size             = new Vector2(20f, 20f);
                sprite.relativePosition = Vector3.zero;

                settingsFilter[i].checkedBoxObject = sprite.AddUIComponent <UISprite>();
                ((UISprite)settingsFilter[i].checkedBoxObject).spriteName = "ToggleBaseFocused";
                settingsFilter[i].checkedBoxObject.size             = new Vector2(20f, 20f);
                settingsFilter[i].checkedBoxObject.relativePosition = Vector3.zero;

                // Tooltip.
                settingsFilter[i].tooltip = Translations.Translate(FilterTooltipKeys[i]);

                // Special event handling for 'any' checkbox.
                if (i == (int)FilterCategories.Any)
                {
                    settingsFilter[i].eventCheckChanged += (control, isChecked) =>
                    {
                        if (isChecked)
                        {
                            // Unselect all other checkboxes if 'any' is checked.
                            settingsFilter[(int)FilterCategories.HasOverride].isChecked   = false;
                            settingsFilter[(int)FilterCategories.HasNonDefault].isChecked = false;
                        }
                    };
                }
                else
                {
                    // Non-'any' checkboxes.
                    // Unselect 'any' checkbox if any other is checked.
                    settingsFilter[i].eventCheckChanged += (control, isChecked) =>
                    {
                        if (isChecked)
                        {
                            settingsFilter[0].isChecked = false;
                        }
                    };
                }

                // Trigger filtering changed event if any checkbox is changed.
                settingsFilter[i].eventCheckChanged += (control, isChecked) => { EventFilteringChanged(this, 0); };
            }
        }
        /// <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>
        /// Adds control buttons to the bottom of the panel.
        /// </summary>
        /// <param name="panel">UI panel instance</param>
        protected void AddButtons(UIPanel panel)
        {
            // Add extra space.
            currentY += Margin;

            // Reset button.
            UIButton resetButton = UIControls.AddButton(panel, Margin, currentY, Translations.Translate("RPR_OPT_RTD"), 150f);

            resetButton.eventClicked += (component, clickEvent) => ResetToDefaults();

            // Revert button.
            UIButton revertToSaveButton = UIControls.AddButton(panel, (Margin * 2) + 150f, currentY, Translations.Translate("RPR_OPT_RTS"), 150f);

            revertToSaveButton.eventClicked += (component, clickEvent) => { ConfigUtils.LoadSettings(); PopulateFields(); };

            // Save button.
            UIButton saveButton = UIControls.AddButton(panel, (Margin * 3) + 300f, currentY, Translations.Translate("RPR_OPT_SAA"), 150f);

            saveButton.eventClicked += (component, clickEvent) => ApplyFields();
        }
        /// <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 _, int subService, bool isExtract = false, string label = null)
        {
            // Add a row for each level within this subservice.
            for (int i = 0; i < powerFields[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.
                powerFields[subService][i]     = AddTextField(panel, ColumnWidth, PowerX, currentY, powerLabel);
                waterFields[subService][i]     = AddTextField(panel, ColumnWidth, WaterX, currentY, waterLabel);
                garbageFields[subService][i]   = AddTextField(panel, ColumnWidth, GarbageX, currentY, garbageLabel);
                sewageFields[subService][i]    = AddTextField(panel, ColumnWidth, SewageX, currentY, sewageLabel);
                pollutionFields[subService][i] = AddTextField(panel, ColumnWidth, PollutionX, currentY, pollutionLabel);
                noiseFields[subService][i]     = AddTextField(panel, ColumnWidth, NoiseX, currentY, noiseLabel);
                mailFields[subService][i]      = AddTextField(panel, ColumnWidth, MailX, currentY, mailLabel);
                incomeFields[subService][i]    = AddTextField(panel, WideColumnWidth, IncomeX, currentY, wealthLabel);

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

            // Add an extra bit of space at the end.
            currentY += Margin;
        }
Beispiel #10
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()
        {
            // 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, TitleY);
            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;

            // Checkboxes.
            popCheck   = UIControls.LabelledCheckBox(this, 20f, PopCheckY, Translations.Translate("RPR_EDT_POP"), textScale: 1.0f);
            floorCheck = UIControls.LabelledCheckBox(this, 20f, FloorCheckY, Translations.Translate("RPR_EDT_FLR"), textScale: 1.0f);

            // Text fields.
            homeJobsCount    = AddLabelledTextfield(HomeJobY, "RPR_LBL_HOM");
            firstFloorField  = AddLabelledTextfield(FirstFloorY, "RPR_LBL_OFF");
            floorHeightField = AddLabelledTextfield(FloorHeightY, "RPR_LBL_OFH");
            homeJobLabel     = homeJobsCount.label;

            // Save button.
            saveButton         = UIControls.AddButton(this, MarginPadding, SaveY, Translations.Translate("RPR_CUS_ADD"));
            saveButton.tooltip = Translations.Translate("RPR_CUS_ADD_TIP");
            saveButton.Disable();

            // Delete button.
            deleteButton         = UIControls.AddButton(this, MarginPadding, DeleteY, Translations.Translate("RPR_CUS_DEL"));
            deleteButton.tooltip = Translations.Translate("RPR_CUS_DEL_TIP");
            deleteButton.Disable();

            // Message label (initially hidden).
            messageLabel = this.AddUIComponent <UILabel>();
            messageLabel.relativePosition = new Vector3(MarginPadding, MessageY);
            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";

            // Checkbox event handlers.
            popCheck.eventCheckChanged += (component, isChecked) =>
            {
                // If this is now selected and floorCheck is also selected, deselect floorCheck.
                if (isChecked && floorCheck.isChecked)
                {
                    floorCheck.isChecked = false;
                }
            };
            floorCheck.eventCheckChanged += (component, isChecked) =>
            {
                // If this is now selected and popCheck is also selected, deselect popCheck.
                if (isChecked && popCheck.isChecked)
                {
                    popCheck.isChecked = false;
                }
            };

            // Save button event handler.
            saveButton.eventClick += (component, clickEvent) => SaveAndApply();

            // Delete button event handler.
            deleteButton.eventClick += (component, clickEvent) => DeleteOverride();
        }
Beispiel #11
0
        /// <summary>
        /// Saves and applies settings - save button event handler.
        /// </summary>
        private void SaveAndApply()
        {
            // Hide message.
            messageLabel.isVisible = false;

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

            // Are we doing population overrides?
            if (popCheck.isChecked)
            {
                // Read total floor count textfield if possible; ignore zero values
                if (int.TryParse(homeJobsCount.textField.text, out int homesJobs) && homesJobs != 0)
                {
                    // 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
                    {
                        // Set overide.
                        PopData.instance.SetOverride(currentSelection, homesJobs);

                        // Update CitizenUnits for existing building instances.
                        CitizenUnitUtils.UpdateCitizenUnits(currentSelection.name, ItemClass.Service.None, currentSelection.GetSubService(), false);

                        // Repopulate field with parsed value.
                        homeJobLabel.text = homesJobs.ToString();
                    }
                }
                else
                {
                    // TryParse couldn't parse any data; print warning message in red.
                    messageLabel.textColor = new Color32(255, 0, 0, 255);
                    messageLabel.text      = Translations.Translate("RPR_ERR_INV");
                    messageLabel.isVisible = true;
                }
            }
            else
            {
                // Population override checkbox wasn't checked; remove any custom settings.
                PopData.instance.DeleteOverride(currentSelection);

                // Remove any legacy file settings to avoid conflicts.
                OverrideUtils.RemoveResidential(currentSelection);
                OverrideUtils.RemoveWorker(currentSelection);
            }

            // Are we doing floor overrides?
            if (floorCheck.isChecked)
            {
                // Attempt to parse values into override floor pack.
                FloorDataPack overrideFloors = TryParseFloors();

                // Were we successful?.
                if (overrideFloors != null)
                {
                    // Successful parsing - add override.
                    FloorData.instance.SetOverride(currentSelection, overrideFloors);

                    // Save configuration.
                    ConfigUtils.SaveSettings();

                    // Update panel override.
                    BuildingDetailsPanel.Panel.OverrideFloors = overrideFloors;

                    // Repopulate fields with parsed values.
                    UpdateFloorTextFields(overrideFloors.firstFloorMin.ToString(), overrideFloors.floorHeight.ToString());
                }
                else
                {
                    // Couldn't parse values; print warning message in red.
                    messageLabel.textColor = new Color32(255, 0, 0, 255);
                    messageLabel.text      = Translations.Translate("RPR_ERR_INV");
                    messageLabel.isVisible = true;
                }
            }
            else
            {
                // Floor override checkbox wasn't checked; remove any floor override.
                FloorData.instance.DeleteOverride(currentSelection);
            }

            // Refresh the display so that all panels reflect the updated settings.
            BuildingDetailsPanel.Panel.Refresh();
        }
Beispiel #12
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)
        {
            string buildingName = building.name;

            // Hide message.
            messageLabel.isVisible = false;

            // Set current selecion.
            currentSelection = building;

            // Blank all textfields and deselect checkboxes to start with.
            homeJobsCount.textField.text = string.Empty;
            UpdateFloorTextFields(string.Empty, string.Empty);
            popCheck.isChecked   = false;
            floorCheck.isChecked = false;

            // Disable buttons and exit if no valid building is selected.
            if (building == null || building.name == null)
            {
                saveButton.Disable();
                deleteButton.Disable();
                return;
            }
            // Set label by building type.
            if (building.GetService() == ItemClass.Service.Residential)
            {
                // Residential building - homes.
                homeJobLabel.text = Translations.Translate("RPR_LBL_HOM");
            }
            else if (building.GetService() == ItemClass.Service.Education)
            {
                // Schoool building - students.
                homeJobLabel.text = Translations.Translate("RPR_LBL_STU");
            }
            else
            {
                // Workplace building - jobs.
                homeJobLabel.text = Translations.Translate("RPR_LBL_JOB");
            }

            // Get any population override.
            int homesJobs = PopData.instance.GetOverride(buildingName);

            // If custom settings were found (return value was non-zero), then display the result, rename the save button, and enable the delete button.
            if (homesJobs != 0)
            {
                // Valid custom settings found; display the result, rename the save button, and enable the delete button.
                homeJobsCount.textField.text = homesJobs.ToString();
                saveButton.text = Translations.Translate("RPR_CUS_UPD");
                deleteButton.Enable();

                // Select the 'has population override' check.
                popCheck.isChecked = true;
            }
            else
            {
                // No population override - check for custom floor override.
                FloorDataPack overridePack = FloorData.instance.HasOverride(buildingName);
                if (overridePack != null)
                {
                    // Valid custom settings found; display the result, rename the save button, and enable the delete button.
                    UpdateFloorTextFields(overridePack.firstFloorMin.ToString(), overridePack.floorHeight.ToString());
                    saveButton.text = Translations.Translate("RPR_CUS_UPD");
                    deleteButton.Enable();

                    // Select the 'has floor override' check.
                    floorCheck.isChecked = true;
                }
                else
                {
                    //  No valid selection - rename the save button, and disable the delete button.
                    saveButton.text = Translations.Translate("RPR_CUS_ADD");
                    deleteButton.Disable();
                }

                // Communicate override to panel.
                BuildingDetailsPanel.Panel.OverrideFloors = overridePack;
            }

            // We've at least got a valid building, so enable the save button.
            saveButton.Enable();
        }
        /// <summary>
        /// Perform and display volumetric calculations for the currently selected building.
        /// </summary>
        /// <param name="building">Selected building prefab</param>
        /// <param name="levelData">Population (level) calculation data to apply to calculations</param>
        /// <param name="floorData">Floor calculation data to apply to calculations</param>
        /// <param name="schoolData">School calculation data to apply to calculations</param>
        /// <param name="schoolData">Multiplier to apply to calculations</param>
        internal void CalculateVolumetric(BuildingInfo building, LevelData levelData, FloorDataPack floorData, SchoolDataPack schoolData, float multiplier)
        {
            // Safety first!
            if (building == null)
            {
                return;
            }

            // Reset message label.
            messageLabel.text = string.Empty;

            // Perform calculations.
            // Get floors and allocate area an number of floor labels.
            SortedList <int, float> floors = PopData.instance.VolumetricFloors(building.m_generatedInfo, floorData, out float totalArea);

            floorAreaLabel.text = totalArea.ToString("N0", LocaleManager.cultureInfo);
            numFloorsLabel.text = floors.Count.ToString();

            // Get total units.
            int totalUnits = PopData.instance.VolumetricPopulation(building.m_generatedInfo, levelData, floorData, multiplier, floors, totalArea);

            // Floor labels list.
            List <string> floorLabels = new List <string>();

            // What we call our units for this building.
            string unitName;

            switch (building.GetService())
            {
            case ItemClass.Service.Residential:
                // Residential - households.
                unitName = Translations.Translate("RPR_CAL_UNI_HOU");
                break;

            case ItemClass.Service.Education:
                // Education - students.
                unitName = Translations.Translate("RPR_CAL_UNI_STU");
                break;

            default:
                // Default - workplaces.
                unitName = Translations.Translate("RPR_CAL_UNI_WOR");
                break;
            }

            // See if we're using area calculations for numbers of units, i.e. areaPer is at least one.
            if (levelData.areaPer > 0)
            {
                // Determine area percentage to use for calculations (inverse of empty area percentage).
                float areaPercent = 1 - (levelData.emptyPercent / 100f);

                // Create new floor area labels by iterating through each floor.
                for (int i = 0; i < floors.Count; ++i)
                {
                    // StringBuilder, because we're doing a fair bit of manipulation here.
                    StringBuilder floorString = new StringBuilder("Floor ");

                    // Floor number
                    floorString.Append(i + 1);
                    floorString.Append(" " + Translations.Translate("RPR_CAL_VOL_ARA") + " ");
                    floorString.Append(floors[i].ToString("N0"));

                    // See if we're calculating units per individual floor.
                    if (!levelData.multiFloorUnits)
                    {
                        // Number of units on this floor - always rounded down.
                        int floorUnits = (int)((floors[i] * areaPercent) / levelData.areaPer);
                        // Adjust by multiplier (after rounded calculation above).
                        floorUnits = (int)(floorUnits * multiplier);

                        // Add extra info to label.
                        floorString.Append(" (");
                        floorString.Append(floorUnits.ToString("N0"));
                        floorString.Append(" ");
                        floorString.Append(unitName);
                        floorString.Append(")");
                    }

                    // Add new floor label item with results for this calculation.
                    floorLabels.Add(floorString.ToString());
                }
            }

            // Do we have a current school selection, and are we using school property overrides?
            if (schoolData != null && ModSettings.enableSchoolProperties)
            {
                // Yes - calculate and display school worker breakdown.
                int[] workers = SchoolData.instance.CalcWorkers(schoolData, totalUnits);
                schoolWorkerLabel.Show();
                schoolWorkerLabel.text = workers[0] + " / " + workers[1] + " / " + workers[2] + " / " + workers[3];

                // Calculate construction cost to display.
                int cost = SchoolData.instance.CalcCost(schoolData, totalUnits);
                ColossalFramework.Singleton <EconomyManager> .instance.m_EconomyWrapper.OnGetConstructionCost(ref cost, building.m_class.m_service, building.m_class.m_subService, building.m_class.m_level);

                // Calculate maintenance cost to display.
                int maintenance = SchoolData.instance.CalcMaint(schoolData, totalUnits) * 100;
                ColossalFramework.Singleton <EconomyManager> .instance.m_EconomyWrapper.OnGetMaintenanceCost(ref maintenance, building.m_class.m_service, building.m_class.m_subService, building.m_class.m_level);

                float displayMaint = Mathf.Abs(maintenance * 0.0016f);

                // And display school cost breakdown.
                costLabel.Show();
                costLabel.text = cost.ToString((!(displayMaint >= 10f)) ? Settings.moneyFormat : Settings.moneyFormatNoCents, LocaleManager.cultureInfo) + " / " + displayMaint.ToString((!(displayMaint >= 10f)) ? Settings.moneyFormat : Settings.moneyFormatNoCents, LocaleManager.cultureInfo);

                // Enforce school floors list position.
                ResetFloorListPosition();
            }
            else
            {
                // No - hide school worker breakdown and cost labels.
                schoolWorkerLabel.Hide();
                costLabel.Hide();

                // Enforce default floors list position.
                ResetFloorListPosition();
            }

            // Allocate our new list of labels to the floors list (via an interim fastlist to avoid race conditions if we 'build' manually directly into floorsList).
            FastList <object> fastList = new FastList <object>()
            {
                m_buffer = floorLabels.ToArray(),
                m_size   = floorLabels.Count
            };

            floorsList.rowsData = fastList;

            // Display total unit calculation result.
            switch (building.GetService())
            {
            case ItemClass.Service.Residential:
                // Residential building.
                totalJobsLabel.Hide();
                totalStudentsLabel.Hide();
                totalHomesLabel.Show();
                totalHomesLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;

            case ItemClass.Service.Education:
                // School building.
                totalHomesLabel.Hide();
                totalJobsLabel.Hide();
                totalStudentsLabel.Show();
                totalStudentsLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;

            default:
                // Workplace building.
                totalHomesLabel.Hide();
                totalStudentsLabel.Hide();
                totalJobsLabel.Show();
                totalJobsLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;
            }

            // Display commercial visit count, or hide the label if not commercial.
            if (building.GetAI() is CommercialBuildingAI)
            {
                visitCountLabel.Show();
                visitCountLabel.text = RealisticVisitplaceCount.PreviewVisitCount(building, totalUnits).ToString();
            }
            else
            {
                visitCountLabel.Hide();
            }

            // Display production count, or hide the label if not a production building.
            if (building.GetAI() is PrivateBuildingAI privateAI && (privateAI is OfficeBuildingAI || privateAI is IndustrialBuildingAI || privateAI is IndustrialExtractorAI))
            {
                productionLabel.Show();
                productionLabel.text = privateAI.CalculateProductionCapacity(building.GetClassLevel(), new ColossalFramework.Math.Randomizer(), building.GetWidth(), building.GetLength()).ToString();
            }
        /// <summary>
        /// Adds editing options tab to tabstrip.
        /// </summary>
        /// <param name="tabStrip">Tab strip to add to</param>
        /// <param name="tabIndex">Index number of tab</param>
        internal FloorPanel(UITabstrip tabStrip, int tabIndex) : base(tabStrip, tabIndex)
        {
            // Add title.
            float currentY = PanelUtils.TitleLabel(panel, TabTooltipKey);

            // Initialise arrays
            floorHeightField = new UITextField();
            firstMinField = new UITextField();
            firstExtraField = new UITextField();
            firstEmptyCheck = new UICheckBox();

            // Pack selection dropdown.
            packDropDown = UIControls.AddPlainDropDown(panel, Translations.Translate("RPR_OPT_CPK"), new string[0], -1);
            packDropDown.parent.relativePosition = new Vector3(20f, currentY);
            packDropDown.eventSelectedIndexChanged += PackChanged;

            // Headings.
            currentY += 140f;
            PanelUtils.ColumnLabel(panel, FloorHeightX, currentY, ColumnWidth, Translations.Translate("RPR_CAL_VOL_FLH"), Translations.Translate("RPR_CAL_VOL_FLH_TIP"), 1.0f);
            PanelUtils.ColumnLabel(panel, FirstMinX, currentY, ColumnWidth, Translations.Translate("RPR_CAL_VOL_FMN"), Translations.Translate("RPR_CAL_VOL_FMN_TIP"), 1.0f);
            PanelUtils.ColumnLabel(panel, FirstMaxX, currentY, ColumnWidth, Translations.Translate("RPR_CAL_VOL_FMX"), Translations.Translate("RPR_CAL_VOL_FMX_TIP"), 1.0f);
            PanelUtils.ColumnLabel(panel, FirstEmptyX, currentY, ColumnWidth, Translations.Translate("RPR_CAL_VOL_IGF"), Translations.Translate("RPR_CAL_VOL_IGF_TIP"), 1.0f);

            // Add level textfields.
            currentY += RowHeight;
            floorHeightField = UIControls.AddTextField(panel, FloorHeightX + Margin, currentY, width: TextFieldWidth, tooltip: Translations.Translate("RPR_CAL_VOL_FLH_TIP"));
            floorHeightField.eventTextChanged += (control, value) => PanelUtils.FloatTextFilter((UITextField)control, value);
            floorHeightField.tooltipBox = TooltipUtils.TooltipBox;

            firstMinField = UIControls.AddTextField(panel, FirstMinX + Margin, currentY, width: TextFieldWidth, tooltip: Translations.Translate("RPR_CAL_VOL_FMN_TIP"));
            firstMinField.eventTextChanged += (control, value) => PanelUtils.FloatTextFilter((UITextField)control, value);
            firstMinField.tooltipBox = TooltipUtils.TooltipBox;

            firstExtraField = UIControls.AddTextField(panel, FirstMaxX + Margin, currentY, width: TextFieldWidth, tooltip: Translations.Translate("RPR_CAL_VOL_FMX_TIP"));
            firstExtraField.eventTextChanged += (control, value) => PanelUtils.FloatTextFilter((UITextField)control, value);
            firstExtraField.tooltipBox = TooltipUtils.TooltipBox;

            firstEmptyCheck = UIControls.AddCheckBox(panel, FirstEmptyX + (ColumnWidth / 2), currentY, tooltip: Translations.Translate("RPR_CAL_VOL_IGF_TIP"));
            firstEmptyCheck.tooltipBox = TooltipUtils.TooltipBox;

            // Move to next row.
            currentY += RowHeight;

            // Add footer controls.
            PanelFooter(currentY);

            // Populate pack menu and set onitial pack selection.
            packDropDown.items = PackList();
            packDropDown.selectedIndex = 0;
        }