Skip to content

Timelines

cengels edited this page Jul 14, 2024 · 4 revisions

The fight timelines are the main component of thaliak.com, and as a result also make up the majority of files in this repository. Each file maps to a single fight. In some cases (specifically door bosses) two files may be created for what the game considers only a single instance.

The file name directly maps to the URL the timeline will have on thaliak.com. Inside, each file is a massive YAML document containing at least the following properties:

Property Description
id The ID of the duty finder instance in the game files. This corresponds to the ContentFinderCondition in the game's EXD.
patch The patch in which this fight was released.
date The date the timeline was first created.
modified The date the timeline was last modified.
by An array of contributors. The id refers to their Lodestone ID.
boss The name of the boss to be fought in this fight. If multiple bosses are fought, this should be the final boss' name. Can later be referred to using [boss].
HP The total HP of the boss. In ultimates, set this to 0.
average_hp The average HP of non-tank party members when this fight was on content.
tier For savage fights, the full name of the tier the fight takes place in.
suffix Can be used to add a suffix to the name of the fight. Useful for fights that have a door boss, since those fights have the same name otherwise.
autos An object composed of a mechanic and type property that specify the type of auto-attacks in this fight.
description A short appetizer for the fight that is displayed at the top of the timeline.
status A dictionary of status effects to be found in the fight.
actions A dictionary of (enemy) actions to be found in the fight.
timeline A list of timestamps along with the action that takes place at that point in time.
graphing Graphing data (used to define interactive diagrams).

For more information on the properties that each status, action, etc. can have, check some of the existing timelines or review the JSON schema. Please be aware that Thaliak automatically retrieves whatever additional data it can from the game files itself, so properties such as an action's or status effect's name or its damage type should not be specified unless the game files do not provide this information themselves.

Full documentation on every available property may come later.

Timeline text

All descriptions within the timeline (including action descriptions, status descriptions, strategy descriptions, etc.) are post-processed. This allows you to use a limited form of Markdown (supporting bold, italics, and asterisk-prefixed lists) as well as certain placeholders that automatically replace themselves with various variables and buttons.

The following placeholders are valid:

Placeholder Example Description
[fight] [fight] Replaced with the name of the fight.
[boss] [boss] Replaced with the name of the boss.
[a:ACTION_ID] [a:blaster] Replaced with a button that opens a dialog to explain the action in more detail. ACTION_ID must be one of the keys in actions.
[a:ACTION_ID:c] [a:blaster:c] Like [a:ACTION_ID], but the name of the action is capitalized.
[a:ACTION_ID:d] [a:blaster:d] Replaced with the full description of the action.
[TEXT](a:ACTION_ID) [Blaster (spread)](a:blaster) Like [a:ACTION_ID], but instead of using the name of the action for the button, uses TEXT instead.
[s:STATUS_ID] [s:doom] Replaced with a button that opens a dialog to explain the status effect in more detail. STATUS_ID must be one of the keys in status.
[s:STATUS_ID:c] [s:doom:c] Like [s:STATUS_ID], but the name of the status effect is capitalized.
[s:STATUS_ID:d] [s:doom:d] Replaced with the full description of the status effect.
[s:STATUS_ID:N] [s:vuln-up:4] Like [s:STATUS_ID], but for status effects that can have multiple stacks, uses the icon for N stacks.
[TEXT](s:STATUS_ID) [Doom (5s)](s:doom) Like [s:STATUS_ID], but instead of using the name of the status effect for the button, uses TEXT instead.
[t:TERM(:d)(:c)] [t:AoE] [t:AoE:d] [t:wall:c] Replaced with a button that opens a dialog to explain the term in more detail. TERM must be a key from enums/terms.yaml.
[TEXT](t:TERM) [walling](t:wall) Like [t:TERM], but instead of using the name of the term for the button, uses TEXT instead.
[m:MECHANIC(:d)(:c)] [m:spread] [m:spread:d] [m:spread:c] Replaced with a button that opens a dialog to explain the mechanic type in more detail. MECHANIC must be a key from enums/mechanic-types.yaml.
[TEXT](m:MECHANIC) [walling](t:wall) Like [m:MECHANIC], but instead of using the name of the mechanic type for the button, uses TEXT instead.
[ms:SHAPE(:d)(:c)] [ms:cone] [ms:cone:d] [ms:cone:c] Replaced with a button that opens a dialog to explain the mechanic shape in more detail. SHAPE must be a key from enums/mechanic-shapes.yaml.
[TEXT](ms:SHAPE) [conal](ms:cone) Like [ms:SHAPE], but instead of using the name of the mechanic shape for the button, uses TEXT instead.
[st:STATUS_TYPE(:d)(:c)] [st:dot] [st:dot:d] [st:dot:c] Replaced with a button that opens a dialog to explain the status effect type in more detail. STATUS_TYPE must be a key from enums/status-types.yaml.
[TEXT](st:STATUS_TYPE) [damage over time](st:dot) Like [st:STATUS_TYPE], but instead of using the name of the status effect type for the button, uses TEXT instead.
[dt:DAMAGE_TYPE(:d)(:c)] [dt:physical] [st:physical:d] Replaced with a button that opens a dialog to explain the damage type in more detail. DAMAGE_TYPE must be a key from enums/damage-types.yaml.
[TEXT](dt:DAMAGE_TYPE) [physical damage](dt:physical) Like [dt:DAMAGE_TYPE], but instead of using the name of the damage type for the button, uses TEXT instead.
[i:ICON] [i:caster] Replaced with the specified icon. See below for a list of valid icons.
:unverified(TEXT) :unverified(always hits twice) Highlights a given piece of text with the disclaimer that the information is unverified and may be false.

