Skip to content

Conversation

@PeterN
Copy link
Member

@PeterN PeterN commented Jan 18, 2025

Add support for NewGRF badge feature in OpenTTD/OpenTTD#13073

Use badge_table { } to create the translation table.
Use badges property to assign a list of badges to an entity.

See 042_badges.nml for slightly more info.

@PeterN PeterN force-pushed the newgrf-badges branch 3 times, most recently from 8132947 to df390e8 Compare January 18, 2025 19:56
@PeterN
Copy link
Member Author

PeterN commented Jan 18, 2025

The regression test 042_badges will add a badge to the "Chaney 'Jubilee' (Steam)" engine, which will appear like so:

image

@PeterN PeterN marked this pull request as ready for review February 16, 2025 20:23
@WenSimEHRP
Copy link
Contributor

Checking badges on a nearby tile for now would require something like this

switch(FEAT_STATIONS, SELF, _sw_check_yard_p1n1, [
    STORE_TEMP(1, 0x100),
    STORE_TEMP(bitmask(4), 0x101),
    var[0x79, 0, 0x1, 0xF1]]){return;}

better if we can simplify it to

nearby_tile_has_badge(x, y, badge, features)

whereas features is bitmask(FEAT_1, FEAT_2, ...)

@andythenorth
Copy link
Contributor

action 0 flags...
badge_flags.patch

@andythenorth
Copy link
Contributor

andythenorth commented Apr 13, 2025

badge_error_line_num.patch

More than happy to push these patches to my fork for cherrypick, but eh, they're tiny.

}
# Railtypes have no 60+x variables

varact2vars60x_railtype = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the variables are missing from NFO docs. This one is also missing from OTTD implementation at least for rail/road/tram types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be implemented now (Iron Horse uses has_badge on railtypes).

@andythenorth
Copy link
Contributor

Been using this for Iron Horse since the PR opened, works for me.

@PeterN PeterN force-pushed the newgrf-badges branch 3 times, most recently from 1628ea4 to 662945b Compare October 27, 2025 19:49

def get_badgelist_action(badge_list):
index = 0
actions = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item blocks can be inside if/else blocks, but badgetable can't, therefore NewGRF autor is not able to provide backward compability in NewGRF that uses badges.

Adding act9 which skips badgetable would prevent NewGRFs with badgetables from crashing on older OpenTTD versions.

+   if len(badge_list) <= 0:
+       return []
+
    index = 0
-   actions = []
+   actions = [action7.SkipAction(9, 0xA1, 4, (0x04, r"\7<"), 0x1F000000, len(badge_list))]

PS. I have tested this code snippet and it works as intended with 13.3 and 15.0-beta3.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well if you are using badges, backward compatibility is not expected. Just target OpenTTD 15+.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather support backward compatibility than badges, but I'm able to compile nml myself, so it isn't a big deal for me :). Just because 15.0 also adds bridges over stations I already have the station item blocks inside if blocks. It is a shame a litle bit that the badgetable can't be inside conditional block.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, we don't need to skip badges, backward compatibility isn't a good goal for grf.

@audigex
Copy link

audigex commented Nov 4, 2025

No warning/error is thrown when creating a FEAT_BADGES item, whereby the label is not in the badgetable. Unless I'm msissing something here, I can't think why I would define a badge item that isn't in the badge table (and therefore isn't used on a vehicle?

I'm not sure if that should be considered an error, but it certainly feels like something that should trigger a warning since it's most likely a typo

@audigex
Copy link

audigex commented Nov 4, 2025

A similar "A warning would be nice" situation

If I have a badge eg "power/steam", but no "power" badge defined, the "power/steam" badge does show up in the newGRF debug menu but doesn't show up in the purchase menu as either a sprite or text. If the user's intention was to add text that's kinda obvious ("Power: steam" can't displayed if "Power" doesn't exist to have text), but it's a little counter-intuitive if the user is mostly thinking about adding a sprite but didn't want text

I assume this is a limitation of the implementation in OpenTTD rather than NML, but it would be good if NML threw a warning if a nested badge was defined without a parent badge already being defined

@PeterN
Copy link
Member Author

PeterN commented Nov 22, 2025

No warning/error is thrown when creating a FEAT_BADGES item, whereby the label is not in the badgetable. Unless I'm msissing something here, I can't think why I would define a badge item that isn't in the badge table (and therefore isn't used on a vehicle?

I'm not sure if that should be considered an error, but it certainly feels like something that should trigger a warning since it's most likely a typo

Much like defining railtypes and roadtypes, there's no requirement to use badges when defining them. Maybe you wanted to make a collection of badges to be used in other NewGRFs. A badge table is only required to use badges, not to define them.

It's also not an error to not define a class badge.

@audigex
Copy link

audigex commented Nov 22, 2025

No warning/error is thrown when creating a FEAT_BADGES item, whereby the label is not in the badgetable. Unless I'm msissing something here, I can't think why I would define a badge item that isn't in the badge table (and therefore isn't used on a vehicle?

I'm not sure if that should be considered an error, but it certainly feels like something that should trigger a warning since it's most likely a typo

Much like defining railtypes and roadtypes, there's no requirement to use badges when defining them. Maybe you wanted to make a collection of badges to be used in other NewGRFs. A badge table is only required to use badges, not to define them.

It's also not an error to not define a class badge.

Lots of other NML warnings aren't really errors, either ... that's the point of a warning, surely? "You might be doing this intentionally but there's a good chance you aren't, maybe take a look?"

@PeterN
Copy link
Member Author

PeterN commented Nov 30, 2025

Fixed the naming of flag BADGE_FLAG_USE_COMPANY_COLOUR (previously was BADGE_FLAG_NAME_USE_COMPANY_COLOUR)
Added new flag BADGE_FLAG_NAME_SKIP

Instead of a boolean to toggle using an extended-byte, len_size allows byte, word, extended-byte and dword types.
BlockAllocation(0, 62, "Roadtype"),
BlockAllocation(0, 62, "Tramtype"),
BlockAllocation(0, 0xFFFE, "RoadStop"), # UINT16_MAX - 1
BlockAllocation(0, 64000, "Badge"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the limit here is only 64000 and not UINT16_MAX like the other limits?
Especially when the badge_id in the OpenTTD source code is uint16_t.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because if you ever need an extra 'special' ID for some feature, having a range of IDs that are unused is way easier than lowering the limit as you need to write extra logic to remove the badges with that special ID.

Even then, if 64000 seems way too much to me. The idea it to be able to filter with them, and if you need more than a few hundred I'd argue that the categories are way too fine grained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants