Format String

Halo: Reach's scripting engine supports the display of "format strings." A format string can be a simple piece of text, but it can also include little placeholders that will automatically be replaced with other values (the format string "parameters") when the string is displayed. Up to two additional parameters can be provided after the format string itself.

A percentage sign (%) marks the start of a placeholder; the content after it determines the type of placeholder to use, and in some cases, the options to use for the placeholder. If you want to write an actual percentage sign in text, you must type %%. Using a placeholder symbol incorrectly (including by writing just one % symbol) will cause Halo: Reach to refuse to display your string.

Examples

Here's an example of how to use format strings: this code, taken from Slayer, displays the number of points needed to win if the game variant has a score limit.

for each player do
   if game.score_to_win != 0 and game.teams_enabled == 1 then 
      current_player.set_objective_text("Kill players on the enemy team.\r\n%n points to win.", game.score_to_win)
   end
   if game.score_to_win != 0 and game.teams_enabled == 0 then 
      current_player.set_objective_text("Score points by killing other players.\r\n%n points to win.", game.score_to_win)
   end
   if game.score_to_win == 0 and game.teams_enabled == 1 then 
      current_player.set_objective_text("Kill players on the enemy team.")
   end
   if game.score_to_win == 0 and game.teams_enabled == 0 then 
      current_player.set_objective_text("Score points by killing other players.")
   end
end

Persistent strings

Some functions treat format strings as persistent. This means that those particular strings can't be used to display "transient" variables — that is, any variable whose value will likely be reset before the frame is over, like the current_player or killer_player variables.

The game.show_message_to function is an example of a non-persistent string:

for each player do
   if current_player.number[0] == 1 then
      game.show_message_to(all_players, "%s won the game!", current_player)
      current_player.number[0] = 0
   end
end

When that function runs, it will immediately substitute your format string parameters into the format string, and it will pass the resulting text to the kill feed, which remembers that text. This means that for the case above, your kill feed message will always display whatever player was current_player at the time the function was called.

The widget.set_text function is an example of a persistent string:

script_widget[0].set_text("%s won the game!", global.player[0])

When that function runs, it doesn't immediately substitute out your placeholders. Instead, it remembers the format string, and it remembers which variables you passed as format string parameters — not the variables' values, but the variables themselves. Every frame, the HUD widget will take the format string and substitute out %s for the name of whatever player happens to be the one referred to by global.player[0] on that frame. If that variable's value is changed, the HUD widget will update in real time.

This is why transient variables can't be used with persistent strings: their values don't last through a whole frame, so when the HUD widget (or object waypoint or similar UI) redoes the format string substitution, the variables won't have the values you intended.

If you want players to see their own names (or variables) in a persistent string, then use variables like local_player.

Placeholder codes by type

Different placeholder codes are intended to format different kinds of information. If you pass a value of the wrong type (e.g. passing a player variable for a placeholder meant for numbers), then you'll see a default value which varies depending on the placeholder code.

If you don't pass any value at all, then the placeholder will be cut out of the string, but the string should still display as long as the placeholder itself was valid.

Integer number

%n
%o
%p
%s
%t
All of these codes print the number normally, as you would expect.
Default result: 0

Object

%n
%o
%p
%s
%t
Displays the name of the object as defined in the map file. Note that most objects don't have actual names defined, and will instead display unknown.

Player

%n
%o
%p
%s
%t
Displays the player's Xbox LIVE gamertag.

Timer

%n
%o
%p
%s
%t
Displays minutes and seconds, separated with a colon and with the seconds padded to two digits, e.g. 0:05.

Unknown

%c
Sometimes, this fails as an error and prevents the string from being displayed. Other times, it produces a line break followed by the number 25. Notably, in ASCII, the number 25 is used to encode the percentage sign.
%d
%i
%u
%x
%X

Seems to always produce 1, no matter what you pass in.

In the C and C++ programming languages, these codes are used to format integers. You can put additional settings between the percentage sign and the number, in order to pad the number to a certain length using spaces or zeroes. Those settings work here, but they always act as if you passed in the number 1; %03d, for example, always produces 001.

%S
%Z
Crashes the game instantly, when displayed.

Decimal number

Format codes exist for decimal numbers, but it's not clear why. None of the variable types available to Megalo are decimal numbers, and these codes don't work for timers (except when timers have special meanings for them).

These codes seem to be meant for double-precision floating-point values.

%a
Prints the number in hexadecimal (base-16) format.
Default result: 0x0.0000000000001p-1022
%A
Same as %a, but the result uses all-caps.
Default result: 0X0.0000000000001P-1022
%e
Prints the number using scientific notation.
Default result: 0.000000e+00
%f
Prints the number out to six significant figures.
Default result: 0.000000
%g
Prints the number out to exactly as many decimal points as is necessary if the number has fewer than six significant digits before the decimal point and fewer than four after; otherwise, uses scientific notation like %e does.
Default result: 0