In addition, all HTML is valid in descriptions but should be avoided if possible.

Valid icons

The following values are valid for the icon ([i:ICON]) placeholder:

  • tank [i:tank]
  • healer [i:healer]
  • dps [i:dps]
  • melee [i:melee]
  • ranged [i:ranged]
  • pranged [i:pranged]
  • caster [i:caster]
  • circle [i:circle]
  • cross [i:cross]
  • square [i:square]
  • triangle [i:triangle]

Tools for writing a timeline

It is strongly recommended that you use Visual Studio Code in conjunction with the Red Hat YAML Language Support extension to get code completion and syntax highlighting for the basic structure of a timeline file and see errors when your file violates the schema.

Additionally, we now have our very own language server, which further enhances the timeline editing experience by adding syntax highlighting, code completion, and symbol resolution for placeholders (such as [a:mountain-fire]).

Generating a timeline

Since timelines use millisecond-precision for each action, writing one from scratch would be a ludicrous (and borderline impossible) amount of work. To lay the groundwork for your timeline, use the provided timeline-generator utility. Clone the repository, then follow the instructions in the README to automatically generate a full scaffold for a timeline from an fflogs log.

The utility works by extracting all enemy actions (under the Casts tab in fflogs). However, the utility isn't perfect, so there are some gotchas, such as the following:

  • To ensure all actions from the fight are included in the timeline, you should use a log that wipes to enrage. For fights with multiple enrages (such as ultimates), you should pick one log per enrage and manually merge them afterwards. Since the exact timestamps of each of these logs are likely to be different, you can use the provided adjust-timestamps.mjs script to add an offset to all timestamps of a later log to bring it back in line with the timestamps you already have.
  • Whenever it encounters a new action, it automatically generates a new entry under the actions property in the YAML, using a kebab-cased version of the action as its key. If there are multiple actions with different in-game IDs but the same name, the utility adds a ...-1, ...-2, etc to the key. You should check if the actions are actually distinct enough to warrant unique entries in the actions table, and if so, give them a new, more descriptive key and name.
  • The utility automatically adds entries to an action's children if multiple actions with the same name appear in sequence. In some cases, this can mistakenly lead to a circular recursion, where an action is simultaneously a child and its parent. You must resolve such issues manually.
  • Beyond that, the utility does not automatically add entries to an action's children, so most of the time you'll have to define an action's children yourself. You can use the provided make-children.mjs script to convert entries with an absolute timestamp (under timeline:) to entries with a relative timestamp (under children:).
  • For actions that deal damage, the utility automatically generates the average damage number (excluding tanks and any outliers, for instance party members with an unintended vuln stack) and writes it into the YAML. Despite this, you should still check each damage value to ensure it looks plausible.
  • Status effects are not processed by the utility and must be added yourself.
Clone this wiki locally