Ghosts¶
The leaderboard ghosts of the track on screen: the top entries of its Steam leaderboard and your own, each with the
path its ball took. Everything here needs host 0.14.0. Loading takes a few seconds (every replay is a download); read
State to follow it. Nothing here races: loading ghosts never touches your own run.
Load¶
bool Ghosts::Load(const string &in leaderboard = "", int count = 25)
Starts loading the top count entries of a leaderboard (0 for every entry: each replay is about 80 KB, so a
popular track's whole leaderboard is a long download), plus your own entry. leaderboard "" is the leaderboard of
the track on screen (the game's own tracks and workshop tracks alike); a name loads that leaderboard instead (the
game's tracks use their level name, "Map_LethTrial_01"). Loading the same leaderboard again keeps the replays
already loaded that are still wanted and downloads only the rest; another leaderboard replaces them. False if it
can't start.
Example
Ghosts::Load("", 25); // the top 25 of the track on screen
Ghosts::Load("", 100); // then 75 more
Ghosts::Load("", 0); // then everyone
LoadFile¶
int Ghosts::LoadFile(const string &in file)
Runs from a text file in the plugin's own folder instead of a leaderboard, replacing the runs loaded (anything still
downloading is dropped; Leaderboard() reads "local"). Every other Ghosts function works on them. Returns how many
runs it read, or -1 if the file can't be read, isn't this format, or isn't the plugin's own (the same rule as
Plugins::ReadFile). The format, tab-separated:
RUN1 ox oy oz unit
name time skin t0 dt samples
The first line gives an origin and a unit (game units, cm); each next line is a run: its name, its time in seconds,
a ball skin material path (- for none), the first sample's time and the time between samples, then the samples:
base64 of signed 16-bit x, y, z per sample, a position of origin + value x unit. Speed and the camera's view are
worked out from the path. Requires host 0.25.0.
Example
int runs = Ghosts::LoadFile("runs/best.txt");
PrefetchFile¶
void Ghosts::PrefetchFile(const string &in file)
Starts reading and parsing a LoadFile file in the background, so a later LoadFile of it is quick (for stepping
through many files). Requires host 0.25.0.
Example
Ghosts::PrefetchFile("runs/" + (shown + 1) + ".txt");
PrefetchReady¶
bool Ghosts::PrefetchReady(const string &in file)
Whether a PrefetchFile is done. LoadFile works either way; before this is true it reads the file itself.
Requires host 0.25.0.
Example
if (Ghosts::PrefetchReady(next))
Ghosts::LoadFile(next);
CrowdTrailDetail¶
void Ghosts::CrowdTrailDetail(int points, double budgetMs)
How detailed crowd trails are: points per trail at most (8 to 2000, default 160) and the time spent building them each frame (0.5 to 40 ms, default 3). For every crowd. Requires host 0.25.0.
Example
Ghosts::CrowdTrailDetail(60, 2); // lighter trails for many runs
CrowdTrailsPending¶
bool Ghosts::CrowdTrailsPending()
Whether any of your crowds' trails are still waiting to be built. Requires host 0.25.0.
Example
loading.visible = Ghosts::CrowdTrailsPending();
CrowdTrailStyle¶
bool Ghosts::CrowdTrailStyle(int id, bool glow, float brightness, double fadeSeconds = 0, float fadeMin = 0)
How the crowd's trails look: glowing at a brightness (opaque trails only; set before CrowdTrails), and, for
see-through trails built in chunks, each chunk fading with age, from the trails' opacity down to fadeMin over
fadeSeconds of the clock given to CrowdTrailsUpTo (0: no fading). False if it isn't your crowd. Requires host
0.25.0.
Example
Ghosts::CrowdTrailStyle(crowd, false, 1, 3.0, 0.05f); // the last 3 s of each trail, fading out
Ghosts::CrowdTrails(crowd, 4, 0.5f, 0.5);
CrowdBallScale¶
bool Ghosts::CrowdBallScale(int id, double scale)
The size of the crowd's balls that wear skins (CrowdSkins), 1 the game's ball. Requires host 0.25.0.
Example
Ghosts::CrowdBallScale(crowd, 0.8);
CrowdGroupGlass¶
bool Ghosts::CrowdGroupGlass(int id, int group, float opacity)
One colour group's balls as tinted glass of its colour at opacity (below 1), for dimming some balls; they then keep
their colour when the crowd wears skins. 1 makes them glow again. False if the crowd or group isn't there. Requires
host 0.25.0.
Example
Ghosts::CrowdGroupGlass(crowd, 1, 0.3f); // group 1 dimmed
State¶
string Ghosts::State()
How the load is going: "idle", "loading ..." (with what it's doing), "ready", or "error: ...".
Example
if (Ghosts::State() == "ready")
Log::Info(Ghosts::Count() + " ghosts loaded");
Leaderboard¶
string Ghosts::Leaderboard()
The Steam leaderboard's name, once it's known.
Example
titleText.text = Ghosts::Leaderboard();
Entries¶
int Ghosts::Entries()
How many entries the leaderboard has in all, once the top entries have arrived (0 until then).
Example
statusText.text = Ghosts::Count() + " of " + Ghosts::Entries();
WithoutReplay¶
int Ghosts::WithoutReplay()
How many of the entries asked for have no replay file on Steam, so there is nothing to load for them: why loading the top 1000 can give 994 runs.
Example
string missing = Ghosts::WithoutReplay() > 0 ? Ghosts::WithoutReplay() + " have no replay" : "";
Count¶
int Ghosts::Count()
How many ghosts have arrived, in rank order. The list grows while replays download.
Example
for (int i = 0; i < Ghosts::Count(); i++)
Log::Info(Ghosts::Rank(i) + ". " + Ghosts::Name(i));
Name¶
string Ghosts::Name(int)
The player's name, as saved with their replay.
Example
Log::Info(Ghosts::Name(0) + " holds the record");
Rank¶
int Ghosts::Rank(int)
The entry's global rank on the leaderboard (1 is the record).
Example
int rank = Ghosts::Rank(i);
Time¶
double Ghosts::Time(int)
The run's time in seconds.
Example
timeText.text = formatFloat(Ghosts::Time(i), "", 0, 3);
IsOwn¶
bool Ghosts::IsOwn(int)
Whether the entry is yours.
Example
if (Ghosts::IsOwn(i))
nameText.text += " (you)";
SampleCount¶
int Ghosts::SampleCount(int)
How many samples the replay has: the ball's position about ten times a second.
Example
int samples = Ghosts::SampleCount(i);
Sample¶
bool Ghosts::Sample(int, int, double &out, double &out, double &out, double &out)
One sample of a replay: its time from the start and where the ball was. False if there's no such sample.
Example
// the whole path, for Draw::Tube
array<double> path;
double t, x, y, z;
for (int j = 0; j < Ghosts::SampleCount(i); j++)
if (Ghosts::Sample(i, j, t, x, y, z))
{
path.insertLast(x);
path.insertLast(y);
path.insertLast(z);
}
Position¶
bool Ghosts::Position(int, double, double &out, double &out, double &out)
Where the ball was at a time from the start of the run, between samples along a straight line (before the start or after the end: the first or last sample).
Example
double x, y, z;
if (Ghosts::Position(i, playTime, x, y, z))
Draw::Move(ball, x, y, z);
Splits¶
array<double>@ Ghosts::Splits(int)
The times the run took its checkpoints, in the order it took them.
Example
array<double>@ splits = Ghosts::Splits(i);
CheckpointOrder¶
array<int>@ Ghosts::CheckpointOrder(int)
The numbers of the checkpoints the run took, in order: for each split, the checkpoint nearest to where the ball was then. Two runs with the same order took the same route. 0 for a split no checkpoint matched.
Example
array<int>@ order = Ghosts::CheckpointOrder(i);
string route = "";
for (uint k = 0; k < order.length(); k++)
route += (k == 0 ? "" : ",") + order[k];
CheckpointCount¶
int Ghosts::CheckpointCount()
How many checkpoints the track on screen has (the finish is one of them).
Example
Log::Info(Ghosts::CheckpointCount() + " checkpoints");
PlayerBall¶
int Ghosts::PlayerBall(int ghost, bool realSkin = false)
The player's own ball for a ghost, as the game shows other players' balls: their skin (or special ball), their
accessory, see-through like the game's ghosts, with their name over it. With realSkin true, it wears the player's
real skin and accessory, fully opaque, instead of the game's rival-ghost look (needs host 0.25.0). Returns a Draw id
(show, hide and remove it like a shape), or 0 if it couldn't be made. Move it with PlaceBall.
Example
int ball = Ghosts::PlayerBall(0);
int real = Ghosts::PlayerBall(0, true); // as the player sees their own ball
PlaceBall¶
bool Ghosts::PlaceBall(int id, int ghost, double time)
Puts a ball from PlayerBall where that ghost was at time, turning the way their ball turned.
Example
Ghosts::PlaceBall(ball, 0, time);
ShowBallName¶
bool Ghosts::ShowBallName(int id, bool shown)
Shows or hides the name over a ball from PlayerBall.
Example
Ghosts::ShowBallName(ball, false);
CrowdCreate¶
int Ghosts::CrowdCreate(double radius, const array<float>@ palette)
A crowd: many ghosts' balls drawn at once, for thousands of them, where a ball each would be too many objects. The
balls are glowing spheres of radius, in colour groups (palette: red, green, blue for each group). Returns a
Draw id (remove it like a shape), or 0.
Example
array<float> palette = {1, 0.2f, 0.2f, 0.2f, 0.6f, 1}; // two groups: red, blue
int crowd = Ghosts::CrowdCreate(25, palette);
CrowdMembers¶
bool Ghosts::CrowdMembers(int id, const array<int>@ ghosts, const array<int>@ groups)
Which ghosts are in the crowd, and each one's colour group (by place in the palette).
Example
array<int> who = {30, 31, 32}, groups = {0, 1, 0};
Ghosts::CrowdMembers(crowd, who, groups);
CrowdSkins¶
bool Ghosts::CrowdSkins(int id)
Puts the crowd's members in their players' own skins: a ball the game's size, one instanced mesh per skin. They
don't roll. A member whose skin doesn't load keeps its colour. Call it after CrowdMembers.
Example
Ghosts::CrowdSkins(crowd);
CrowdTrails¶
bool Ghosts::CrowdTrails(int id, double radius, float opacity, double chunkSeconds = 0)
A see-through trail for each member shown (CrowdTimes), in its colour group's colour (radius in cm, opacity from 0
to 1, as Draw::Tube's). They are built a few each frame, top ranks first, so there's no hitch. Several trails share
one mesh, so they can't be hidden one by one: call it again after CrowdTimes changes who's shown, and they're built
again for those.
Example
Ghosts::CrowdTrails(crowd, 4, 0.25f);
CrowdTrailsUpTo¶
bool Ghosts::CrowdTrailsUpTo(int id, double time)
With the trails built in chunks (CrowdTrails' last argument, in seconds), shows only the chunks each run has
finished by time on its own clock: the trails so far, for a trail that grows as the balls roll.
Example
Ghosts::CrowdTrails(crowd, 4, 0.25f, 1.0);
Ghosts::CrowdTrailsUpTo(crowd, playTime); // every frame
CrowdShowTrails¶
bool Ghosts::CrowdShowTrails(int id, bool shown)
Shows or hides the crowd's trails; ones still being built follow.
Example
Ghosts::CrowdShowTrails(crowd, false);
CrowdTimes¶
bool Ghosts::CrowdTimes(int id, const array<double>@ offsets, const array<bool>@ shown)
Per ghost (indexed by ghost, for every ghost loaded): its time offset (added to the time CrowdPlace is given), and
whether it's shown.
Example
Ghosts::CrowdTimes(crowd, offsets, shown);
CrowdPlace¶
bool Ghosts::CrowdPlace(int id, double time)
Puts every ball of the crowd where its ghost is at time (plus its offset), in one go. Call it every frame.
Example
Ghosts::CrowdPlace(crowd, time);
View¶
bool Ghosts::View(int, double, double &out, double &out, double &out, double &out, double &out, double &out)
Where a ghost's player was looking at time t: their camera as the game would have placed it (the ball's camera,
turned the way they turned it), for watching the run as they saw it. Gives the camera's position, pitch, yaw and
field of view, ready for Camera::Set. The game's camera also pulls in when it would go through the track; this
doesn't. False if the replay didn't record where they looked.
Example
double x, y, z, pitch, yaw, fov;
if (Ghosts::View(0, time, x, y, z, pitch, yaw, fov))
Camera::Set(x, y, z, pitch, yaw, fov);
Checkpoint¶
bool Ghosts::Checkpoint(int, int &out, double &out, double &out, double &out)
One of the track's checkpoints, by index (0 to CheckpointCount - 1, in order of their number): its number and where it is. Checkpoints a track doesn't number (the game's own tracks, such as Leth Trial, leave them all unnumbered) are numbered 1, 2, 3 ... here, so each still has a number of its own.
Example
int number;
double x, y, z;
if (Ghosts::Checkpoint(0, number, x, y, z))
Log::Info("checkpoint " + number + " at " + x + ", " + y + ", " + z);
ReplayBall¶
int Ghosts::ReplayBall(const string &in replay, bool realSkin = false)
A ghost's ball from replay text (the game's ghost JSON, e.g. downloaded from a plugin's own server): the game's
player ball dressed as that replay's player, with their name tag, in the game's rival-ghost look (realSkin: their
own skin at full opacity). A Draw id (remove it with Draw::Remove), or 0 if the text isn't a replay. Place it with
PlaceReplayBall. Requires host 0.26.0.
Example
int ghost = Ghosts::ReplayBall(Net::Body(request));
PlaceReplayBall¶
bool Ghosts::PlaceReplayBall(int id, double time)
Puts a replay ball where its run was at time t (seconds), turning as it turned: call it every frame with the race's
clock (Runs::Elapsed()) to race against it. False once the ball is gone (a new map, or removed). Requires host 0.26.0.
Example
double t = Runs::Elapsed();
Ghosts::PlaceReplayBall(ghost, t < 0 ? 0 : t);
ReplayTime¶
double Ghosts::ReplayTime(int id)
A replay ball's run time (its bestTime, seconds), -1 for no such ball. Requires host 0.26.0.
Example
Log::Info("ghost time " + Ghosts::ReplayTime(ghost));
ReplayName¶
string Ghosts::ReplayName(int id)
The player name in a replay ball's replay. Requires host 0.26.0.
Example
Log::Info("racing " + Ghosts::ReplayName(ghost));