-
Notifications
You must be signed in to change notification settings - Fork 11
Script File Syntax
NOTE: This page has been split into several subpages. Please use the sidebar to navigate to the appropriate subpage. |
---|
RAScript is a domain-specific language (DSL) that allows achievement developers for retroachievements.org the ability to design achievements using many of the modern conveniences provided by programming languages - such as variables, loops, and comments.
You can browse several examples in Jamiras's repository.
Here's an excerpt from Magic Darts illustrating several of the features:
First, we define a function for determining the character in each player slot and some constants for each of the available values.
function player_character(index) => byte(0x0060 + index * 16)
character_none = 0
character_tom = 1
character_bob = 2
character_ann = 3
character_sue = 4
character_joe = 8 // expert male
character_max = 9 // macho man
character_lee = 10 // kung fu
character_rom = 11 // robot
character_liz = 12 // expert female
character_ken = 13 // ninja
character_rio = 14 // monkey
character_ebe = 15 // alien
character_ebe2 = 5 // alien alternate face
Then, we create a helper function to ensure a player exists for the slot and is not CPU controlled.
function player_is_player(index) => player_character(index) != character_none && byte(0x006E + index * 16) == 1
And some more helper functions that map other data in memory to other constants.
function player_dart_weight(index) => byte(0x0067 + index * 16)
weight_light = 1
weight_medium = 2
weight_heavy = 3
function game_variant() => byte(0x04B0)
variant_301 = 1
variant_501 = 2
variant_701 = 3
variant_countup = 4
variant_roundtheclock = 5
variant_halfit = 6
function current_player() => byte(0x04CF) // 1-based
function throwing_phase() => byte(0x04CA)
phase_player_intro = 2
phase_left_right = 3
phase_angle = 4
phase_power = 5
phase_throw = 6
phase_in_flight = 7
phase_hit_board = 8
phase_scoring = 9
And a function for cheat protection. If you're not familiar with the functionality for creating achievements provided by the base editor, the explanation may not make sense.
function trigger_game_start() => word(0x04D6) == 0x0100 // round 1, 0 darts thrown
function using_alien_aim() => byte(0x04DC) == 2
function never_using_alien_aim() =>
once(trigger_game_start()) &&
(always_true() ||
(always_false() && never(using_alien_aim()) && unless(current_player() != 1))
)
- The
once(trigger_game_start())
sets a flag when the game starts that must be true for the achievement to trigger. - The
never(using_alien_aim())
will reset the flag if the player activates the cheat. - The
unless(current_player() != 1)
clause prevents thenever(using_alien_aim())
clause from resetting the flag if one of the opponents activates the cheat. - The
always_true()
clause pushes the rest of conditions into an alt group so the pause doesn't affect the logic in the core group. - The
always_false()
clause prevents the alt group that is being used to reset the game start flag from being true, so another alt group (usually thealways_true()
above) must be true for the achievement to trigger
There's a lot of complex logic expressed in mostly-readable English. Additionally, it's packaged up in a function, so it can be reused on every achievement where the cheat protection is needed.
And finally, the actual achievement definition.
achievement(
title = "Light to 500",
description = "Get at least 500 points in Count Up using light darts",
points = 5,
trigger = game_variant() == variant_countup &&
player_is_player(0) &&
current_player() == 1 &&
byte(0x04F2) >= 5 && // score (hundreds)
throwing_phase() == phase_hit_board &&
player_dart_weight(0) == weight_light &&
never_using_alien_aim()
)
Other than the byte(0x04F2) >= 5
(which is documented with a comment), it's very easy to read what the achievement is expecting.
- The player must be playing "Count Up"
- The first player (0) must be human controlled
- It must be the player's turn
- The player must have at least 500 points
- The player's dart must have just hit the board
- The player must be using light darts
- The player must not have used the cheat
The resulting achievement is:
Core Group
Mem 8-bit 0x0004b0 = Value 0x000004 (0)
Mem 8-bit 0x000060 != Value 0x000000 (0)
Mem 8-bit 0x00006e = Value 0x000001 (0)
Mem 8-bit 0x0004cf = Value 0x000001 (0)
Mem 8-bit 0x0004f2 >= Value 0x000005 (0)
Mem 8-bit 0x0004ca = Value 0x000008 (0)
Mem 8-bit 0x000067 = Value 0x000001 (0)
Mem 16-bit 0x0004d6 = Value 0x000100 (1)
Alt 1
Value 0x000001 = Value 0x000001 (0)
Alt 2
Reset If Mem 8-bit 0x0004dc = Value 0x000002 (0)
Pause If Mem 8-bit 0x0004cf != Value 0x000001 (0)
Value 0x000000 = Value 0x000001 (0)
And since everything is neatly packaged up into helper functions and constants, if we want a second achievement for doing the same things with heavy darts, we only have to change the comparison related to dart weight and the title/description text:
achievement(
title = "Heavy to 500",
description = "Get at least 500 points in Count Up using heavy darts",
points = 5,
trigger = game_variant() == variant_countup &&
player_is_player(0) &&
current_player() == 1 &&
byte(0x04F2) >= 5 && // score (hundreds)
throwing_phase() == phase_hit_board &&
player_dart_weight(0) == weight_heavy && // <-- this was changed
never_using_alien_aim()
)
Much of the same logic can also be carried through to other achievements. I've left the description off this one, see if you can guess it:
achievement(
title = "??",
description = "??",
points = 10,
trigger = trigger_win() &&
game_variant() == variant_301 &&
current_round() <= 4 &&
never_using_alien_aim()
)