- Condition
- Achievement
- Leaderboard
- AchievementSet
- RichPresence(params: RichPresence.Params)
This class represents a code piece that can be part of achievements, leaderboards or rich presence for RetroAchievements.
Conditions are immutable, if you need to a make a new Condition instance based of existing one - use with()
method.
flag: '' | 'PauseIf' | 'ResetIf' | 'ResetNextIf' | 'AddHits' | 'SubHits' | 'AndNext' | 'OrNext' | 'Measured' | 'Measured%' | 'MeasuredIf' | 'Trigger' | 'AddSource' | 'SubSource' | 'AddAddress' | 'Remember'
Affects condition logic or the way it reads memory.
Individual documentation for each flag can be seen here.
Condition's left value, it always exists.
An operator set between left and right value. Empty string is allowed for conditions that don't specify right value.
Condition's optional right value. If it's not set - rvalue properties are empty strings.
Amount of hits set (also known as Hit Count), additional explanation can be seen here.
Specifies if value is read from memory and the way it's read/interpreted, or if value is constant. Empty string is allowed for rvalue.
size: '' | 'Bit0' | 'Bit1' | 'Bit2' | 'Bit3' | 'Bit4' | 'Bit5' | 'Bit6' | 'Bit7' | 'Lower4' | 'Upper4' | '8bit' | '16bit' | '24bit' | '32bit' | '16bitBE' | '24bitBE' | '32bitBE' | 'BitCount' | 'Float' | 'FloatBE' | 'Double32' | 'Double32BE' | 'MBF32' | 'MBF32LE'
Specifies how to interpret the value at specified memory address. Not required for constant values.
If value type implies reading from memory - this specifies memory address, otherwise specifies constant value.
Creates Condition using array representing it.
new Condition(['ResetIf', 'Mem', 'Bit0', 71, '>', 'Delta', 'Bit1', 71, 3])
Creates Condition using a string representing the condition.
new Condition('R:0xM47>d0xN47.3.')
// same as
new Condition(['ResetIf', 'Mem', 'Bit0', 71, '>', 'Delta', 'Bit1', 71, 3])
Returns the same Condition instance passed, which is due to Conditions being immutable.
Creates Condition using an object that directly represents the Condition data.
new Condition({
flag: '',
lvalue: {
type: 'Mem',
size: '32bit',
value: 0xcafe,
},
cmp: '=',
rvalue: {
type: 'Value',
size: '',
value: 47,
},
hits: 0,
})
Returns new Condition instance with different values merged.
lvalue
and rvalue
can be specified as partial array, which can be less verbose
new Condition('0=1')
.with({ cmp: '!=', rvalue: { value: 47 } })
.toString() // 0!=47
new Condition('0xXcafe=0xXfeed')
.with({ rvalue: ['Delta', '16bit', 0xabcd] })
.toString() // 0xXcafe=d0x abcd
new Condition('0xXcafe=0xXfeed')
.with({ rvalue: ['Delta'] })
.toString() // 0xXcafe=d0xXfeed
Returns string representation of Condition suitable for RetroAchievements and local files.
new Condition(['ResetIf', 'Mem', 'Bit0', 71, '>', 'Delta', 'Bit1', 71, 3]).toString() // 'R:0xM47>d0xN47.3.'
Returns direct Array representation of Condition, values are exactly same as properties of Condition.
new Condition(['Measured', 'Mem', '8bit', 4]).toArray()
// [ "Measured", "Mem", "8bit", 4, "", "", "", 0, 0 ]
Returns prettier Array representation of Condition, which is more suitable for display:
- Everything is a string
- Values are formatted as hexadecimal if they are greater or equal to 100000
- Negative values are formatted as decimal if they are greater or equal to -4096, otherwise formatted as hexadecimal with underflow correction
- Hits are empty string if equal to zero
new cruncheevos.Condition(['ResetIf', 'Mem', '32bit', 0xfeedcafe, '>', 'Value', '', 71]).toArrayPretty()
// [ "ResetIf", "Mem", "32bit", "0xfeedcafe", ">", "Value", "", "71", "" ]
new cruncheevos.Condition(['', 'Value', '', -4097, '>', 'Value', '', -1]).toArrayPretty()
// [ "", "Value", "", "0xffffefff", ">", "Value", "", "-1", "" ]
This class represents an achievement for RetroAchievements. Achievement can be a part of AchievementSet class instance, or used separately if your goal is to parse and produce string representations of achievement that would go into local RACache file.
Achievements are immutable, if you need to a make a new Achievement instance based of existing one - use with()
method.
ID of an Asset matching the one on server.
If Asset does not exist on the server yet, id
should be set
to a high number like 111000001, similar to what RAIntegration
does when creating local assets.
Title of an Asset, must be set.
Description of an Asset, required by server, but optional for library.
Achievement's author name, it's not necessary and is not sent to servers, but local RACache files do mention the author.
Amount of points that players will get when earning the Achievement. Must be set to any positive integer or 0.
Server accepts following values: 0, 1, 2, 3, 4, 5, 10, 25, 50, 100.
Server may still have odd Achievements with incorrect point values, which is the reason for allowing any positive integer for points.
Optional type of achievement, accepted strings are self-explanatory.
Falsy values are treated as empty string, which marks no type set.
Optional numeric string representing Achievement's badge ID on server.
Alternatively, can be set to a string like 'local\\\\mybadge.png'
, which will be recognized by RAIntegration.
Array of arrays containing Condition class instances:
- Outer array represents Condition groups like Core, Alt 1, Alt 2 ...
- Inner array represents individual Conditions within the group
Creates Achievement using object representing it.
import { define as $ } from '@cruncheevos/core'
new Achievement({
id: 58, // or numeric string
title: 'My Achievement',
description: 'Do something funny',
points: 5,
badge: `local\\\\my_achievement.png`, // optional, or ID of badge on server
author: 'peepy', // optional and is not uploaded to server
conditions: {
core: [
['', 'Mem', '8bit', 0x00fff0, '=', 'Value', '', 0],
['', 'Mem', '8bit', 0x00fffb, '=', 'Value', '', 0],
],
alt1: $(
['', 'Mem', '8bit', 0x00fe10, '>', 'Delta', '8bit', 0x00fe10],
['', 'Mem', '8bit', 0x00fe11, '=', 'Value', '', 0],
),
alt2: '0=1'
}
})
new Achievement({
// ...
conditions: '0=1'
})
new Achievement({
// ...
conditions: [
['', 'Mem', '8bit', 0x00fff0, '=', 'Value', '', 0],
['', 'Mem', '8bit', 0x00fffb, '=', 'Value', '', 0],
] // same as providing an object: { core: [ ... ] }
})
Creates Achievement using string representing it, taken from RACache/Data/GameId-User.txt
file.
new Achievement(
'58:"0xHfff0=0_0xHfffb=0S0xHfe10>d0xHfe10_0xHfe11=0S0=1"' +
':My Achievement:Do something funny::::peepy:5:::::"local\\\\my_achievement.png"'
)
Returns new Achievement instance with different values merged.
someAchievement
.with({ title: someAchievement.title + 'suffix' })
Returns string representation of Achievement suitable
for RACache/Data/GameId-User.txt
file.
someAchievement.toString()
someAchievement.toString('achievement')
// '58:"0=1":My Achievement:Do something funny::::cruncheevos:5:::::00000'
someAchievement.toString('conditions') // '0=1'
This class represents a leaderboard for RetroAchievements. Leaderboards can be a part of AchievementSet class instances, or used separately if your goal is to parse and produce string representations of leaderboard that would go into local RACache file.
Leaderboard are immutable, if you need to a make a new Leaderboard instance based of existing one - use with()
method.
ID of an Asset matching the one on server.
If Asset does not exist on the server yet, id
should be set
to a high number like 111000001, similar to what RAIntegration
does when creating local assets.
Title of an Asset, must be set.
Description of an Asset, required by server, but optional for library.
type: 'SCORE' | 'TIME' | 'FRAMES' | 'MILLISECS' | 'SECS' | 'TIMESECS' | 'MINUTES' | 'SECS_AS_MINS' | 'VALUE' | 'UNSIGNED' | 'TENS' | 'HUNDREDS' | 'THOUSANDS' | 'FIXED1' | 'FIXED2' | 'FIXED3'
Specifies how to interpret Leaderboard's value.
Additional info can be seen here
Self explanatory, affects how leaderboard results are displayed.
conditions: { start: Condition[][], cancel: Condition[][], submit: Condition[][], value: Condition[][] }
Object representing four condition groups that make up Leaderboard code.
Each group is an array of arrays containing Condition class instances:
- Outer array represents Condition groups like Core, Alt 1, Alt 2 ...
- Inner array represents individual Conditions within the group
- For
value
group, each outer array represents Value retrieval and Max of these values is taken
Creates Leaderboard using object representing it.
import { define as $ } from '@cruncheevos/core'
new Leaderboard({
id: 58, // or numeric string
title: 'My Leaderboard',
description: 'Best score while doing something funny',
type: 'SCORE',
lowerIsBetter: false,
conditions: {
start: {
core: [
['', 'Mem', '8bit', 0x00fff0, '=', 'Value', '', 0],
['', 'Mem', '8bit', 0x00fffb, '=', 'Value', '', 0],
],
alt1: $(
['', 'Mem', '8bit', 0x00fe10, '>', 'Delta', '8bit', 0x00fe10],
['', 'Mem', '8bit', 0x00fe11, '=', 'Value', '', 0],
),
alt2: '0=1',
},
cancel: [
['', 'Mem', '16bit', 0x34684, '=', 'Value', '', 0x140]
], // same as providing an object: { core: [ ... ] }
submit: '0xH59d76=2',
value: [['Measured', 'Mem', '32bit', 0x34440, '*', 'Value', '', 2]],
},
})
Creates Leaderboard using string representing it, taken from RACache/Data/GameId-User.txt
file.
new Leaderboard(
'L58:"0xHfff0=1S":"0=1":"1=1":"M:0xX34440*2"' +
':SCORE:My Leaderboard:Best score while doing something funny:0'
)
Returns new Leaderboard instance with different values merged.
someLeaderboard
.with({ title: someLeaderboard.title + 'suffix' })
Returns string representation of Leaderboard suitable
for RACache/Data/GameId-User.txt
file.
someLeaderboard.toString()
someLeaderboard.toString('leaderboard')
// 'L58:"0xHfff0=1S":"0=1":"1=1":"M:0xX34440*2":SCORE:My Leaderboard:Best score while doing something funny:0'
someLeaderboard.toString('conditions') // '"0xHfff0=1S":"0=1":"1=1":"M:0xX34440*2"'
This class represents AchievementSet that can be converted into RACache/Data/GameId-User.txt file
AchievementSet is mostly to be used with standalone scripts that export it
for @cruncheevos/cli
to update local file in RACache.
Game ID matching the one on RetroAchievement servers, must be set correctly if using this class with @cruncheevos/cli
Game title or name, it doesn't have to be exact match and is merely put on the second line of produced local file.
Object containing all added achievements, with achievement id as a key. Treat it as read-only unless you know better.
Also implements Symbol.iterator which yields each Achievement stored.
Object containing all added leaderboards, with leaderboard id as a key. Treat it as read-only unless you know better.
Also implements Symbol.iterator which yields each Leaderboard stored.
Creates AchievementSet.
new AchievementSet({ gameId: 1234, title: 'Funny Game' })
Adds Achievement to the set, accepts same data as Achievement class constructor, but you're allowed to omit id when passing an object (id will be assigned automatically, similar to how RAIntegration does it).
Also returns current AchievementSet instance, allowing you to chain calls.
import { AchievementSet, define as $ } from '@cruncheevos/core'
const set = new AchievementSet({ gameId: 1234, title: 'Funny Game' })
set.addAchievement({
id: 58, // optional, or numeric string
title: 'My Achievement',
description: 'Do something funny',
points: 5,
badge: `local\\\\my_achievement.png`, // optional, or ID of badge on server
author: 'peepy', // optional and is not uploaded to server
conditions: {
core: [
['', 'Mem', '8bit', 0x00fff0, '=', 'Value', '', 0],
['', 'Mem', '8bit', 0x00fffb, '=', 'Value', '', 0],
],
alt1: $(
['', 'Mem', '8bit', 0x00fe10, '>', 'Delta', '8bit', 0x00fe10],
['', 'Mem', '8bit', 0x00fe11, '=', 'Value', '', 0],
),
alt2: '0=1'
}
}).addAchievement(...)
Adds Leaderboard to the set, accepts same data as Leaderboard class constructor, but you're allowed to omit id when passing an object (id will be assigned automatically, similar to how RAIntegration does it).
Also returns current AchievementSet instance, allowing you to chain calls.
import { AchievementSet, define as $ } from '@cruncheevos/core'
const set = new AchievementSet({ gameId: 1234, title: 'Funny Game' })
set.addLeaderboard({
id: 58, // optional, or numeric string
title: 'My Leaderboard',
description: 'Best score while doing something funny',
type: 'SCORE',
lowerIsBetter: false,
conditions: {
start: {
core: [
['', 'Mem', '8bit', 0x00fff0, '=', 'Value', '', 0],
['', 'Mem', '8bit', 0x00fffb, '=', 'Value', '', 0],
],
alt1: $(
['', 'Mem', '8bit', 0x00fe10, '>', 'Delta', '8bit', 0x00fe10],
['', 'Mem', '8bit', 0x00fe11, '=', 'Value', '', 0],
),
alt2: '0=1',
},
cancel: [
['', 'Mem', '16bit', 0x34684, '=', 'Value', '', 0x140]
], // same as providing an object: { core: [ ... ] }
submit: '0xH59d76=2',
value: [['Measured', 'Mem', '32bit', 0x34440, '*', 'Value', '', 2]],
},
}).addLeaderboard(...)
Allows to iterate the whole set for both achievements and leaderboards.
for (const asset of achSet) {
if (asset instanceof Achievement) {
// ...
}
if (asset instanceof Leaderboard) {
// ...
}
}
Returns string representation of AchievementSet suitable for
RACache/Data/GameId-User.txt
file.
First line is version, always set to 1.0, second line is game's title. Then come string representations of achievements and leaderboards, each sorted by id.
new AchievementSet({ gameId: 1234, title: 'Funny Game' })
.addAchievement(...)
.addAchievement(...)
.addLeaderboard(...)
.addLeaderboard(...)
.toString()
// may result in:
`
1.0
Funny Game
57:"0x cafe=102":Ach2:Desc2::::cruncheevos:2:::::00000
111000001:"0x cafe=101":Ach1:Desc1::::cruncheevos:1:::::00000
L58:"0x cafe=102":"0=1":"1=1":"M:0x feed":FRAMES:Lb2:Desc2:1
L111000001:"0x cafe=101":"0=1":"1=1":"M:0x feed":SCORE:Lb1:Desc1:0
`
Provides declarative API to produce Rich Presence string
import { define as $, RichPresence } from '@cruncheevos/core'
RichPresence({
lookupDefaultParameters: { keyFormat: 'hex', compressRanges: true },
// Wraps calls to RichPresence.format
format: {
Score: 'VALUE',
},
// Wraps calls to RichPresence.lookup
lookup: {
Song: {
// No need to specify name, it's taken from object
values: {
'*': 'Unknown value',
1: 'Same value',
2: 'Same value',
3: 'Same value',
},
// overrides lookupDefaultParameters.keyFormat
keyFormat: 'dec',
defaultAt: 0x100,
},
Mode: {
values: {
1: 'Mode 1',
2: 'Mode 2',
},
// overrides lookupDefaultParameters.compressRanges
compressRanges: false
},
},
// Callback function that must return an array of display strings.
// All the previously specified Lookups and Formats are provided
// through `lookup` and `format` objects respectively,
// along with the `tag` function to inject lookups into display strings.
displays: ({ lookup, format, macro, tag }) => [
[
$(['', 'Mem', '16bit', 0xcafe, '=', 'Value', '', 1]),
// Passing lookup.Song to this tagged template literal function causes
// `lookup.Song.at()` call with previosly set `defaultAt` value
tag`Cafe at value 1, Song: ${lookup.Song}, Mode: ${lookup.Mode.at(0x990)}`,
],
['0xCAFE=2', tag`Cafe at value 2, format example: ${format.Score.at(0x600)}`],
// `macro` is an object providing several pre-existing Formats
['0xCAFE=3', tag`Default macro test ${macro.Score.at('0xfeed')}`],
'Playing a good game',
],
})
`Format:Score
FormatType=VALUE
Lookup:Song
1-3=Same value
*=Unknown value
Lookup:Mode
0x1=Mode 1
0x2=Mode 2
Display:
?0x cafe=1?Cafe at value 1, Song: @Song(0x100), Mode: @Mode(0x990)
?0xCAFE=2?Cafe at value 2, format example: @Score(0x600)
?0xCAFE=3?Default macro test @Score(0xfeed)
Playing a good game`
Returns a string representing Rich Presence Display line
Does not check if provided arguments are of correct type
import { RichPresence } from '@cruncheevos/core'
RichPresence.display('0=1', 'Nothing is happening'))
// '?0=1?Nothing is happening'
Creates an object representing Rich Presence Format
import { RichPresence } from '@cruncheevos/core'
const format = RichPresence.format({
name: 'Score',
type: 'VALUE',
})
format.at('0xCAFE_v1') // '@Score(0xCAFE_v1)'
format.at($(['Measured', 'Mem', '16bit', 0xCAFE])) // '@Score(0xcafe)'
format.toString() // 'Format:Score\nFormatType=VALUE'
Creates an object representing Rich Presence Lookup
import { RichPresence } from '@cruncheevos/core'
const lookup = RichPresence.lookup({
name: 'Car',
keyFormat: 'hex',
values: {
1: 'First!',
2: 'Second!',
4: 'Same',
5: 'Same',
},
defaultAt: 0xfeed,
compressRanges: true
})
lookup.at() // '@Car(0xfeed)'
lookup.at('0xCAFE_v1') // '@Score(0xCAFE_v1)'
lookup.at($(['Measured', 'Mem', 'Float', 0xCAFE])) // '@Car(fFcafe)'
lookup.toString() // `Lookup:Car\n0x1=First!\n0x2=Second!\n0x4-0x5=Same'
Tagged template literal function which can accept Rich Presence Lookup instances. This allows for less noisy display strings.
import { RichPresence } from '@cruncheevos/core'
const lookup = RichPresence.lookup({ name: 'Song', defaultAddress: 0xfeed, values: { ... } })
RichPresence.tag`${lookup} - now playing` // '@Song(0xfeed) - now playing'
Provides an object containing default Rich Presence Macros
import { RichPresence } from '@cruncheevos/core'
RichPresence.macro.Score.at('0xCAFE') // '@Score(0xCAFE)'
RichPresence.macro.ASCIIChar.at('0xCAFE') // '@ASCIIChar(0xCAFE)'