• Moose
  • Announcements

    • Dirktator

      Please help us for June & July   06/20/2017

      This month we're hoping for a little bit of a boost this month or next, in addition to covering the regular bills, we'd love to be able to do some hardware upgrades. STEX Collector's Set gifts can now be sent to you via digital download as well as regular mail if you wish! Every donation dollar goes toward keeping Simtropolis online and humming along. Your contribution is so meaningful, every dollar counts! Hardware Upgrades We've received a recommendation to consider some hardware upgrades to our ageing server. As our website community software improves with more and more features, and as we bolt on additional features such as chat, the increased load to handle all these new toys is taking the server a bit to task. You may have noticed that we'd been forced to temporarily disable the chat as we worked out allocating resources for more optimal performance. Following this, we've applied a number of 'soft' changes such as caching options and experimented with server configurations (nginx as proxy, for example), so the hardware aspect is also an area we'd like to focus on. The last time we upgraded hardware was back in 2012, prior to the release of SC13. Your contribution means a lot! Your donation will go toward helping us to 1.) migrate to a newer server or 2.) at least get some ram/cpu/storage updates and any other infrastructure hardware or services. Donate and Get a Gift or Donate Any Amount Thanks so much! - Dirktator & The Admins
CorinaMarie

C.O.R.I.M.A.P.S. - A Tutorial for SimCity 4

108 posts in this topic

Cori, your imagination and your IT knowledge are mind blowing ... And now you're moving into literary romance with the farm region !!!

But my main point is that I'm stumped. When I first your read  your maps posts (about two weeks ago), I tried the "Grey Cloud Trick" and I paid dirt with a first map. But since then I can't get another one. I have tried about 20 or 30 times and no luck. So I checked that first map into the config.bmp and the gray.bmp and, of course, checked your tut to the point I almost know it by heart. Since I noticed that there are other people in the same situation, it encouraged me to post this comment.

 

CorinaMarie likes this

Share this post


Link to post
Share on other sites
59 minutes ago, huzman said:

But since then I can't get another one. I have tried about 20 or 30 times and no luck.

I'd love to help. Can you be more specific about what isn't working out?

huzman likes this

Share this post


Link to post
Share on other sites

hello Mademoiselle Cori

Well, I follow to the letter the instructions you posted above. No problem getting the grey maps. I see no problem in creating a new region, copying the proper config.bmp into the region. I use the 'magical formula' to get the grey map. And when accept every thing... Pouf! Nothing happens. I notice this because I get the arrow cursor and not the 'generating' cursor.

Share this post


Link to post
Share on other sites

Are you using GIMP to make your config.bmp? I'd been using my PSE to make them since I know it so well. I only got GIMP to write this tutorial so it'd be a program everyone could have for free. I did, however, make a config.bmp using GIMP the other day and I found that when saving I had to select the compatibility option and select the Do not write color space info option. Is that possibly the root of the problem you are experiencing?

If that's not it, perhaps you could attach your grayscale and config.bmp file and I could take a gander at them.

 

huzman likes this

Share this post


Link to post
Share on other sites

Now that's quick !

I used Gimp for the grey map all the way down to the "export file" with no problem. I use an old PSP for the config.bmp with no problem either. After quitting these programs, I check with irfanview my results and I don't see anything wrong. Then I check the relation between the two bmps (12x12 for the config and 769x769 for the map, fer inztance,) again no problem. As for posting them it will be a little later, since I made so many tries, my comp is peppered with them. But I'll get back to you soon.

Merci very much.

CorinaMarie likes this

Share this post


Link to post
Share on other sites

OK ! I cleaned a bit around my hard disks and here they are; I hope the attached files works. It did. the x files are the bad ones and the + ones are the good ones. Sorry that the maps show opened.

configx.bmp

cartex.jpg

config+.bmp

carte+.jpg

cartex.jpg

carte+.jpg

mrsmartman likes this

Share this post


Link to post
Share on other sites
15 hours ago, huzman said:

OK ! I cleaned a bit around my hard disks and here they are; I hope the attached files works. It did. the x files are the bad ones and the + ones are the good ones. Sorry that the maps show opened.

Alrighty. I believe I have some answers for you, but first: It's ok the pictures show as opened. The forum software recognizes .jpgs and so it decides it really ought to show them as a picture.

Anyhow, I went ahead and started with the first set. (The x ones.) I cleared out my Plugins so what you'll see are vanilla renderings.

That grayscale was actually in RGB format which SC4 does not like. The second one is true grayscale so I know you know about converting them. Prolly, like me, when being a bit flustered it's easy to simply forget one of the steps (or to grab the wrong file for attachment). I could bore you for a couple hours with all the trouble I had from mistakes I made when I first started trying to render maps.

So, I converted the cartex.jpg image to grayscale:

img0130_zpslgijogfg.jpg

 

And renamed configx.bmp to config.bmp. I opened the game into that region and here's what I saw:

img0137_zps8mgv5mrp.jpg

^ That part with the large tile bleeding into a medium along with the missing tiles along the edges seemed odd.

So, the first thing I did was replace the config.bmp with one of my 16x16s that I'd painted all blue just so I could see if it would render. Here's what I got:

RV%2016%20Large_zpsddn2n5sq.jpg

^ That shows me your height map (the grayscale) is fine. The size of 1025x1025 is correct for the 16x16 config.

Next I pulled up your Config.Bmp in my imaging program. Here is an oversized version so we can see it:

h1%20config%20org_zpsasictckc.jpg

I checked and each of your Red, Green, and Blue are perfectly 255 (FF in hex) like they need to be. Good job there.

The problem is as simple as the layout doesn't fully take into account the tile sizes for small, medium, and large tiles. Here's what the game is expecting:

Grid%20-%20Tile%20Sizes_zps3pfy1t2t.jpg

So, the valid parts of your config.bmp are:

h1%20config%20valid%20parts_zpsujosnngo.

^And the spaces I've left white are not allowed to be green because they don't form the full 2x2 needed for a medium tile.

