Coding Tutorial: Monthly Costs Per Track Tile
Moderator: OpenTTD Developers
Coding Tutorial: Monthly Costs Per Track Tile
hello together!
i'm looking for a openttd coding tutorial. i mean a tutorial that helps people which have successfully set up a development environment with mingw, msys, a code editor etc and are already able to compile openttd as well as apply patches.
i need a tutorial that shows me the structure of the openttd source code step by step and explains me with some examples how to change things in code, i.e. an example that shows how to change vehicle speeds, ingame menus etc. so that a new openttd developer has the chance to get closer to the code to understand it.
in my case, i want to develop a patch that counts all railway tracks of a player and generates maintenance costs of 10 dollars per month and per track. is there anybody, that already implemented something like that and can tell me which source code files to change?
side notice: im a php, c#, vb, java etc. coder for many years. i started c++ about one year ago and have read several books on this subject.
thanks for your help! i love openttd and want to help to make it even better.
smallfly
i'm looking for a openttd coding tutorial. i mean a tutorial that helps people which have successfully set up a development environment with mingw, msys, a code editor etc and are already able to compile openttd as well as apply patches.
i need a tutorial that shows me the structure of the openttd source code step by step and explains me with some examples how to change things in code, i.e. an example that shows how to change vehicle speeds, ingame menus etc. so that a new openttd developer has the chance to get closer to the code to understand it.
in my case, i want to develop a patch that counts all railway tracks of a player and generates maintenance costs of 10 dollars per month and per track. is there anybody, that already implemented something like that and can tell me which source code files to change?
side notice: im a php, c#, vb, java etc. coder for many years. i started c++ about one year ago and have read several books on this subject.
thanks for your help! i love openttd and want to help to make it even better.
smallfly
Last edited by smallfly on 19 Oct 2009 15:37, edited 1 time in total.
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Is there a OpenTTD Coding Tutorial?
I can't help but i can said that idea is great!
Airports also need (high) upkeep cost.
Airports also need (high) upkeep cost.
Correct me If I am wrong - PM me if my English is bad
AIAI - AI for OpenTTD
AIAI - AI for OpenTTD
Re: Is there a OpenTTD Coding Tutorial?
Well, afaik there's no such thing as a tutorial on that - what would you write in such a tutorial? You can change everything, so a tutorial on how to change things in the OpenTTD code could only cover a few things.
But as you seem to be an experienced programmer and not a newbie that has never seen code before I guess you'll find out yourself where to look for what in the code. The source files are all named pretty descriptive.. you can also look at the Doxygen Documentation. Also, be sure to follow the Coding Style.
More various information about certain aspects of coding in OpenTTD: http://wiki.openttd.org/Development_Documentation
To the examples you mentioned:
Changing the vehicle speeds by hardcoding is discouraged, because that's something exposed to the NewGRF system. Anyone (who's willing to learn nfo coding) can create a newGRF that changes the speed of an existing vehicle to something else, or define a new vehicle with any stats he likes.
Generally, stuff that's exposed to newGRF should not be changed by means of hardcoding.
Ingame windows
For those, look at the *_gui.cpp files. Currently a new window system is being worked on that defines windows in a nested manner, instead of telling pixel by pixel where what goes (the old system). This will hopefully make it more flexible wrt. different fonts, localizations, resizing etc.
I didn't find information on the new window system right now. Some (probably outdated) info on the old window system is found here.
But as you seem to be an experienced programmer and not a newbie that has never seen code before I guess you'll find out yourself where to look for what in the code. The source files are all named pretty descriptive.. you can also look at the Doxygen Documentation. Also, be sure to follow the Coding Style.
More various information about certain aspects of coding in OpenTTD: http://wiki.openttd.org/Development_Documentation
To the examples you mentioned:
Changing the vehicle speeds by hardcoding is discouraged, because that's something exposed to the NewGRF system. Anyone (who's willing to learn nfo coding) can create a newGRF that changes the speed of an existing vehicle to something else, or define a new vehicle with any stats he likes.
Generally, stuff that's exposed to newGRF should not be changed by means of hardcoding.
Ingame windows
For those, look at the *_gui.cpp files. Currently a new window system is being worked on that defines windows in a nested manner, instead of telling pixel by pixel where what goes (the old system). This will hopefully make it more flexible wrt. different fonts, localizations, resizing etc.
I didn't find information on the new window system right now. Some (probably outdated) info on the old window system is found here.
Monthly Track Maintenance Costs
First of all thanks for your fast replies!
To get more specific, lets make this thread a tutorial on how to implement a patch, that realizes monthly costs for tracks or other player related things. I think this is an option that will find its way to the trunk, because there are several players out there, wishing to config values like:
- Monthly costs per track tile
- Monthly costs per airport
- Monthly costs per railway station tile
etc.
And if these cost values are set to "0" by default, but can be set to any value in "patch settings" in game then no one has any reason to prevent this patch from getting in trunk.
So ill make a start and because i have no idea of openttd development i appreciate your help by giving specific hints (real source code not just a suggestion)
We need a new expense type:
- EXPENSES_TRACK_RUN
or to make it easier (not defining a new type), we just use the type EXPENSES_TRAIN_RUN.
We need a place in source code that is called periodically, lets say every month and a algorythm that walks through all track tiles and multiplies the sum of all tiles by a factor like MONTHLY_COST_PER_TRACK_TILE.
For this purpose we look at "date.cpp" there we find a call of the method "CompaniesMonthlyLoop()" which is called, when a new month is entered. The method itself is in "economy.cpp" where we go now. Above the line "CompaniesPayInterest();" in the method "CompaniesMonthlyLoop" we insert a new line called "CompaniesPayMonthlyTrackFees()" and outside the method we create a new method:
So guys. That was a quick thought. Please correct me and call me stupid 

