How custom sprites are found
From OpenTTD
Since this is not a trivial process I try to help anyone interested to understand what's going on. The terminology on this subject is kind of confusing, since OpenTTD uses different (and not very intuitive) names, and TTDPatch uses very different (and varying) names. I'll stick here to the terminology I used in the file grfspecial.c, so people don't get confused when reading that file. Please ask if anything is unclear! I suck in putting complicated things into simple terms!
Contents |
[edit] SpriteSet
A "SpriteSet" is a collection of sprites. For vehicles there are either four or eight "SpriteID"s (one for each direction), that make one "Subset". In one "SpriteSet" are up to 32 such "Subset"s. They are divided into "loaded" and "loading": When a vehicle is stopped one of the up to 16 "loading" "Subset"s is used, if the vehicle is moving one of the up to 16 "loaded" "Subset"s is used. Since the 4 or 8 "SpriteID"s in a "Subset" are always linear only the number of "SpriteID"s in each "Subset" (this number is the same for the entire "SpriteSet") and the first "SpriteID" is being stored in memory.
[edit] CargoSet
A "CargoSet" is either a pointer to one "SpriteSet" or a list of pointers to other "CargoSet"s with additional meta-information that is used to decide which "CargoSet" is being used. "CargoSet"s that are in one such list may be of the list-type themselves, so several recursive steps may be required till one arrives at a "SpriteSet".
[edit] Engine
If a (specific) vehicle is of type "wagon" it is often part of a "train". A train is a collection of a few (specific) vehicles: One of the type "engine" and one or more of the type "wagon". In our context "engine" refers to the (specific) vehicle pulling the "train", in which a (specific) "wagon" is in. Therefore it only makes sence to talk about an "engine" (rather than a "vehicle") if the "vehicle" we're dealing with is of the type "wagon". "engine_type" of couse refers to the "vehicle_type" of the "engine". This is not very exciting, but important for the next step..
[edit] CargoOverride
A "CargoOverride" maps a "vehicle_type" to one or more "CargoSet"s. For the mapping, the "cargo_type" and/or, if applicable, the "engine_type" is used.
[edit] Example
This has been a bottom-up approach so far. To help you absolutely understand how everything works together I'll explain how one sprite is actually found, with the functions actually used. Since I'm not done coding this, this will propably change as I proceed..
At first GetCustomVehicleSprite is called, passing the "Vehicle" structure of the vehicle that is to be drawn. If possible engine_type and cargo_type are copyed from the Vehicle-structure. vehicle_type, cargo_type and engine_type are passed to GetCargoID (TODO rename function). The function looks in struct CargoOverride *_vehicle_mappings[] if any CargoOverrides are defined for this vehicle_type. It wanders along the list till it finds a "perfect match" (one, that matches both, engine_type and cargo_type) or reaches the end of the list. If it comes along a better match a pointer is updated. (TODO Since this is done everytime the screen is updated it's propably good to move matches to the head of the list, so the next time this function is called it will be found quickly). Eventually it returns (a pointer to) a CargoSet. This CargoSet is passed to GetVehicleSpriteSetFromCargoSet which decides which CargoSet to use, if this is a list, or returns the SpriteSet, if it is not. (This is one single big
TODO) Using the Vehicle struct GetCustomVehicleSprite now determines how loaded the vehicle is, and if it is moving. If finds the right "Subset", adds the direction to the "SpriteID" and returns the result.
[edit] Not done yet
Although I implemented most parts of this so far, it is very very very unstable. It crashes every other time I start the game and I didn't play with it more then 2 minutes yet. I'll upload a patch somewhere as soon as I think I'll do more good than it causes harm. Until that happens, this page is merely for some guys I tried to explain what I was doing on IRC and failed badly.