Let's take out the grid lines from the medium and large tiles so it's more apparent how the game sees this:

h1%20config%20valid%20parts2_zpsnlwt2gyl

 

So, I just filled in those white pixels (which were originally green) with red to make a valid config.bmp. (Any of the red that form a 2x2 square could be painted green to make a medium tile, but it needs to be all 4 pixels. Same for 4x4 green could be painted blue to make a large tile.)

h1%20config%20workable_zpsuxxesda2.jpg

 

So now in the game before rendering it looks like this:

RV%202_zpsefetoltb.jpg

 

And after rendering like this:

RV%203_zpsrdouhi50.jpg

 

Also, I started with .jpg for my grayscales too, but later decided I liked the non-compression of .bmp for them. With the .jpg it'll make subtle changes that our eyes won't notice, but the game will.

Let me know if this helps.

 

Share this post


Link to post
Share on other sites

Just in case you want to make life easier, having made your heightmap, you can use a little application, SC4 Mapper to make the Config.bmp for you. The great thing about it is that you simply select where to place L, M and S tiles, it then works it all out for you. Not only that, but since it knows what is and isn't valid, it will always produce an image that works.

 

Share this post


Link to post
Share on other sites

Yeah !

8 hours ago, CorinaMarie said:

That grayscale was actually in RGB format which SC4 does not like.

It never came to mind to check THAT, which it should. Also the white tiles tip is a  great one. A big thank you.

 

7 hours ago, rsc204 said:

you can use a little application, SC4 Mapper

Thanks to you too. I have stayed away from those kind of addons, since I'm learning the ropes of SC4D for the time being. The TUTs in Youtube are a great help as they correspond exactly to what I'm tackling now

CorinaMarie likes this

Share this post


Link to post
Share on other sites
On 24/07/2016 at 1:46 AM, CorinaMarie said:

I noticed is having mods for terrain, rocks, water, and beaches...

I loaded many of them, but I don't know where to place them. I mention this because I have a lousy result with the generating maps : very high coast edges, no beach  slopes,  very little heights inland, very narrow rivers, I could go on but I won't bother with them. Actually, I rather want to find these solutions than examples. Want a Parisian post card ?

Share this post


Link to post
Share on other sites
5 hours ago, huzman said:

I loaded many of them, but I don't know where to place them.

They go in your Plugins folder which is under your SimCity 4 folder in your Documents folder. If you haven't seen them here's picture catalogues of each:

Use only one from each category. One thing I recently discovered is some of them have altered the Terrain Properties exemplar with regard to erosion settings so you might get different results with different terrains installed while rendering.

5 hours ago, huzman said:

very high coast edges, no beach  slopes,

I found using the Burn Tool set to 150 pixel size is good for smoothing out the river banks. (I edited that into my first post a little while back.)

5 hours ago, huzman said:

very little heights inland

The highest you will get with the default ImportScaleFactor of 3 is 765 meters. This setting can be adjusted using iLive's Reader if you want taller hills and mountains. There is a related variable for the maximum terrain height that needs adjusted accordingly if changing the scale.

5 hours ago, huzman said:

very narrow rivers

The width of your rivers are based on the pixel size brush you use to paint them. In my guide I used the default 20 pixels. Try 40 or 60 and see how you like that.

huzman and Handyman like this

Share this post


Link to post
Share on other sites

You did it again ! I needed a glass of water and I was drowned by the avalanche of info here. Thanks a bucket !

It's going to take me a few years to load and install all the mods and their dependencies, but then, I shall be very selective and do a few of them.

CorinaMarie likes this

Share this post


Link to post
Share on other sites

Geological Terrain & Water
I create (or try) to build my gray maps. I'm having trouble with beaches and gently slopes, they are very abrupt and there's land in the water which makes them unsuitable for construction and harbors.
My question is about the different heights of land and likewise for water. How does one go about determining height ?
The screenshots shows these very clearly.

@CorinaMarie tackled the smoothing of beaches with Gimp which are spot on, but my experience with that technique left me a little unsatisfied. My questions here have to do with Heights and how to identify them. I just went to the C.O.R.I.M.A.P.S. thread and most images are not showing. I saw some references to the heights while roaming around, so I presume that there must be some documentations about it. And that would be great.

Image2.jpg

Image1.jpg

mrsmartman likes this

Share this post


Link to post
Share on other sites
1 hour ago, huzman said:

... most images are not showing.

I checked just now and they are back. This is an unfortunate side effect of when my image hosting site (Photobucket) goes goofy. I'll see if I can attach the relevant pic here:

58a9da70486b3_BurnTool.jpg.d55f7620f396cbe4f3e181767b50b4f2.jpg

 

1 hour ago, huzman said:

@CorinaMarie tackled the smoothing of beaches with Gimp which are spot on, but my experience with that technique left me a little unsatisfied.

You could try changing the range of the Burn Tool to Highlights for those steeper banks which are represented by the lighter shades of gray.

 

1 hour ago, huzman said:

My questions here have to do with Heights and how to identify them.

This is a valid question and I confess I went with what others said about the grayscale color number when I said: Providing you have not installed a Height Mod with an alteration to the scale factor and/or sea level, grayscale 83 is slightly below sea level. Lower values are farther under water. Grayscale 84 is slightly above water. Higher values are then higher elevations up to a max of 255 (white).

This could use some further exploration to see if the GIMP grayscale numbers really do correspond correctly.

mrsmartman, APSMS and huzman like this

Share this post


Link to post
Share on other sites

This is good indeed. I am surprised that there are 171 levels of terrain above water !!!???

14 hours ago, CorinaMarie said:

This could use some further exploration to see if the GIMP grayscale numbers really do correspond correctly.

I intend to do just that, not only with GIMP.

Share this post


Link to post
Share on other sites
14 hours ago, CorinaMarie said:

I checked just now and they are back.

Not in my case, sorry to inform.

However, the images from @Yum and @BC Canuck show well.

Share this post


Link to post
Share on other sites
On 2/20/2017 at 3:16 AM, huzman said:

