Jump to content
Cyclone Boom

Tutorial: Method for Automated SimCity 4 Screen Mode Switching

7 posts in this topic Last Reply

Highlighted Posts

I am always amazed at the level of detail you employ when creating tutorials. This is both thorough and a delightful help for advanced players to automate the mundane task of swapping specific mods in or out of the plugins folder. It saves time and insures there are no mistakes.

There was a question some time back where the player wanted to use different specific terrain mods on a per city tile basis. This would be ideal for that as well.

Share this post


Link to post
Share on other sites
18 hours ago, Cyclone Boom said:

in Windows it's possible to use the "Auto-hide the taskbar" option from Taskbar Properties. This does what it says, and makes the taskbar hidden from view when not hovered over. However, this requires toggling back the setting once exiting the game.

Well, you're not required to change back. Some of you might take a liking to auto-hide and decide to keep it.

Share this post


Link to post
Share on other sites
  • Original Poster
  • 9 hours ago, CorinaMarie said:

    I am always amazed at the level of detail you employ when creating tutorials.

    I suppose anything worth doing is worth overdoing. *:)

     

    9 hours ago, CorinaMarie said:

    There was a question some time back where the player wanted to use different specific terrain mods on a per city tile basis. This would be ideal for that as well.

    I can certainly see potential scope here for other mods. In terms of a single file it'd be the case of continuing on with two scripts: One for with the mod, the other for without. Then modifying the File= <File Name> option in both. Finally if so desired, adjusting the parameters to make each start either windowed ( -w ) or full screen ( -f ).

    The same I suspect could also be done by moving an entire folder containing multiple files. Or expanding the code to perform additional move processes.

     

    1 hour ago, jeffryfisher said:

    Well, you're not required to change back. Some of you might take a liking to auto-hide and decide to keep it.

    True, it is indeed the personal choice of each user as with any option. The only reason I say "required" is since to restore the configuration how it was before starting the game and using said workaround, the auto-hide setting must be toggled back and unchecked.

    Of course in the scope of this tutorial, preserving the Taskbar is needed or else there probably isn't much point in raising the UI when running windowed. *;)

    Share this post


    Link to post
    Share on other sites

    You can also use a program to assist.

    I use Shiftwindow. All you need to do is specify what monitor and what program you want to be full screen and your window'd program now fits perfectly. You can also use your other monitor without trouble at all!

    Just hit trigger and you're all set!

    aHbEuf7.png

     

    Share this post


    Link to post
    Share on other sites
  • Original Poster
  • 8 hours ago, Haljackey said:

    All you need to do is specify what monitor and what program you want to be full screen and your window'd program now fits perfectly.

    Understandably command line scripts aren't for everyone.

    So from what I gather, ShiftWindow would replace the need to raise the UI since the window could be stretched to full screen. Or alternatively by adjusting the "Shift to" options, for a specified width and height with coordinates. Then it would be possible to position and resize the game's window above the Taskbar if so desired. Is that correct?

    Really the idea behind my method is for there to be two shortcuts, allowing the game to be started either way with a familiar double click. Which in turn moves the mod file in or out of one's Plugins folder to benefit from the raised UI (keeping the Taskbar visible). Or whenever preferred, to run SC4 in full screen as normal. Also being batch scripts that Windows recognises by default, they aren't dependent on external programs which may not be a like-for-like solution to move the mod file as described.

    I'm also curious on the possible other uses to manage sets of custom content. Because batch files are simply a set of repeatable instructions.

    Share this post


    Link to post
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an Account  

    Sign up to join our friendly community. It's easy!  

    Register a New Account

    Sign In  

    Already have an account? Sign in here.

    Sign In Now


    • Similar Content

      • By Dragonxander
        Welcome everyone! Today I want to present the second entry of my earthworks tutorial series! Today we're covering the basics of building a main, two-lane road into a town across rolling/undulating terrain!
         
        I would appreciate if forum members could give me some feedback on the tutorial entry's format. This time I'm posting a slideshow video, as it was easier to make than wrestling image hosting sites with a huge upload that was gonna have about 70 screenshots.
         
         
         
        For general discussions and polls on this tutorial series, visit:
         
        Mods featured:
        * Network Addon Mod 36:
         
         
        * BRF Tunnel and Slope Mod:
         
        * CPT Meadowshire Terrain Mod: 
        http://sc4devotion.com/csxlex/lex_filedesc.php?lotGET=58
         
        * CPT Meadowshire Coast Tree Mod: 
        http://sc4devotion.com/csxlex/lex_filedesc.php?lotGET=60
         
        * B98 Beach Extend Mod:
         
         
        * RVT Coast Mod 2009:
         
         
      • By Dragonxander
        Greetings to all fellow mayors and Simtropolitan citizens!
         
        I want to present you a series of tutorials revolving around a very immersing part of the game: earthworks!
        Why? Because it's a very fundamental aspect of any construction in real life and can add aesthetic and functional depth to your cities.  I came up with a series of tutorials I'll be developing for this purpose, ranging from the most fundamental aspects to highly demanding applications of in-game earthmoving to shape the destiny of your city.
        Perhaps the biggest gain players can get from mastering earthworks is making rugged terrain regions all that much more enjoyable. It may be tempting to always go with a very flat region to make building easy, but that approach makes players miss the opportunity of a challenging terrain resulting in a thrilling and wonderful landscape. I'll be making these based on my main ongoing region, the Sinnoh Region, as the one fundamental geographic element that defines it are its mountains.
         
        One favor I ask followers of this series is to avoid posting on the tutorial threads until they have been constructed. Their completion will be announced here and in the meantime, the corresponding comments can be made here.
        Finally, before we start, I would like everyone's feedback on which category should I tackle next after completing the fundamentals tutorial.
         
        ESSENTIAL TOOLS:
        Network Addon Mod, latest version (currently the 36, versions 34 and 35 will still be adequate). In particular, make sure to have the NAM hole digging lots, these are super important to do a lot of the required steps I'll be showing.  
         
        Slope mode of your choice (in my case, the BRF Tunnel and Slope Mod). I recommend this one in particular because it's one of the most strict slope mods available for SimCity 4. I currently use an older version which still did not include a dedicated Real Highway (RHW) slope, but that isn't a problem for me as you'll all learn during the tutorials.  
        Region with hilly, rolling and/or mountainous terrain--anything that isn't a super flat expanse of nothingness will do.  
        God Terraforming in Mayor Mode (or if not your thing, use the cheat to activate God mode terraforming tools in Mayor Mode  
         
        T U T O R I A L   S E R I E S :
        A. EARTHWORKS FUNDAMENTALS: (in the making)
        Starting a town (Emeragrove): now available! Main local road (Emeragrove): screenshots available, pending writing Bridge across river (Emeragrove): upcoming Power lines (Emeragrove): upcoming  
        B. EARTHWORKS APPLIED TO MAJOR TRANSPORTATION PROJECTS AND URBAN RENEWAL (future)
        Rural Freeway Segment (Route 203) Rural Freeway Interchanges (Route 203) River Diversion (Jubilife) Urban Freeway Retrofit (Jubilife) Median Mass Transit (Jubilife) Suburban Freeway Retrofit (Route 204)  
        C. MOUNTAIN MADNESS (future)
         Difficult mountain crossing (Oreburgh-Pal Park) Mountain Railway (Oreburgh-Pal Park) Dam and reservoir (Pal Park) Mine expansion (Oreburgh) Tunnels across mountains (Route 216)  
        D. COASTAL CHALLENGE (future)
        Seaport (Jubilife Port) Power Plant (Jubilife Port) Beaches (Hotel Grand Lake) City straddling the sea (Sunyshore) Building a linear breakwater (Seabreak Path) Building an island (Flower Paradise) Underwater tunnels (Jubilife)
      • By Dragonxander
        The very first post of this tutorial series!!
         
        Earthwork Fundamentals 1: Starting a Town
         
        Welcome to Emeragrove!!
        Population: 0 Infrastructure: just a neighbor connection Trees: 0 Citizen complaints: 0 ...Huh....not a whole lot of action in here. Maybe we can change that!
         
        Our blank slate: a large city tile with a river running along the western side and rolling terrain on almost all of its land area. We will settle next to the head of navigation of the small river, highlighted by the saddle shape in red.

         
        We want to investigate the terrain characteristics in the area, we start by activating the “terrainquery” cheat (Ctrl+X, then write "terrainquery"). This allows us to obtain the tridimensional coordinates for any point within the city tile.

         
        We find that, towards the northwest of our view, the terrain’s elevation (y = 258.9 meters above SimCity level) is a meager 8.9 meters above the game’s sea level (250.0 meters).

         
         
        Moving towards the southeast, we find a slightly more elevated position, at 260.8 meters above SimCity level. We secure this elevation by plopping road tiles to form a flat surface we want to propagate via cut and fill. This matters because if the starting terrain is too low, the slope cannot be demonstrated effectively and Sims are at risk of losing everything to a river flood within the floodplain area.

         
        We then continue placing single road squares in a checkerboard pattern and emphasizing the boundaries of the terrain we intend to level off. One convenient way to do this is to plop a sufficiently long stretch of road squares along which we’ll be dragging the roads proper to level additional terrain along the stretch of squares.

         
        We keep dragging roads until covering an area sufficiently large to host our town’s historic center.

         
        Now, we zoom in towards an edge of the flattened terrain. As you can notice, the slope between the leveled terrain and the floodplain may be too steep for comfort or even safety (landslide risks, perhaps?).

         
        We can now fix that by dragging short stubs of road, starting at the flattened area’s edge. These stubs should be one square shorter than the distance needed to directly touch the floodplain.

         
        We continue this operation until we have the particular edge covered.

         
        Then we begin doing the same along the diagonal edge. Notice how the stub is dragged from the protruding corner of the tile.

      • By AmiPolizeiFunk
        I only discovered that terrain-party exports are ~4.166% bigger than they should be (terrain-party crams an 18km square area into a 17.28km square mapspace) while I was half-way through making a new Berlin 1:1 map. It was easy to upscale the heigtmap work that I had done (export my cleaned heightmap from the map editor, scale it up in photoshop, re-import it), but I was kinda stuck when it came to all of the work I did on rails and roads. I needed a global function to scale up my rails and roads in the x and z dimensions. Enter MoveIt! export->import and python. 
         
        A friend wrote this script for me in Python v3 to go through the xml file and tweak the X and Z coordinates. All X and Z values are multiplied by 1.041666
        Here are the MoveIt! XML export files before and after manipulation: https://www.coh2.org/file/16224/moveitexports.rar
         
        And a pic, showing the perfectly fitting result after the upscale:
         
        I think this kind of thing opens up some amazing possibilities! Next, I'm interested in scraping tree data from Berlin's databases and creating ploppable MoveIt! XML files from them. This is a bit out of my league tho, but I have the feeling that it can be done. 
         
      • By boformer
        In this tutorial, we will create a simple mod that globally replaces vanilla road trees with custom trees made by MrMaison. Like in Tutorial 1, we will use a loading hook, prefab collections and the ModTools scene explorer. You will also learn how to add settings to your mod.
         
        Step 1: Exploring and modifying network and tree prefabs with the ModTools scene explorer
        Before writing the actual mod, we will apply the replacement with the ModTools scene explorer.
        Just like buildings, networks and trees are defined as prefabs (NetInfo and TreeInfo, see Tutorial 1 Step 3). There are various way to find these prefabs in the ModTools scene explorer. The easiest way that works for vanilla and workshop assets is the ToolsController. "Tools" in Cities: Skylines allow you to interact with the game world. The most obvious tool is the bulldozer, but there are also tools for the placement of objects, zoning, transport line creation and camera control. For this tutorial, we will access the TreeTool and the NetTool, which hold a reference to the tree/network prefab you are placing.
        Select a tree you in the Landscaping panel. I chose the Royal Palm made by MrMaison. While the placement mode is enabled, open the ModTools scene explorer (CTRL + E). On the left, select Tool Controller > TreeTool. You will see the properties of the TreeTool on the right. The currently selected tree prefab is stored in the m_prefab property. Press the "Copy" button to copy the reference of the tree prefab to the ModTools clipboard (it does not copy the actual data, just the memory address of the prefab).

        Tip: For every prefab (prop, building, network, ...) there is also a "Preview" button that displays a model viewer, and more importantly, a "Plop" button that allows you to plop the asset. With this button, you can place assets which are not available in the panels of the game, like props or sub-buildings.
        Now, select a road with trees in the Roads panel. I chose the "Large Avenue with Grass" that was added by the MT update. Open the scene explorer and select Tool Controller > NetTool. On the right, expand the m_prefab property (the currently selected network prefab).

        You can now see the properties of the network prefab. The structure of a network prefab looks like this:
        NetInfo: Network Prefab ├ m_nodes[]: Array of objects defining meshes/textures for intersections ├ m_segments[]: Array of objects defining meshes/textures for segments ├ m_lanes[]: Array of NetInfo.Lane objects │ └ NetInfo.Lane: Defines a lane used by vehicles or pedestrians + prop/tree decoration │ ├ m_laneProps │ │ └ m_props[]: Array of prop items │ │ └ NetLaneProps.Prop: Like BuildingInfo.Prop, defines the position a prop/tree │ │ ├ m_prop/m_finalProp: The prop prefab │ │ ├ m_tree/m_finalTree: The tree prefab │ │ ├ m_probability: Probability that the prop/tree spawns │ │ ├ m_repeatDistance: Distance between two props/trees │ │ └ ... │ └ ... └ ... Expand the m_lanes array and search for the lane that contains the tree prop items you want to replace. In case of the "Large Avenue with Grass", navigate to m_lanes > m_lanes.[0] > m_laneProps > m_props > m_props.[4]:

        Click the "Paste" button on the right for the properties m_finalTree and m_tree. This will assign the custom tree prefab we copied earlier to the prop item:

        You will instantly see the result: The trees of all avenues in the city have been replaced.

        In the next steps, we will create a mod that automates the replacement.
        Step 2: Project Setup
        Create a new project named "RoadTreeReplacer" in VS2017, following Method 2 in Tutorial 0 (working with a text editor is still possible in this tutorial, but not recommended)
        Your IUserMod implementation should look like this:
        using ICities; namespace RoadTreeReplacer { public class RoadTreeReplacerMod : IUserMod { public string Name => "Road Tree Replacer"; public string Description => "Replaces the boring Oak roadside trees with MrMaison's creations"; } }  
        Step 3: Loading Hook & Tree Replacement Logic
        Create a new file called RoadTreeReplacerLoading.cs in the Solution Explorer. This file will contain our ILoadingExtension implementation that is invoked by the game when a save is loaded (See Tutorial 1 Step 2). Like in the last tutorial, we will use the static PrefabCollection class to find our network and tree prefab.
        Add the following contents to the file:
        using UnityEngine; using ICities; namespace RoadTreeReplacer { public class RoadTreeReplacerLoading : LoadingExtensionBase { public override void OnLevelLoaded(LoadMode mode) { // Find the network NetInfo netPrefab = PrefabCollection<NetInfo>.FindLoaded("Avenue Large With Grass"); // Find the tree we want to use as a replacement TreeInfo treePrefab = PrefabCollection<TreeInfo>.FindLoaded("909448182.Royal Palm_Data"); // Check if both prefabs are loaded, cancel if not if (netPrefab == null) { Debug.LogError("RTR: The network could not be found"); return; } if (treePrefab == null) { Debug.LogError("RTR: The replacement tree could not be found"); return; } // cancel if lanes array is null (networks without lanes) if (netPrefab.m_lanes == null) return; // iterate through all lanes foreach (NetInfo.Lane lane in netPrefab.m_lanes) { // cancel if lane props array is null (networks without lanes) if (lane?.m_laneProps?.m_props == null) continue; // iterate through all lane props of that lane foreach (NetLaneProps.Prop laneProp in lane.m_laneProps.m_props) { if (laneProp == null) continue; // if the tree/finalTree field is set, replace it with our tree prefab if (laneProp.m_tree != null) { laneProp.m_tree = treePrefab; } if (laneProp.m_finalTree != null) { laneProp.m_finalTree = treePrefab; } } } Debug.Log("RTR: Replacement successful!"); } } } Tip: You can use the Debug.Log(...) method to add log entries to the output_log.txt. The log entries are also displayed in the ModTools console, which can be used for debugging your mod.

        Now compile the mod (F6). If there are any compilation errors, use the error list in VS2017 to locate and fix the error (View > Error List)
        When the compilation was successful, run the game. Enable the mod in content manager (if your mod does not show up, you probably forgot to setup the post build script).
        Now create or load a city. The tree replacement is now automated. You will also see the success message in the ModTools console:

         
        Before we proceed with the next step, we will move our replacement code to a separate ReplaceNetTrees method. We can call this method multiple times with a single line of code:
        using UnityEngine; using ICities; namespace RoadTreeReplacer { public class RoadTreeReplacerLoading : LoadingExtensionBase { public override void OnLevelLoaded(LoadMode mode) { ReplaceNetTrees("Avenue Large With Grass", "909448182.Royal Palm_Data"); ReplaceNetTrees("Medium Road Decoration Trees", "909448182.Royal Palm_Data"); } private void ReplaceNetTrees(string netName, string treeName) { NetInfo netPrefab = PrefabCollection<NetInfo>.FindLoaded(netName); TreeInfo treePrefab = PrefabCollection<TreeInfo>.FindLoaded(treeName); if (netPrefab == null) { Debug.LogError($"RTR: The network {netName} could not be found"); return; } if (treePrefab == null) { Debug.LogError($"RTR: The replacement tree {treeName} could not be found"); return; } if (netPrefab.m_lanes == null) return; foreach (NetInfo.Lane lane in netPrefab.m_lanes) { if (lane?.m_laneProps?.m_props == null) continue; foreach (NetLaneProps.Prop laneProp in lane.m_laneProps.m_props) { if (laneProp == null) continue; if (laneProp.m_tree != null) laneProp.m_tree = treePrefab; if (laneProp.m_finalTree != null) laneProp.m_finalTree = treePrefab; } } Debug.Log($"RTR: Replacement of tree in network {netName} successful!"); } } }  
        Step 4: Adding Mod Settings
        Right now the functionality of the mod is fixed, there is no way to configure which trees are replaced. Adding settings to a mod is a difficult task. It will require 3 components:
        User interface with checkboxes or dropdown menus (using the settings API provided by CO, or a custom window) Data structure for the settings data (usually a C# class, or a set of key-value pairs) Serialization System (to .xml file, or save game) To keep it simple, we will only add a simple settings page with 3 dropdown options (Small Road Tree, Medium Road Tree, Large Road Tree), with a fixed number of trees to choose from (Default, Royal Palm, Weeping Silver Birch, River Red Gum Small). The settings will saved in a .xml file in the Cities: Skylines installation directory. The settings are global, that means the mod applies the same settings to all cities (in one of the next tutorials, I will show you how to save additional data in the save game).
        Serialization System
        Add a new file called Configuration.cs and paste this code.
        It is a very minimalistic serialization library that I've written some time ago. It does all the heavy lifting for you (loading and saving of .xml files, transformation from/to C# objects). It is not important to understand what happens internally, you just have to understand how to use it.
        The library provides a method to load your configuration data:
        YourConfiguration config = Configuration<YourConfiguration>.Load(); And to save it:
        Configuration<YourConfiguration>.Save(); You only have to provide the data class that defines the structure of the .xml file.
        Note: If you are using a Mac, you may have to add the System.Xml dependency to your project
        Data Structure
        Add a new class named RoadTreeReplacerConfiguration. This data class contains the string options we want to save:
        namespace RoadTreeReplacer { [ConfigurationPath("RoadTreeReplacer.xml")] public class RoadTreeReplacerConfiguration { public string SmallRoadTree { get; set; } = "909448182.Royal Palm_Data"; public string MediumRoadTree { get; set; } = "909448182.Royal Palm_Data"; public string LargeRoadTree { get; set; } = "909448182.Royal Palm_Data"; } } The [ConfigurationPath] attribute is read by the serialization library. The strings on the right are the default values used when a new configuration is created.
        User Interface
        Some time ago, CO added a simple settings API that allows you to create simple setting menus with a few lines of code. To use it, add a new method called OnSettingsUI to your IUserMod implementation:
        using System; using System.Collections.Generic; using ICities; namespace RoadTreeReplacer { public class RoadTreeReplacerMod : IUserMod { public string Name => "Road Tree Replacer"; public string Description => "Replaces the boring Oak roadside trees with MrMaison's creations"; // The strings displayed in the dropdown private static readonly string[] OptionLabels = { "Default", "Royal Palm", "Weeping Silver Birch", "River Red Gum" }; // The corresponding prefab names private static readonly string[] OptionValues = { "Tree2Variant", "909448182.Royal Palm_Data", "765126845.Weeping Silver Birch_Data", "742114726.River Red Gum small_Data" }; // Sets up a settings user interface public void OnSettingsUI(UIHelperBase helper) { // Load the configuration RoadTreeReplacerConfiguration config = Configuration<RoadTreeReplacerConfiguration>.Load(); // Small Roads int smallSelectedIndex = GetSelectedOptionIndex(config.SmallRoadTree); helper.AddDropdown("Small Road Tree", OptionLabels, smallSelectedIndex, sel => { // Change config value and save config config.SmallRoadTree = OptionValues[sel]; Configuration<RoadTreeReplacerConfiguration>.Save(); }); // Medium Roads int mediumSelectedIndex = GetSelectedOptionIndex(config.MediumRoadTree); helper.AddDropdown("Medium Road Tree", OptionLabels, mediumSelectedIndex, sel => { // Change config value and save config config.MediumRoadTree = OptionValues[sel]; Configuration<RoadTreeReplacerConfiguration>.Save(); }); // Large Roads int largeSelectedIndex = GetSelectedOptionIndex(config.LargeRoadTree); helper.AddDropdown("Large Road Tree", OptionLabels, largeSelectedIndex, sel => { // Change config value and save config config.LargeRoadTree = OptionValues[sel]; Configuration<RoadTreeReplacerConfiguration>.Save(); }); } // Returns the index number of the option that is currently selected private int GetSelectedOptionIndex(string value) { int index = Array.IndexOf(OptionValues, value); if (index < 0) index = 0; return index; } } } What happens here?
        Load the config data with the Load method provided by the library Get the index of the dropdown option that should be selected at first Add a dropdown option with label, options, selected index and a callback (called when selection is changed) (Repeat 2. and 3. for medium and large roads) The OptionLabels array contains the strings displayed in the dropdown menus. The other array, OptionValues, contains the internal names of the prefabs.
        The callback is a lamda function that takes the selected index (0-3). It saves the value that corresponds to the selected index in the configuration file.
        The game automatically creates a settings page for mods which are implementing the OnSettingsUI method. No further steps are needed.
        Tip: Other methods for UI element generation: AddButton(), AddCheckbox(), AddGroup(), AddSlider(), AddSpace(), AddTextfield()
        Step 5: Using the configuration values in our loading hook
        In the RoadTreeReplacerLoading class, replace the OnLevelLoaded method:
        public override void OnLevelLoaded(LoadMode mode) { // Load the configuration RoadTreeReplacerConfiguration config = Configuration<RoadTreeReplacerConfiguration>.Load(); ReplaceNetTrees("Basic Road Decoration Trees", config.SmallRoadTree); ReplaceNetTrees("Oneway Road Decoration Trees", config.SmallRoadTree); ReplaceNetTrees("Medium Road Decoration Trees", config.MediumRoadTree); ReplaceNetTrees("Avenue Large With Grass", config.MediumRoadTree); ReplaceNetTrees("Avenue Large With Buslanes Grass", config.MediumRoadTree); ReplaceNetTrees("Large Road Decoration Trees", config.LargeRoadTree); ReplaceNetTrees("Large Oneway Decoration Trees", config.LargeRoadTree); } This will replace the trees of all vanilla roads with the trees selected in the settings. And we are done!

        After changing the options for the first time, the settings are saved to .xml:

        Note: To apply the changed settings, you have to reload your city.
        Happy Coding!
        Download Source
         
        Next part:
         
    • Recently Browsing   0 members

      No registered users viewing this page.

    ×

    Thank You for the Continued Support!

    Simtropolis relies mainly on member donations to continue operating. Without your support, we just would not be able to be entering our 15th year online!  You've really help make this a great community.

    But we still need your support to stay online. If you're able to, please consider a donation to help us stay up and running, so that we can help keep bringing SimCity players together to share our creations.

    Make a Donation, Get a Gift!

    Expand your city with the best from the Simtropolis Echange.
    Make a donation and get one or all three discs today!

    stexcollection-header.png

    By way of a "Thank You" gift, we'd like to send you our STEX Collector's DVD. It's some of the best buildings, lots, maps and mods collected for you over the years. Check out the STEX Collections for more info.

    Each donation helps keep Simtropolis online, open and free!

    Thank you for reading and enjoy the site!

    More About STEX Collections