Transport Tycoon Forums

The place to talk about Transport Tycoon
It is currently Fri Oct 19, 2018 1:28 am

All times are UTC




Post new topic  Reply to topic  [ 5 posts ] 
Author Message
 Post subject: Callbacks
PostPosted: Mon Apr 24, 2017 11:23 am 
Offline
Traffic Manager
Traffic Manager
User avatar

Joined: Sat Nov 17, 2007 5:23 pm
Posts: 160
Skype: ben313371
Hey there

I couldn't find much on the GRF spec wiki about what callbacks are and how to use them within a GRF so this is why I'm making this short thread about them.


Top
   
 Post subject: Re: Callbacks
PostPosted: Sun Apr 30, 2017 9:56 am 
Offline
Tycoon
Tycoon

Joined: Tue Feb 22, 2011 6:34 pm
Posts: 2652
Callbacks basically are calls to other pieces of code in your NewGRF. Those pieces can just be a simple value like 42, but also references to strings in the language files or spritesets, or switches to determine the result based on some variables. If you write NewGRFs you automatically use them, even without knowing it. In NML, they are in the graphics and livery_override blocks.

_________________
Coder of the Dutch Trackset | Development support for the Dutch Trainset | Coder of the 2cc TrainsInNML


Top
   
 Post subject: Re: Callbacks
PostPosted: Sun Apr 30, 2017 12:05 pm 
Offline
Tycoon
Tycoon
User avatar

Joined: Sat Mar 31, 2007 2:23 pm
Posts: 4689
Location: Lost in Music
NewGRF Callbacks are triggered by events in OpenTTD. Broadly these events are:

- something in the game loop on a regular frequency
- user interaction (via UI)
- other game events, such as vehicle arriving in station

When the event is triggered OpenTTD calls the callback chain in the NewGRF, passing certain additional CB information. The NewGRF code calculates a result (optionally using the additional CB information), and returns a result to OpenTTD. OpenTTD then uses the result. Different values for the result will result in different behaviours from OpenTTD.

Examples are:
- text in industry windows
- mutable vehicle properties
- tile animation
- cargo processing at industries
...and many more.

_________________
FIRS Industry Replacement Set (Released) | HEQS Heavy Equipment Set (trucks, industrial trams and more) (Finished)
Squid Ate FISH (ships) (Released) | CHIPS Has Improved Players' Stations (Finished)
Iron Horse (trains, released) | Termite (tracks for Iron Horse, released) | Busy Bee (game script, released)

Road Hog (road vehicles, released)


Top
   
 Post subject: Re: Callbacks
PostPosted: Fri Jun 16, 2017 11:10 pm 
Offline
Traffic Manager
Traffic Manager
User avatar

Joined: Sat Nov 17, 2007 5:23 pm
Posts: 160
Skype: ben313371
Is there anywhere where I can learn the callback feature in more detail?


Top
   
 Post subject: Re: Callbacks
PostPosted: Sun Jun 25, 2017 6:18 am 
Offline
Transport Coordinator
Transport Coordinator

Joined: Thu Oct 07, 2004 12:51 pm
Posts: 290
Depends on what flavor you are working with...

If you are doing NFO then i would refer you to the documentation on that: https://newgrf-specs.tt-wiki.net/wiki/Main_Page
If you are doing NML then you could look here: https://newgrf-specs.tt-wiki.net/wiki/NML:Main

In the case of NML, a callback is simply a piece of code that evaluates whatever value you want for your property in question.
Now, mind you this will mostly be from memory...

Ok, say you want to alter the running cost of a train dynamically based on the train's current speed.

First, in the graphics block of the train you would add:
Code:
graphics {
    running_cost_factor: switch_my_train_running_cost_factor;
}


running_cost_factor - The property we want to assign. In the properties section of your train, that particular property takes values ranging from 0 to 255.
switch_my_train_running_cost_factor - Is the name (identifier) of a switch block that you have defined previously in your NML file.