Not in my case, sorry to inform.

However, the images from @Yum and @BC Canuck show well.

Hmm. That's weird. As a test would you please try going to the first post in this thread and doing a Ctrl+F5? That's supposed to force a cache refresh. If that doesn't work, it might be worth trying to manually clear your cache. The second paragraph here has linkys for all major browsers for how to do that.

 

On 2/20/2017 at 2:58 AM, huzman said:

This is good indeed. I am surprised that there are 171 levels of terrain above water !!!???

Yes. Providing you are using the default settings for the Terrain Properties in Simcity_1.dat. Here's what it looks like:

Terrain%20Properties%20Default_zps490b2j

^ The relevant parts are the SeaLevel which is at 250 meters by default.  Then the ImageImportScaleFactor (default is 3) determines how many meters correspond to each grayscale value. When you divide the 250 sea level by that 3 you get 83.3333 which is why 83 is to be slightly below water and 84 is to be above. With a max grayscale value of 255 that makes the maximum height be 255 * 3 = 765 meters. And yes, 255 - 84 = 171 levels above sea level.

There are several other variables which affect how a map is rendered. I once spent a couple hours trying out different settings. I thought my tests proved that none of them changed anything, however, I wasn't aware at that time that the B98_Beach_Extend.dat I had installed is the very same Terrain Properties Exemplar and it was loading after my A Test.dat and therefore overriding everything I tried.

There's a brief explanation of each of those variables on the SC4Devotion Wiki here in case you are curious.

 

On 2/20/2017 at 2:58 AM, huzman said:

I intend to do just that, not only with GIMP.

I look forward to the results of your tests. One thing I learned subsequent to posting this thread is that instead of typing 40 into red, green, and blue one can enter 16 into the Gray Value and it makes those three show 41 (pretty close to 40). For a test I believe trying 83 in there and painting a large section followed by 84 painted next to it would be a good test.

huzman, mrsmartman and Cyclone Boom like this

Share this post


Link to post
Share on other sites

I just finished a first test and right away I can say

On 20/02/2017 at 1:05 PM, CorinaMarie said:

For a test I believe trying 83 in there and painting a large section followed by 84 painted next to it would be a good test.

It ain't good. Too much low level water and unsuitable for harbors. On the terrain side, there is a lot of land that can't be settled.

My test includes 4 levels of water and four levels of terrain. I used increments of 3 but they are hardly visible. About the water,  I tried level 82 but it created a lot of sand. My suggestion is to next to the 83 one should then choose a really deep level like 74 or 77 or more. That's my next task. There's more to it, but like they say, 'To be continued.'

But I still don't know how to up-load a zipped region.

I still don't have the images in @CorinaMarie tutorial in this thread although I can see them in other threads. Are you using that 'Pocket-site' ?

Share this post


Link to post
Share on other sites
8 hours ago, huzman said:

It ain't good. Too much low level water and unsuitable for harbors.

Oops. I should have clarified my test idea wasn't meant to be the solution. What I was suggesting was to see if 83 was unbuildable and slightly below water and 84 was beechy, buildable, and slightly above water.

 

8 hours ago, huzman said:

On the terrain side, there is a lot of land that can't be settled.

I'm guessing with the random erosion and such that might cause some to drop below sea level.  The test, however, is a success if each side is relatively close to what we expect.

 

8 hours ago, huzman said:

My suggestion is to next to the 83 one should then choose a really deep level like 74 or 77 or more. That's my next task. There's more to it, but like they say, 'To be continued.'

Seems like you are definitely on the right track here. I look forward to you continuation.

 

8 hours ago, huzman said:

But I still don't know how to up-load a zipped region.

When I was doing my initial testing I used a single large tile so game load and rendering time would be minimal. If you zip one up that size it it will likely be well under the 4mb limit for an attachment. Then just select the Choose Files... in the lower left below the typing box and browse your comp to select the zip.

 

8 hours ago, huzman said:

I still don't have the images in @CorinaMarie tutorial in this thread although I can see them in other threads. Are you using that 'Pocket-site' ?

This is weird. I'm just using the same regular (free) Photobucket account I created about 4 years ago. I'll browse this thread from work tomorrow as a guest and see if I see my pics or not. I'm curious if you see any of the zillions of pics in my Shoppes threads?

huzman likes this

Share this post


Link to post
Share on other sites
7 hours ago, CorinaMarie said:

 

16 hours ago, huzman said:

I still don't have the images in @CorinaMarie tutorial in this thread although I can see them in other threads. Are you using that 'Pocket-site' ?

This is weird. I'm just using the same regular (free) Photobucket account I created about 4 years ago. I'll browse this thread from work tomorrow as a guest and see if I see my pics or not. I'm curious if you see any of the zillions of pics in my Shoppes threads?

I can see all images very well. 

CorinaMarie likes this

Share this post


Link to post
Share on other sites
2 hours ago, huzman said:

Well yes. Thanks to the tips from @CorinaMarie I just loaded them. Look at the time and date info.

Am I understanding this right? You did the Ctrl-F5 or cleared your cache or something and now all the pics in this thread show for you?

 

I downloaded and opened up your region. Here's a topographical map I made of it:

Nourvelle%20Topo_zpsuqz3cjkn.jpg

^ The seams between the 4 city tiles are not perfect, but it does give a pretty good overview of your whole region. I presume these are the step increments from like 74 to 92 grayscale?

When I made one for my farm region I drew grid lines to hide the seams:

Spoiler

Topo%20Map_zpseb5alxeh.jpg

 

huzman and mrsmartman like this

Share this post


Link to post
Share on other sites

I'm sorry to say that I still don't get any images. (I suspect that Pocket site). Your 'Shoppes' are off too. I did the F5 trick. No luck. I used CCleaner and got rid of ALL caches, and everything it does which is lot. No luck. Now, in other sites or even other threads here, everything works.

The seams are voluntary.