To get more specific, lets make this thread a tutorial on how to implement a patch, that realizes monthly costs for tracks or other player related things. I think this is an option that will find its way to the trunk, because there are several players out there, wishing to config values like:
- Monthly costs per track tile
- Monthly costs per airport
- Monthly costs per railway station tile
etc.
And if these cost values are set to "0" by default, but can be set to any value in "patch settings" in game then no one has any reason to prevent this patch from getting in trunk.
So ill make a start and because i have no idea of openttd development i appreciate your help by giving specific hints (real source code not just a suggestion)
We need a new expense type:
- EXPENSES_TRACK_RUN
or to make it easier (not defining a new type), we just use the type EXPENSES_TRAIN_RUN.
We need a place in source code that is called periodically, lets say every month and a algorythm that walks through all track tiles and multiplies the sum of all tiles by a factor like MONTHLY_COST_PER_TRACK_TILE.
For this purpose we look at "date.cpp" there we find a call of the method "CompaniesMonthlyLoop()" which is called, when a new month is entered. The method itself is in "economy.cpp" where we go now. Above the line "CompaniesPayInterest();" in the method "CompaniesMonthlyLoop" we insert a new line called "CompaniesPayMonthlyTrackFees()" and outside the method we create a new method:
Code: Select all
static void CompaniesPayMonthlyTrackFees()
{
const Company *c;
FOR_ALL_COMPANIES(c) {
_current_company = c->index;
// count all track tiles of the current company
int numberOfTrackTiles;
numberOfTrackTiles = 0;
foreach (track in tracks)
{
if (track->companyOfThatTrack == c->index)
numberOfTrackTiles++;
}
SubtractMoneyFromCompany(CommandCost(EXPENSES_TRAIN_RUN, numberOfTrackTiles * MONTHLY_COST_PER_TRACK_TILE));
}
}

www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Monthly Track Maintenance Costs
No reason tosmallfly wrote:So guys. That was a quick thought. Please correct me and call me stupid