The switch could look something like this:
Code:
switch(FEAT_TRAINS, PARENT, switch_my_train_running_cost_factor, 0) {
    return current_speed * 100 / max_speed * 255 / 100;
}


If you don't know how switch blocks works then see the NML reference i linked at the top. I am not going to explain that in full here!
But essentially, switch blocks make decisions based on their fourth parameter (which in this case i've set to zero as i am not making a decision) and then evaluates an expression, returning the result of that evaluated expression to the caller. In other words, calling back or a callback if you will!

FEAT_TRAINS - The feature we are working on. In this case, trains, which gives us access to a subset of variables related to trains. It basically sets the stage for what we want to use in the following expressions.
PARENT - We want to get information about the parent vehicle of our train. Or in this case... the head, the first, the one that actually has all the data on it or the locomotive if you will... rather than each individual wagon. If we used SELF, we wouldn't be able to query the speed variables of the train.
switch_my_train_running_cost_factor - Again, the name (Identifier) of the switch block in question. Same as the identifier in the graphics block above.
0 - As i said, the fourth parameter is used to make decisions. Read about the switch block in the NML documentation for more details on that. We are making no decisions so i have hardcoded this to zero as one cannot omit the fourth parameter in a switch block.

return - Not necessary but essentially means we want to return a value back to the caller at this point. I could type "return 42;" and the returning value would be "42". Or i could simply omit the return statement and type "42;" here. Or, i could even input the identifier of another switch block in here for a chain.
current_speed - This is a variable that the locomotive knows about. Essentially, it's the same as typing "var[0xB4]" there. Or get variable B4 (180 in decimal) from the table of variables that exist for the current feature. It's the current speed of the vehicle in internal speed units.
max_speed - Same as current_speed but instead we are getting variable 0x4C for the vehicle's maximum speed.

Then, what we are doing is multiplying the current_speed variable by 100 and dividing that with the maximum speed of the train.
Why multiply, you might ask? Well, because the return from callbacks is ALWAYS integers. In other words, you cannot return 0.5 or 0.2 etc. If you do, the value will be floored to 0. Same if you return 0.9, the value is floored, not rounded! The same goes for any calculations inside the expression. Each step of the way, the values are turned to integers. Hence...
Code:
10 * (1/2) = 0

The (1/2) does NOT resolve to 0.5, it resolves to 0. Sure, it's possible to use floating point numbers in an expression. But then the whole expression has to be a constant, as soon as you bring variables into the mix, you cannot use floating point numbers in the expression.
Or in other words...
Code:
VALID: 5.5*0.2594;
INVALID: current_speed*5.5*0.2594

Either way, the returned result is turned (except for a few properties that actually are floats) into an integer in the end. The result therefore of the valid expression above would be 0 anyways.
So, essentially, we have to turn integers into numbers that can be used to represent the decimal points of any operation, remembering to truncate those decimal points in the end.
Which is why there's a division by 100 at the end.

Inside the little decimal point trick, we also multiply the result by 255. This to conform to the range of 0-255 of the running cost factor. Not that you need to anymore, you could return 25500 here. The vehicle would then have a running cost that is 25.5 thousand times as expensive to run as it's base running cost. Or at least that's what i have found out today.

There you have it. Good luck if you are trying to do this in NFO. ;)
It's possible but it's a PITA.

TL;DR

A callback is like saying, in math terms:
Code:
f(x) = 2x
f(1) = 2
f(2) = 4
f(3) = 6
...
f(factor) = current_speed * 100 / max_speed * factor / 100
running_cost_factor: 25; // = 25
or...
running_cost_factor: f(25); // = 0 when speed is 0, = 12 when speed is 50% of max speed, = 25 when speed is 100% of max speed

It's a function. A callback function.


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 5 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000-2018 phpBB Limited

Copyright © Owen Rudge/The Transport Tycoon Forums 2001-2018.
Hosted by Zernebok Hosting.