So back to this thread : I'm working on 83-84 thing. Forget doing 82. In fact, When leveling a coast with the leveling tool in Divine Mod, I got rid of those 82,83 and such and I was able to build all the 3 harbors available and settling houses was good enough. I haven't finished that region so 'Stay tuned, coming soon !'

Share this post


Link to post
Share on other sites
7 hours ago, huzman said:

I'm sorry to say that I still don't get any images.

Well, phooey. I'm out of ideas. I did view this thread as a guest from work today and all the pics showed. That tells me neither ST nor PB is the culprit.

 

7 hours ago, huzman said:

(I suspect that Pocket site).

(I don't even know what that is.)

 

7 hours ago, huzman said:

I haven't finished that region so 'Stay tuned, coming soon !'

I'll be waiting.

huzman likes this

Share this post


Link to post
Share on other sites
8 hours ago, huzman said:

Your 'Shoppes' are off too. I did the F5 trick. No luck. I used CCleaner and got rid of ALL caches, and everything it does which is lot. No luck. Now, in other sites or even other threads here, everything works.

Sounds like an ISP issue to me; either that or an overactive anti-spyware program blocking the IP address of the host.

I'd try looking at these threads at an alternate location, either a library or school away from your home. Alternatively but requiring more setup, you could try a proxy server, but I'd save that for later unless you're familiar with them, because even the free web-based ones are often more work than they're worth.

Other test would be trying to load the images directly, either by right-clicking "view image" or seeing if you can access normal photobucket.com period, away from ST.

CorinaMarie, rsc204 and huzman like this

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!  :thumb:


Register a New Account

Sign In  

Already have an account? Sign in here.


Sign In Now


  •  
  • Similar Content

    • By DJDL
      Hello Simtropolis Community!
      I am looking to buy a new Windows laptop and I would appreciate your input.  Along with the usual Office functions, my goal will be gaming (SC4, possibly Civ 3 and eventually Cities Skylines) and graphics/dtp (Adobe).
      These are the machines (all Dell) that I am considering. and I'm hoping to purchase a new machine this weekend, since there's a sale going on.
      Inspiron 5000 Gaming with Win10 Pro
      7th Generation Intel® Core™ i7-7700HQ Quad Core (6MB Cache, up to 3.8 GHz) 8GB, 2400MHz, DDR4; up to 32GB NVIDIA® GeForce® GTX 1050 with 4GB GDDR5 128GB Solid State Drive (Boot) + 1TB 5400 rpm Hard Drive (Storage) 15.6-inch FHD (1920 x 1080) Anti-Glare LED-Backlit Display 74WHr, 6-Cell Battery (Integrated) "optimal cooling" Ports 1 HDMITM 1.4a
      3 USB 3.0 including 1 with PowerShare
      1 2-in-1 SD (UHS50) / MMC
      1 RJ-45
      1 Kensington Lock
      1 Headphone/Mic Inspiron 15 7000 Gaming with Win 10 Pro
      7th Generation Intel® Core™ i5-7300HQ Quad Core (6MB Cache, up to 3.5 GHz) or 17-7700HQ Quad, 6MB up to 3.8GHz)  8GB, 2400MHz, DDR4; up to 32GB or 16GB, 2400MHz, DDR4; up to 32GB 256GB Solid State Drive or 128GB Solid State Drive (Boot) + 1TB 5400RPM Hard Drive (Storage) 15.6-inch FHD (1920 x 1080) IPS Anti-Glare LED-Backlit Display NVIDIA® GeForce® GTX 1050Ti with 4GB GDDR5 74 Whr, 6-Cell Battery (Integrated) Ports and Slots: Combo Jack (Headset/Mic, USB 3.0 port, USB 3.0 Powershare port, HDMI, Gigabit Ethernet, Noble Lock Security Slot, Power, 8.USB 3.0 port, Media Card Reader front-firing speakers, a subwoofer and Waves MaxxAudio® Pro. maximum cooling feature Inspiron 17 7000 2in1 with Win 10 Pro--I'm considering two machines which share the following specs
      7th Generation Intel® Core™ i7-7500U Processor (4M Cache, up to 3.5 GHz) 16GB, DDR4, 2400MHz NVIDIA® GeForce® GTX 940MX 2GB GDDR5 17.3-inch FHD (1920 x 1080) Truelife LED-Backlit Touch Display with Wide Viewing Angles 56 WHr, 4-Cell Battery (integrated) The differences are the HD and port/slots: 1TB 5400 rpm Hard Drive; Ports:   SD, SDHC, SDXC Card Reader (specified), USB 2.0, Noble Lock Slot,  DC Power In, USB Type C, HDMITM 1.4a (specified), USB 3.0 with PowerShare, Audio Jack  1TB 5400 rpm Hard Drive + 128GB Solid State Drive (for about $40 more); Ports:  SD Card Reader, USB 2.0, Noble Lock Slot, DC Power In, USB Type C, HDMI, USB 3.0 with PowerShare,. Audio Jack XPS 15 with Win 10 Pro (priciest)
      7th Generation Intel® Core™ i7-7700HQ Quad Core Processor (6M cache, up to 3.8 GHz) 16GB DDR4-2400MHz; up to 32GB (additional memory sold separately) 512GB PCle Solid State Drive NVIDIA® GeForce® GTX 1050 with 4GB GDDR5 15.6" FHD (1920 x 1080) InfinityEdge, Non-touch Killer 1535 802.11ac 2x2 WiFi and Bluetooth 4.1 Battery: 97WHr Ports: SD card slot, USB 3.0 with PowerShare, Battery gauge button and indicator, Kensington lock slot, AC power, HDMI, Thunderbolt™ 3 (2 lanes of PCI Express Gen 3) supporting: Power in/charging, PowerShare, Thunderbolt 3 (40Gbps bi-directional), USB 3.1 Gen 2 (10Gbps), Native DisplayPort 1.2 video output, VGA, HDMI, Ethernet and USB-A via Dell Adapter (sold separately) | 8. Headset jack  (I'm not planning on buying the Dell Adapter) I like aspects of each machine, bigger screen for one, SSD, cooling feature, (SC4 and flash games are dangerously hot on my present machine), and graphics card.  I just wish they all came on one machine.  I would consider upgrading my existing machine but the motherboard on my TOSH Satellite is pretty archaic by today's standards so much so that a previous Win 10 update knocked out the functionality of my native keyboard.  I got a new one and it has the same issue, so I'm using a wireless one after breaking a USB keyboard.
      Thank you all for your help!
      Diana
    • By Silur
      I'm curious to know Your opinion about the website Simtropolis. Today I started editing one site in my country. And I realized that this is far from the quality that I see on Simtropolis. How do You compare the quality of most of the other sites? Are there any sites built even more conveniently?  Are there any ideals in this world? Thank you.
    • By boformer
      In this tutorial, we will replicate the functionality of the "Make Historical" checkbox in SimCity 4 (stop building from leveling up). The mod will utilize the same interfaces as the Control Building Level Up mod. You will learn how to inspect and modify user interfaces with ModTools, how the info panels of the game work, how to add a checkbox to the UI, how to control building level up with the official API, how to get notified when a building is added/removed and how to serialize custom data in the savegame. Once again, we will use the loading and threading hooks provided by the API.

       
      Step 1: Project Setup
      Create a new project named "MakeHistorical" 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 MakeHistorical { public class MakeHistoricalMod : IUserMod { public string Name => "Make Historical"; public string Description => "Prevents the level up of buildings"; } }  
      Step 2: Data Storage with Serialization Hook
      Data Structure
      We will store the ids (ushort) of the historical building instances in the savegame. Like in Tutorial 2 Step 4, we will create a data class that holds our data:
      using System.Collections.Generic; namespace MakeHistorical { public class MakeHistoricalData { public List<ushort> HistoricalBuildingIds { get; set; } = new List<ushort>(); } } The game requires additional savegame data to be in byte[] (byte array) format (basically zeros and ones, very low level). Luckily, Colossal Order provides a few useful tools to convert data classes to byte arrays (package ColossalFramework.IO). To use these tools, our data class must implement the IDataContainer interface.
      Extend the class like this:
      using System.Collections.Generic; using System.Linq; using ColossalFramework.IO; namespace MakeHistorical { public class MakeHistoricalData : IDataContainer { // The key for our data in the savegame public const string DataId = "MakeHistorical"; // Version of data save format // This is important when you add new fields to MakeHistoricalData public const int DataVersion = 0; public List<ushort> HistoricalBuildingIds { get; set; } = new List<ushort>(); // This serializes the object (to bytes) public void Serialize(DataSerializer s) { // convert ushort list to int array int[] ids = HistoricalBuildingIds.Select(id => (int)id).ToArray(); s.WriteInt32Array(ids); } // This reads the object (from bytes) public void Deserialize(DataSerializer s) { int[] ids = s.ReadInt32Array(); // convert int array to ushort list HistoricalBuildingIds = ids.Select(id => (ushort)id).ToList(); } // Validates that all building ids are active public void AfterDeserialize(DataSerializer s) { if (!BuildingManager.exists) return; List<ushort> validatedBuildingIds = new List<ushort>(); Building[] buildingInstances = BuildingManager.instance.m_buildings.m_buffer; // itertate through all building ids, filter active ids foreach (ushort buildingId in HistoricalBuildingIds) { if (buildingInstances[buildingId].m_flags != Building.Flags.None) { validatedBuildingIds.Add(buildingId); } } HistoricalBuildingIds = validatedBuildingIds; } } } What happened?
      We added the constants DataId and DataVersion. We will use these in the next step We implemented the Serialize method: In this method, we convert our list of ushort numbers to an integer array (the serializer does not support ushort) and pass it to a DataSerializer, which converts our array to bytes. We implemented the Deserialize method, which does the opposite of Serialize We implemented AfterDeserialize, which validates that all deserialized building ids belong to buildings (important when the mod was disabled for some time, to keep the data clean) Serialization System
      Colossal Order provides a serialization hook for mods (ISerializableDataExtension/SerializableDataExtensionBase). This hook is invoked when the savegame data is loaded, and when the user presses the save button. The game also provides a storage interface (ISerializableData) that allows us to write byte arrays to savegame and read byte arrays from savegame.
      Combined with the data class and the byte array conversion tools provided by Colossal Order, we will use this hook to save our data. Add a new MakeHistoricalDataManager class:
      using ColossalFramework.IO; using ICities; using System.IO; using UnityEngine; namespace MakeHistorical { public class MakeHistoricalDataManager : SerializableDataExtensionBase { // The data object of our mod private MakeHistoricalData _data; public override void OnLoadData() { // Get bytes from savegame byte[] bytes = serializableDataManager.LoadData(MakeHistoricalData.DataId); if (bytes != null) { // Convert the bytes to MakeHistoricalData object using (var stream = new MemoryStream(bytes)) { _data = DataSerializer.Deserialize<MakeHistoricalData>(stream, DataSerializer.Mode.Memory); } Debug.LogFormat("Data loaded (Size in bytes: {0})", bytes.Length); } else { _data = new MakeHistoricalData(); Debug.Log("Data created"); } } public override void OnSaveData() { byte[] bytes; // Convert the MakeHistoricalData object to bytes using (var stream = new MemoryStream()) { DataSerializer.Serialize(stream, DataSerializer.Mode.Memory, MakeHistoricalData.DataVersion, _data); bytes = stream.ToArray(); } // Save bytes in savegame serializableDataManager.SaveData(MakeHistoricalData.DataId, bytes); Debug.LogFormat("Data saved (Size in bytes: {0})", bytes.Length); } } } What happens here?
      The _data property stores our mod data The OnLoadData method attempts to read the saved byte array from the savegame. If the array was found, it converts the bytes to a MakeHistoricalData object (our data structure). If no array was found, it creates a new MakeHistoricalData object (new city or mod enabled for the first time). The OnSaveData method converts the MakeHistoricalData object to bytes, then saves the bytes in the savegame. Tip: To understand how data versioning works, look at this example data manager.
      Singleton
      To make the data readable from other classes, we will make MakeHistoricalDataManager implement a simple singleton pattern. We willl also add a few methods to lookup, add and remove building ids:
      // Singleton getter public static MakeHistoricalDataManager Instance { get; private set; } public bool IsHistorical(ushort buildingId) { return _data.HistoricalBuildingIds.Contains(buildingId); } public void AddBuildingId(ushort buildingId) { if (_data.HistoricalBuildingIds.Contains(buildingId)) return; _data.HistoricalBuildingIds.Add(buildingId); Debug.Log($"Historical Building {buildingId} added"); } public void RemoveBuildingId(ushort buildingId) { if (!_data.HistoricalBuildingIds.Contains(buildingId)) return; _data.HistoricalBuildingIds.Remove(buildingId); Debug.Log($"Historical Building {buildingId} removed"); } public override void OnCreated(ISerializableData serializedData) { base.OnCreated(serializedData); Instance = this; // initialize singleton } public override void OnReleased() { Instance = null; // reset singleton } Now we can use a simple statement to check if a building is historical:
      bool h = MakeHistoricalDataManager.Instance.IsHistorical(1234); Final code of the MakeHistoricalDataManager class.
       
      Step 3: Removing destroyed buildings from the list
      We have to remove buildings from the list when they are buldozed. Luckily CO added a hook just for that (not documented in the wiki):
      using ICities; using UnityEngine; namespace MakeHistorical { public class MakeHistoricalBuildingMonitor : BuildingExtensionBase { public override void OnBuildingReleased(ushort id) { if (MakeHistoricalDataManager.Instance.IsHistorical(id)) { // Remove demolished/destroyed buildings from the list of historical buildings MakeHistoricalDataManager.Instance.RemoveBuildingId(id); } } } } Tip: With the IBuildingExtension/BuildingExtensionBase hook, you can also manipulate which buildings are spawned, and there are hooks for building creation and relocation.
       
      Step 4: Inspecting And Manipulating User Interfaces with ModTools and ILSpy
      Like in SimCity 4, we will add a "Make Historical" checkbox to the building info window:

      To do that, we have to find out how to access the info window programmatically.
      ModTools supports us with its "debug view". You can toggle it with CTRL + R. Then move your mouse over the info panel (so that the tint of the panel changes to green) and press CTRL + F:

      The UI window component is now displayed in the ModTools scene explorer. The class name of the element is ZonedBuildingWorldInfoPanel.
      In the scene explorer, we can change properties like the colour, size and position of the window. We can also look at the child UI elements (buttons, text labels). The position of the child elements is determined by the relativeLayout property.
      Noted values:
      Height: 321 Left Padding: 14 (based on the child element positions) Bottom Padding: 27 (based on the child element positions) Now that we know the class name, we can locate the class in ILSpy to find out how to access it programmatically:

      ZonedBuildingWorldInfoPanel is a subclass of WorldInfoPanel, which contains a static Show<>() method:
      // WorldInfoPanel (taken from ILSpy) public static void Show<TPanel>(Vector3 worldMousePosition, InstanceID instanceID) where TPanel : WorldInfoPanel { TPanel tPanel = UIView.library.Show<TPanel>(typeof(TPanel).Name, false); tPanel.SetTarget(worldMousePosition, instanceID); tPanel.component.opacity = 1f; } After further investigation (use ILSpy's "Analyze" feature), you will find a Get<>() method in UIView.library which returns the ZonedBuildingWorldInfoPanel instance:
      var panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); Note: There is only one panel instance, which is just filled with the data of the currently selected building. The panel even exists when no building is selected, it's just hidden.
       
      Step 5: Adding a Checkbox to the Info Panel
      Finding the right property values for user interfaces is a finicky task, especially when you have to restart the game after every little change. Using the ModTools console, we can add and modify our checkbox while the game is running. Run this script in the ModTools console (F7):
      var panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); var checkBox = panel.component.AddUIComponent<UICheckBox>(); checkBox.width = panel.component.width; checkBox.height = 20f; checkBox.clipChildren = true; UISprite sprite = checkBox.AddUIComponent<UISprite>(); sprite.spriteName = "ToggleBase"; sprite.size = new Vector2(16f, 16f); sprite.relativePosition = Vector3.zero; checkBox.checkedBoxObject = sprite.AddUIComponent<UISprite>(); ((UISprite)checkBox.checkedBoxObject).spriteName = "ToggleBaseFocused"; checkBox.checkedBoxObject.size = new Vector2(16f, 16f); checkBox.checkedBoxObject.relativePosition = Vector3.zero; checkBox.label = checkBox.AddUIComponent<UILabel>(); checkBox.label.text = " "; checkBox.label.textScale = 0.9f; checkBox.label.relativePosition = new Vector3(22f, 2f); checkBox.name = "MakeHistorical"; checkBox.text = "Make Historical"; This will add everything that is required for a checkbox, giving it the name "MakeHistorical". It is still in the wrong position:

      Tip: SamsamTS created a very useful utility class that allows you to create various UI elements. The code above was copied from that class.
       
      With ModTools, we can experiment with the position without restarting the game. Execute this code in the ModTools console:
      var panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); var checkBox = panel.component.Find<UICheckBox>("MakeHistorical"); checkBox.relativePosition = new Vector3(14f, 164f + 130f + 5f); Result:

       
      Now we just have to increase the height of the window a little bit. Execute this code in the ModTools console:
      var panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); panel.component.height = 321f + 5f + 20f; Result:

       
      Now, we will put the code we just executed with the console into the loading hook of our mod:
      using ColossalFramework.UI; using ICities; using UnityEngine; namespace MakeHistorical { public class MakeHistoricalLoading : LoadingExtensionBase { private UICheckBox _makeHistoricalCheckBox; public override void OnLevelLoaded(LoadMode mode) { if (_makeHistoricalCheckBox != null) return; ZonedBuildingWorldInfoPanel panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); UICheckBox checkBox = panel.component.AddUIComponent<UICheckBox>(); checkBox.width = panel.component.width; checkBox.height = 20f; checkBox.clipChildren = true; UISprite sprite = checkBox.AddUIComponent<UISprite>(); sprite.spriteName = "ToggleBase"; sprite.size = new Vector2(16f, 16f); sprite.relativePosition = Vector3.zero; checkBox.checkedBoxObject = sprite.AddUIComponent<UISprite>(); ((UISprite)checkBox.checkedBoxObject).spriteName = "ToggleBaseFocused"; checkBox.checkedBoxObject.size = new Vector2(16f, 16f); checkBox.checkedBoxObject.relativePosition = Vector3.zero; checkBox.label = checkBox.AddUIComponent<UILabel>(); checkBox.label.text = " "; checkBox.label.textScale = 0.9f; checkBox.label.relativePosition = new Vector3(22f, 2f); checkBox.name = "MakeHistorical"; checkBox.text = "Make Historical"; checkBox.relativePosition = new Vector3(14f, 164f + 130f + 5f); panel.component.height = 321f + 5f + 16f; _makeHistoricalCheckBox = checkBox; } } }
      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, open the building info window for a growable building. Et voila:

       
      Step 6: Processing Checkbox Events
      To process checkbox events, we will add a delegate to the CheckedChange event:
      public override void OnLevelLoaded(LoadMode mode) { ... checkBox.eventCheckChanged += (component, check) => { ushort buildingId = WorldInfoPanel.GetCurrentInstanceID().Building; if (check) { MakeHistoricalDataManager.Instance.AddBuildingId(buildingId); } else { MakeHistoricalDataManager.Instance.RemoveBuildingId(buildingId); } }; } Clicking the checkbox now triggers the data manager:

       
      Step 7: Monitoring the ZonedBuildingWorldInfoPanel with a Threading Hook
      There is another problem: The checkbox is not updated when you select a building. We will use a threading hook to update the checkbox:
      using ColossalFramework.UI; using ICities; namespace MakeHistorical { public class MakeHistoricalPanelMonitor : ThreadingExtensionBase { private ZonedBuildingWorldInfoPanel _panel; private UICheckBox _makeHistoricalCheckBox; private ushort _lastBuildingId = 0; // called every frame public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) { if (!FindComponents()) return; if (_panel.component.isVisible) { ushort buildingId = WorldInfoPanel.GetCurrentInstanceID().Building; if (_lastBuildingId != buildingId) { // display the right checkbox state _makeHistoricalCheckBox.isChecked = MakeHistoricalDataManager.Instance.IsHistorical(buildingId); _lastBuildingId = buildingId; } } else { _lastBuildingId = 0; } } private bool FindComponents() { if (_panel != null && _makeHistoricalCheckBox != null) return true; _panel = UIView.library.Get<ZonedBuildingWorldInfoPanel>(typeof(ZonedBuildingWorldInfoPanel).Name); if (_panel == null) return false; _makeHistoricalCheckBox = _panel.component.Find<UICheckBox>("MakeHistorical"); return _makeHistoricalCheckBox != null; } } }  
      Step 8: Controlling Building Level Up
      It took quite some time to get to this point (UI and serialization are always very time consuming). The last missing piece is the ILevelUpExtension/LevelUpExtensionBase hook, which monitors and manipulates the level up behaviour of buildings. Probably the least complicated part of the mod:
      using ICities; namespace MakeHistorical { public class MakeHistoricalLevelUpMonitor : LevelUpExtensionBase { public override ResidentialLevelUp OnCalculateResidentialLevelUp(ResidentialLevelUp levelUp, int averageEducation, int landValue, ushort buildingID, Service service, SubService subService, Level currentLevel) { if (MakeHistoricalDataManager.Instance.IsHistorical(buildingID)) { levelUp.targetLevel = currentLevel; } return levelUp; } public override CommercialLevelUp OnCalculateCommercialLevelUp(CommercialLevelUp levelUp, int averageWealth, int landValue, ushort buildingID, Service service, SubService subService, Level currentLevel) { if (MakeHistoricalDataManager.Instance.IsHistorical(buildingID)) { levelUp.targetLevel = currentLevel; } return levelUp; } public override IndustrialLevelUp OnCalculateIndustrialLevelUp(IndustrialLevelUp levelUp, int averageEducation, int serviceScore, ushort buildingID, Service service, SubService subService, Level currentLevel) { if (MakeHistoricalDataManager.Instance.IsHistorical(buildingID)) { levelUp.targetLevel = currentLevel; } return levelUp; } public override OfficeLevelUp OnCalculateOfficeLevelUp(OfficeLevelUp levelUp, int averageEducation, int serviceScore, ushort buildingID, Service service, SubService subService, Level currentLevel) { if (MakeHistoricalDataManager.Instance.IsHistorical(buildingID)) { levelUp.targetLevel = currentLevel; } return levelUp; } } } The logic is very simple: When the building is historical, the target level is set to the current building level.
      And we are done! Buildings with the "Make Historical" checkbox enabled will no longer level up. The list of historical buildings is stored in the savegame.

      Happy Coding!
      Download Source
    • By boformer
      Right now I only have little time, and I'm not able to write new mods. But I want to share my knowlege about Cities: Skylines mod development with you!
      Mod Development Tutorials
       
    • By boformer
      In this tutorial, we will replicate the functionality of the Show Limits mod. Like in Tutorial 1, we will use the default alert window to display the limits. You will learn how to use ILSpy for reverse engineering, how to access singleton manager objects, how to work with building/tree instances and how to listen to keyboard shortcuts.
       
      Step 1: Exploring the game source code in ILSpy
      Download the latest version of ILSpy (select "Download Binaries"). ILSpy is an open source decompiler that allows you to take a look at the source code of the game and other mods. It's basically the opposite of a compiler:
      [compiled .dll file] → [DECOMPILER] → [.cs files containing raw C# code] The restored source code is not exactly the same as the original code. Names of variables defined in methods are lost, which makes the code harder to read. Some parts of the loading code are not readable (enumerators), but that won't bother us.
      Open the Assembly-CSharp.dll file in ILSpy (C:\Program Files (x86)\Steam\steamapps\common\Cities_Skylines\Cities_Data\Managed). On the left, expand the source tree:

      The tree contains all classes and structs of the game. Select one of them to view the source code. There are different types of classes, here are the most common ones:
      ...Info: Prefabs or other assets (see Tutorial 1 and 2) Building, Citizen, District, NetLane, NetNode, NetSegment, PropInstance, TransportLine, TreeInstance, Vehicle, ZoneBlock (structs): Instances of objects, managed by a simulation system. There is a limit how many instances of a certain type can exist. ...AI: AI behaviour attached to prefabs, calculating the behavior of object instances. ...Manager: A simulation system,  responsible for rendering, simulation and creation/deletion of instances ...Tool: Tool for interacting with the city ...Collection: A collection of prefabs or assets ...Properties: Properties for simulation systems, like colors, shaders and constants ...Panel: UI elements ...Wrapper: Implementation of the official modding API  
      The various manager classes are the key components of the game. Managers extend the Singleton<> class. The singleton pattern is a software design pattern that ensures that only one instance of a class exists. You can easily access a singleton in Cities: Skylines with the static instance field:
      string cityName = SimulationManager.instance.m_metaData.m_CityName; Debug.Log("Name of the city: " + cityName); Alternatively:
      Singleton<SimulationManager>.instance Tip: You can run these code snippets with the ModTools console (F7)
       
      Today we will take a look at the TreeManager and BuildingManager to display the number of trees and buildings.
      Tree Manager
      Select the TreeManager in ILSpy. After scrolling down a few lines in the source code, you will find the constant MAX_TREE_COUNT and the integer field m_treeCount:

      Outputting these numbers will lead to the desired result. We will go a step further and check how the m_treeCount field is calculated. Right click the m_treeCount item in the left sidebar and select "Analyze":

      The panel at the bottom displays which methods are reading the field, and which methods are assigning values to the field. For example, the AssetEditorChirpPanel and EditorChirpPanel are reading the field to display the number of trees in the asset/map editor.
      The field is modified by 3 methods of the TreeManager:
      CreateTree: When a new tree instance is placed, increment the value by 1. ReleaseTreeImplementation: When a tree instance is deleted, decrement the value by 1 Data.AfterDeserialize: After a save game is loaded, recalculate the tree count The last method is particularly interesting. Double click it to view the source code of the method:

      The method calls TreeManager.instance.m_trees.ItemCount() to calculate the number of trees.
      m_trees is a special kind of array list (Array16<TreeInstance>) that is used by the game to manage instances of trees, props, buildings etc. The list contains a fixed number of TreeInstance structs (262144 in case of trees). When a new tree is created, one of the unused items from the listt is activated and used to describe the state, position and type of a tree. A few lines above you can see how the game iterates through the list to modify the active items.
      Tip: You can also use the "Analyze" feature on methods and classes. It will help you to understand how the different components of the game are connected.
      Building Manager
      Now, select the BuildingManager on the left. You will notice that the BuildingManager contains the same kind of fields:
      const int MAX_BUILDING_COUNT: The max number of building instances int m_buildingCount: The current number of building instances Array16<Building> m_buildings: The array list containing the building instances  
      Step 2: Outputting the limits with ModTools
      Before writing our mod, we will create a ModTools script to see if everything works:
      int treeCount = TreeManager.instance.m_treeCount; int maxTreeCount = TreeManager.MAX_TREE_COUNT; Debug.Log("Number of trees: " + treeCount + " of " + maxTreeCount); int buildingCount = BuildingManager.instance.m_buildingCount; int maxBuildingCount = BuildingManager.MAX_BUILDING_COUNT; Debug.Log("Number of buildings: " + buildingCount + " of " + maxBuildingCount); Load or create a city and open the ModTools console (F7). In the bottom of the console, paste the script and press the "Run" button:

       
      Step 3: Project Setup
      Create a new project named "ShowLimits" 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 ShowLimits { public class ShowLimitsMod : IUserMod { public string Name => "Show Limits"; public string Description => "Displays the number of buildings and trees in your city (Hotkey CTRL + I)"; } }  
      Step 4: Threading Hook & Hotkeys
      The IThreadingExtension/ThreadingExtensionBase is another hook provided by the official modding API. Classes implementing IThreadingExtension are invoked before, during and after every simulation tick (many times per second). It is important that the contained code is very light-weight (no IO operations, like reading configuration files, no object construction...).
      We will use the hook to check if the player is pressing they keyboard shortcut (CTRL + I).
      Create a new class called ShowLimitsThreading:
      using ColossalFramework.UI; using ICities; using UnityEngine; namespace ShowLimits { public class ShowLimitsThreading : ThreadingExtensionBase { private bool _processed = false; public override void OnUpdate(float realTimeDelta, float simulationTimeDelta) { if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) && Input.GetKey(KeyCode.I)) { // cancel if they key input was already processed in a previous frame if (_processed) return; _processed = true; // compose message int treeCount = TreeManager.instance.m_treeCount; int maxTreeCount = TreeManager.MAX_TREE_COUNT; int buildingCount = BuildingManager.instance.m_buildingCount; int maxBuildingCount = BuildingManager.MAX_BUILDING_COUNT; string message = $"Trees: {treeCount} of {maxTreeCount}\nBuildings: {buildingCount} of {maxBuildingCount}"; // display message ExceptionPanel panel = UIView.library.ShowModal<ExceptionPanel>("ExceptionPanel"); panel.SetMessage("Show Limits", message, false); } else { // not both keys pressed: Reset processed state _processed = false; } } } }
      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. Press Ctrl + I:

      Happy Coding!
      Download Source
       
      Next Part:
       
  • Recently Browsing   0 members

    No registered users viewing this page.