I wouldn't use the EXPENSES_TRAIN_RUN type though, as the type determines where in the finance window (which category) the cost will appear.
A better category would be "Property Maintenance". I don't know what EXPENSES_* corresponds with this, but I guess you'll find it quickly in the same place EXPENSES_TRAIN_RUN is defined.
edit: Looked it up, it's EXPENSES_PROPERTY.
While you're at it, you could search the source code for places where costs in that category ("Property Maintenance") is already added, and expand it the way you want to have it, instead of writing a whole new function.
Currently in OpenTTD, there are already periodic costs (monthly/quarterly/yearly I don't know) for each Station a player owns.
edit: it's inside CompanyGenStatistics(), which is called inside CompaniesMonthlyLoop().
Oh and one more thing in your code snippet (just now read it carefully, sorry): the "foreach (track in tracks)" won't work because tracks (unlike stations) are not stored like that in a list - you'll have to iterate over all the tiles of the map and pick out those those that have the correct TileType (see doxygen on tile_map.h) and the correct owner.
Maybe this could get quite expensive on a big map though, 2048^2 is 4 million tiles to iterate through. You'd have to check if this causes any problems.
An alternative to this expensive check would be caching the number of rail tiles a company owns in a variable. Every time this a rail tile is built or removed by any means, this variable would have to be updated accordingly. Then you'll have to make yourself familiar with the saveload system though, as this variable has to be stored in the savegame.
Re: Monthly Track Maintenance Costs
You're right.Roujin wrote:A better category would be "Property Maintenance" ... EXPENSES_PROPERTY.
So I will place the algorythm below these lines of code:Roujin wrote:it's inside CompanyGenStatistics(), which is called inside CompaniesMonthlyLoop().
Code: Select all
FOR_ALL_STATIONS(st) {
_current_company = st->owner;
CommandCost cost(EXPENSES_PROPERTY, _price.station_value >> 1);
SubtractMoneyFromCompany(cost);
}
I expected that. I will have a look for the walk-trough-all-tiles snippet now.Roujin wrote:Oh and one more thing in your code snippet (just now read it carefully, sorry): the "foreach (track in tracks)" won't work because tracks (unlike stations) are not stored like that in a list
OK, I will implement the function by using a cache variable. That makes sense. But I dont want this cache variable to be saved in savegame. Loading up a savegame should force the game to count the tiles again and save it to a cache variable. (Reason: If the variable gets out of sync (is not the real sum of all tiles anymore because of any unexpected events) a the user can refresh the variable by saving und loading the game)Roujin wrote: An alternative to this expensive check would be caching the number of rail tiles a company owns in a variable. Every time this a rail tile is built or removed by any means, this variable would have to be updated accordingly. Then you'll have to make yourself familiar with the saveload system though, as this variable has to be stored in the savegame.
//EDIT: some snippets ill need
Code: Select all
#include "map_func.h"
#include "tile_type.h" // MP_RAILWAY + MP_ROAD for level crossings
#include "tile_map.h"
static bool IsTileOwner(TileIndex tile, Owner owner)
static bool IsTileType(TileIndex tile, TileType type)
Last edited by smallfly on 19 Oct 2009 17:44, edited 9 times in total.
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Monthly Track Maintenance Costs
Maybe airport tile (intercontinental should cost more than commuter)smallfly wrote: - Monthly costs per airport
- Monthly costs per railway station tile
Correct me If I am wrong - PM me if my English is bad
AIAI - AI for OpenTTD
AIAI - AI for OpenTTD
Re: Coding Tutorial: Monthly Costs Per Track Tile
Please dont go into detail. Otherwise we will lose the thread fast. (But you're right of courseKogut wrote:Maybe airport tile (intercontinental should cost more than commuter)smallfly wrote: - Monthly costs per airport
- Monthly costs per railway station tile

www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Coding Tutorial: Monthly Costs Per Track Tile
For periodically processing all tiles TileLoop_Xxx (e.g. TileLoop_Track)... functions are suited best.
⢇⡸⢸⠢⡇⡇⢎⡁⢎⡱⢸⡱⢸⣭⠀⢸⢜⢸⢸⣀⢸⣀⢸⣭⢸⡱⠀⢰⠭⡆⣫⠰⣉⢸⢸⠀⢰⠭⡆⡯⡆⢹⠁⠀⢐⠰⡁
Re: Coding Tutorial: Monthly Costs Per Track Tile
I looked at the function, but I dont know how to handle it. What benefit should this function provide compared to just run through all tile indices and ask for owner and tile type in the end of a month like:frosch wrote:For periodically processing all tiles TileLoop_Xxx (e.g. TileLoop_Track)... functions are suited best.
Code: Select all
uint maxTileIndex = MapSizeX() * MapSizeY();
FOR_ALL_COMPANIES(c) {
for (uint tile = 0; tile < maxTileIndex; tile++) {
if (IsTileOwner(tile, c) && IsTileType(tile, MP_RAILWAY)) {
_current_company = c;
CommandCost cost(EXPENSES_PROPERTY, 100);
}
}
}
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Coding Tutorial: Monthly Costs Per Track Tile
- The function already runs over all tiles,
- it does this in a more fine grained way than monthly,
- it does not run over the whole map at once.
To see its effect, clear some grass and watch it growing. It won't grow synchronously on all tiles.
- it does this in a more fine grained way than monthly,
- it does not run over the whole map at once.
To see its effect, clear some grass and watch it growing. It won't grow synchronously on all tiles.
⢇⡸⢸⠢⡇⡇⢎⡁⢎⡱⢸⡱⢸⣭⠀⢸⢜⢸⢸⣀⢸⣀⢸⣭⢸⡱⠀⢰⠭⡆⣫⠰⣉⢸⢸⠀⢰⠭⡆⡯⡆⢹⠁⠀⢐⠰⡁
Re: Coding Tutorial: Monthly Costs Per Track Tile
Ok, you got me. I should implement it in the loop you mentioned, but first of all i need to understand some basics...frosch wrote:- The function already runs over all tiles,
- it does this in a more fine grained way than monthly,
- it does not run over the whole map at once.
To see its effect, clear some grass and watch it growing. It won't grow synchronously on all tiles.
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Incredible!
Yeehaw! That works great! Here is what i coded to make it function:
In the game that looks like the following screenshot. There is one problem: The currency. I set the MONTHLY_TRACK_FEE to 10 >> 1 which is 10/2 = 5 (pounds because pounds is the default currency). The currency in game is for example us dollar (one pound = two us dollars), so that the finance overview correctly says "10 $" (because its only one tile).
How do i say the console that it sould output the value MONTHLY_TRACK_FEE not in pounds but in us dollars inclusive the dollar sign? I think its the function FormatGenericCurrency() but i do not know how to use it. HELP!
Code: Select all
uint maxTileIndex = MapSize();
uint countTracks = 0;
const uint MONTHLY_TRACK_FEE = 10 >> 1;
FOR_ALL_COMPANIES(c) {
_current_company = c->index;
for (uint tile = 0; tile < maxTileIndex; tile++) {
if (IsValidTile(tile) &&
!IsTileType(tile, MP_HOUSE) &&
!IsTileType(tile, MP_INDUSTRY) &&
IsTileOwner(tile, _current_company) &&
IsTileType(tile, MP_RAILWAY)
)
countTracks++;
}
SubtractMoneyFromCompany(CommandCost(EXPENSES_PROPERTY, countTracks * MONTHLY_TRACK_FEE));
IConsolePrintF(CC_INFO, "Total tracks of your company: %i, Fee per Tile and Month: %i", countTracks, MONTHLY_TRACK_FEE);
}
How do i say the console that it sould output the value MONTHLY_TRACK_FEE not in pounds but in us dollars inclusive the dollar sign? I think its the function FormatGenericCurrency() but i do not know how to use it. HELP!

www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Coding Tutorial: Monthly Costs Per Track Tile
Could you post a diff pleaes? In tortoiseSVN: right-click on folder w/ changes, then click TortoiseSVN -> Create Patch -> Ok
Re: Coding Tutorial: Monthly Costs Per Track Tile
The 'base' currency is GBP.
Re: Coding Tutorial: Monthly Costs Per Track Tile
Of course. Here it is. But you wont get happy with it, if you dont have 0.7.3 tag source code downloadedpetert wrote:Could you post a diff pleaes? In tortoiseSVN: right-click on folder w/ changes, then click TortoiseSVN -> Create Patch -> Ok

Does anyone know the function to display costs in user selected currency in console? (see my above question)
- Attachments
-
- track_maintenance_costs_0.7.3_v1.diff
- (1.24 KiB) Downloaded 139 times
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Coding Tutorial: Monthly Costs Per Track Tile
Why? The console is not there for regular output, but for debugging (And for console commands, obviously). For debugging info it's okay (or even appropriate imo) to use the internal value, instead of whatever currency the player has chosen.
I'd also suggest using the DEBUG(...) macro instead of IConsolePrintF. Just search the source for other occurrencies of "DEBUG(" to see how it's used. It gets a category passed (e.g. misc), a level, and then your (formatted) text.
The text will then only be displayed in the console if you run OpenTTD with at least the debug level you specified
or set the debug level in the console
Also, don't hardcode the cost like this. Check the source for other occurrencies of SubtractMoneyFromCompany: you'll find that they all use one of the Prices defined in economy_type.h, their values set in src/table/pricebase.h
example: the station property payment you already quoted in a previous post
The base costs are exposed to newGRF, however I don't know what measures you have to take if you want to add a new one. For simplicity, you could just use (a fraction of) station_value, like the station property payment does.
Finally, two suggestions to simplify your current code: don't iterate over all companies and check if the rail belongs to that company inside, but just get the owner of the tile. Second: Your TileType checks are redundant. Each tile has exactly one of those types.
Oh and one more thing I just found: you're not covering rail+road crossings. According to this, they count as MP_ROAD.
I'd also suggest using the DEBUG(...) macro instead of IConsolePrintF. Just search the source for other occurrencies of "DEBUG(" to see how it's used. It gets a category passed (e.g. misc), a level, and then your (formatted) text.
The text will then only be displayed in the console if you run OpenTTD with at least the debug level you specified
Code: Select all
openttd.exe -d misc=2
Code: Select all
debug_level misc=2
Also, don't hardcode the cost like this. Check the source for other occurrencies of SubtractMoneyFromCompany: you'll find that they all use one of the Prices defined in economy_type.h, their values set in src/table/pricebase.h
example: the station property payment you already quoted in a previous post
Code: Select all
CommandCost cost(EXPENSES_PROPERTY, _price.station_value >> 1);
Finally, two suggestions to simplify your current code: don't iterate over all companies and check if the rail belongs to that company inside, but just get the owner of the tile. Second: Your TileType checks are redundant. Each tile has exactly one of those types.
Oh and one more thing I just found: you're not covering rail+road crossings. According to this, they count as MP_ROAD.
Re: Coding Tutorial: Monthly Costs Per Track Tile
Thanks for you nice help
I will give back something to the community by writing some easy-to-understand wiki pages about topics i messed around with during the next weeks.
To get back to this thread:
Also thanks for the debug level hint.
But i will learn it!
)

To get back to this thread:
Thats my opinion too, at least for a final patch. At the moment I do it quick and dirty, because i dont know it better, i.e. i dont know how to add a button to finance window that leads you to a new window with additional financial information like track maintenance costs. Another way would be to make the finance menu like a tree, where you can open a point like "running costs", below that there are "track maintenance costs", "station maintenance costs" etc.Roujin wrote:Why? The console is not there for regular output, but for debugging (And for console commands, obviously). For debugging info it's okay (or even appropriate imo) to use the internal value, instead of whatever currency the player has chosen.
Thanks for the hint. I will use it.Roujin wrote:I'd also suggest using the DEBUG(...) macro instead of IConsolePrintF.

Of course. In final patch i want to make the running costs alterable by the server by using "advanced settings". At the moment, i dont know how to manage it.Roujin wrote:Also, don't hardcode the cost like this.


So you want to iterate over the tiles (but not the companies) then ask for the owner and say SubtractMoneyFromCompany()? That way the SubtractMoneyFromCompany() Function gets called very often. Isnt that a problem? But perhaps youre right... If you have 20 companies, there would be 20 iterations over the tile iterations ...Roujin wrote:Finally, two suggestions to simplify your current code: don't iterate over all companies and check if the rail belongs to that company inside, but just get the owner of the tile.
i had to do those checks. otherwise an assert failure appears in game because the "IsTileOwner" Function requires those checks. or what do you mean?Roujin wrote:Second: Your TileType checks are redundant. Each tile has exactly one of those types.
I know that. (That sounds if a know much about the topic, but i just stepped over it during my doxygen searchRoujin wrote:Oh and one more thing I just found: you're not covering rail+road crossings. According to this, they count as MP_ROAD.

www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Re: Coding Tutorial: Monthly Costs Per Track Tile
If you think calling SubtractMoneyFromCompany is a problem you can also count the number of tracks in a local array and call it once for each company after the tileloop. Doing the tileloop 20 times is definitely an inefficient solution.smallfly wrote:So you want to iterate over the tiles (but not the companies) then ask for the owner and say SubtractMoneyFromCompany()? That way the SubtractMoneyFromCompany() Function gets called very often. Isnt that a problem? But perhaps youre right... If you have 20 companies, there would be 20 iterations over the tile iterations ...
i had to do those checks. otherwise an assert failure appears in game because the "IsTileOwner" Function requires those checks. or what do you mean?[/quote]The following if check is equivalent to your check:Roujin wrote:Second: Your TileType checks are redundant. Each tile has exactly one of those types.
Code: Select all
if (IsValidTile(tile) &&
IsTileType(tile, MP_RAILWAY) &&
IsTileOwner(tile, _current_company)
)
Re: Coding Tutorial: Monthly Costs Per Track Tile
Ah. Now i now what you mean. Yeah youre right. In this case it is absolutely redundant. I coded it like that, because i also tested to use the patch in that way, that you have to pay for each tile you own, independent of tile type. So you dont "own" anything anymore. You "rent" everything. But that was just a test. The final patch shouldnt have redundant checks. Thanks for the hint.Yexo wrote:Code: Select all
if (IsValidTile(tile) && IsTileType(tile, MP_RAILWAY) && IsTileOwner(tile, _current_company) )
Thats the way i will do it. And i have to get to know the tile loop functions that the user "frosch" mentioned.Yexo wrote:you can also count the number of tracks in a local array and call it once for each company after the tileloop. Doing the tileloop 20 times is definitely an inefficient solution.
www.p1sim.org - P1SIM - Traffic, Logistics, City-Building & more
Join the discussions here on tt-forums.net!
Join the discussions here on tt-forums.net!
Who is online
Users browsing this forum: No registered users and 15 guests