Files
horse/zenno/src/routes/doc/mdb/+page.svelte
T
zephyr 8850dd4a08
ci/woodpecker/push/horsebot Pipeline was successful
ci/woodpecker/push/zenno Pipeline failed
zenno/doc/mdb: document skills
2026-09-07 16:12:32 -04:00

1041 lines
44 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import * as skill from '$lib/data/skill';
import Article from '../Article.svelte';
import Sec from '../Sec.svelte';
import Mono from '../Mono.svelte';
import { onMount } from 'svelte';
import { SvelteMap } from 'svelte/reactivity';
import Skill from '$lib/Skill.svelte';
import { resolve } from '$app/paths';
let skillMap = new SvelteMap<number, skill.Skill>();
onMount(async () => {
const sk = await skill.skills();
skillMap.clear();
for (const s of sk) {
skillMap.set(s.skill_id, s);
}
});
const skills = $derived({
rickey: skillMap.get(100981),
fall2: skillMap.get(200191),
fall1: skillMap.get(200192),
fallX: skillMap.get(200193),
fallR: skillMap.get(200194),
g1Averse: skillMap.get(200311),
focus: skillMap.get(200432),
restraint: skillMap.get(202161),
oppression: skillMap.get(202362),
});
const fallGroup = $derived(skills.fall1?.group);
const skillCategories = [
[5, 'Unique'],
[0, 'Passive'],
[1, 'Early-Race'],
[2, 'Mid-Race'],
[3, 'Late-Race'],
[4, 'Anytime'],
] as const;
const tagMeanings: [number, string][] = [
[101, 'Front Runner'],
[102, 'Pace Chaser'],
[103, 'Late Surger'],
[104, 'End Closer'],
[201, 'Sprint'],
[202, 'Mile'],
[203, 'Medium'],
[204, 'Long'],
[301, 'Early-Race'],
[302, 'Mid-Race'],
[303, 'Late-Race'],
[401, 'Affects speed stat or velocity'],
[402, 'Affects stamina stat or HP'],
[403, 'Affects power stat, acceleration, or lane movement speed'],
[404, 'Affects guts stat'],
[405, 'Affects wit stat or vision'],
[406, "Debuff of any type, or a main story self-debuff, or Behold Thine Emperor's Divine Might"],
[407, 'Modifies gate delay'],
[501, 'Turf'],
[502, 'Dirt'],
[601, 'Ground condition passive'],
[602, 'Weather passive'],
[603, 'Season passive'],
[604, 'Race track passive'],
[605, 'Time of day passive'],
[606, 'Exchange race passive'],
[607, 'Distance passive'],
[608, 'Corner-based passive'],
[609, 'Gate position passive'],
[610, 'Ground type passive'],
[611, 'Popularity passive'],
[612, 'Running style passive'],
[613, "Passive depending on other horses' styles or skills"],
[614, 'Base stat threshold passive'],
[615, 'Mood passive'],
[801, 'URA Finale scenario skill'],
[802, 'Unity Cup scenario skill'],
[803, 'Grand Concert scenario skill'],
[804, 'Trackblazer scenario skill'],
];
const durScalingTypes = Object.entries(skill.DURATION_SCALE_NAME).map(([id, name]) => [id, name || 'direct (no scaling)']);
const abilityTypes = [
[skill.AbilityType.Speed, 'Speed stat', '+n'],
[skill.AbilityType.Stamina, 'Stamina stat', '+n'],
[skill.AbilityType.Power, 'Power stat', '+n'],
[skill.AbilityType.Guts, 'Guts stat', '+n'],
[skill.AbilityType.Wit, 'Wit stat', '+n'],
[skill.AbilityType.Runaway, 'Become Runaway'],
[7, 'HpDecRate (unused)'],
[skill.AbilityType.Vision, 'Vision range', '+m'],
[skill.AbilityType.HP, 'HP', '+ proportion of max HP'],
[skill.AbilityType.GateDelay, 'Adjust gate delay', '×'],
[11, 'ForceOvertakeIn (unused)'],
[12, 'ForceOvertakeOut (unused)'],
[skill.AbilityType.Frenzy, 'Set Rushed duration', 's'],
[skill.AbilityType.AddGateDelay, 'Set gate delay', 's'],
[skill.AbilityType.CurrentSpeed, 'Current speed (debuffs)', '+m/s'],
[skill.AbilityType.CurrentSpeedWithDecel, 'Current speed (buffs)', '+m/s'],
[skill.AbilityType.TargetSpeed, 'Target speed', '+m/s'],
[skill.AbilityType.LaneSpeed, 'Lane change speed', '+CW/s'],
[skill.AbilityType.RushedChance, 'Rushed chance', '+ flat%'],
[30, 'PushPer (unused)'],
[skill.AbilityType.Accel, 'Acceleration', '+m/s²'],
[skill.AbilityType.AllStatus, 'All stats', '+n'],
[skill.AbilityType.LaneChange, 'Set target lane', 'CW'],
[36, 'ActivateRandomNormalAndRareSkill (unused)'],
[skill.AbilityType.ActivateRare, 'Activate rare skills', 'count'],
[38, 'Gain immunity to debuffs'],
[41, 'Activate Sympathy (?)'],
[42, 'Evolved Skill duration (?)', '× (?)'],
[48, 'Zenkai Spurt acceleration (?)', '+m/s² (?)'],
[49, 'Reactivate Unique Skill (?)'],
[501, 'Carnival Bonus effect (old)'],
[502, 'Carnival Bonus stats up'],
[503, 'Carnival Bonus mood up'],
] as const;
const valScalingTypes = Object.entries(skill.ABILITY_SCALE_NAME).map(([id, name]) => [id, name || 'direct (no scaling)']);
const addlActivateTypes = [
[0, 'none'],
[1, 'upon pass, up to three times'],
[2, 'upon activating other skills, up to three times'],
[3, 'upon activating other skills, up to twice'],
] as const;
const levelUsageTypes = [
[0, 'none', 'Hokko Tarumae and Halloween Digitan (on empty abilities)'],
[1, 'default', 'almost all abilities'],
[2, 'ignored', 'Chrono Genesis (on evo duration scaling)'],
[3, 'inverse', 'unused'],
] as const;
const sparkTypes = [
[1, 'Stat', 'spark-color-blue'],
[2, 'Aptitude', 'spark-color-pink'],
[3, 'Unique', 'spark-color-green'],
[4, 'Skill', 'spark-color-white'],
[5, 'Race', 'spark-color-white'],
[6, 'Scenario', 'spark-color-white'],
[7, 'Carnival Bonus', 'spark-color-red'],
[8, 'Distance', 'spark-color-white'],
[9, 'Hidden', 'spark-color-white'],
[10, 'Surface', 'spark-color-white'],
[11, 'Style', 'spark-color-white'],
] as const;
const targetTypes = [
[skill.Target.Self, 'self'],
[skill.Target.Sympathizers, 'everyone'],
[3, 'AllOtherSelf (unused)'],
[skill.Target.InView, 'visible', 'maximum targets'],
[5, 'RandomOtherSelf (unused)'],
[6, 'Order (unused)'],
[skill.Target.Frontmost, 'frontmost', 'maximum targets'],
[8, 'furthest back (unused)'],
[skill.Target.Ahead, 'ahead of the user', 'maximum targets'],
[skill.Target.Behind, 'behind the user', 'maximum targets'],
[skill.Target.AllTeammates, 'all teammates'],
[12, 'Near (unused)'],
[13, 'SelfAndBlockFront (unused)'],
[14, 'BlockSide (unused)'],
[15, 'NearInfront (unused)'],
[16, 'NearBehind (unused)'],
[17, 'RunningStyle (unused)'],
[skill.Target.Style, 'others using the given running style', 'running style ID'],
[skill.Target.RushingAhead, 'those ahead who are Rushed'],
[skill.Target.RushingBehind, 'those behind who are Rushed'],
[skill.Target.RushingStyle, 'those using the given running style who are Rushed', 'running style ID'],
[skill.Target.Character, 'specific character', 'character ID'],
[skill.Target.Triggering, 'whosoever triggered the skill'],
] as const;
const sparkEffects = [
[1, 'Speed', 'Amount'],
[2, 'Stamina', 'Amount'],
[3, 'Power', 'Amount'],
[4, 'Guts', 'Amount'],
[5, 'Wit', 'Amount'],
[6, 'Skill Points', 'Amount'],
[7, 'Random Stat', 'Amount', '(always 1)'],
[11, 'Turf Aptitude', 'Levels'],
[12, 'Dirt Aptitude', 'Levels'],
[21, 'Front Runner Aptitude', 'Levels'],
[22, 'Pace Chaser Aptitude', 'Levels'],
[23, 'Late Surger Aptitude', 'Levels'],
[24, 'End Closer Aptitude', 'Levels'],
[31, 'Sprint Aptitude', 'Levels'],
[32, 'Mile Aptitude', 'Levels'],
[33, 'Medium Aptitude', 'Levels'],
[34, 'Long Aptitude', 'Levels'],
[41, 'Skill Hint', 'Skill ID', 'Hint Levels'],
[51, 'Carnival Bonus', 'Skill ID', '(always 1)'],
[61, 'Max Speed', 'Amount'],
[62, 'Max Stamina', 'Amount'],
[63, 'Max Power', 'Amount'],
[64, 'Max Guts', 'Amount'],
[65, 'Max Wit', 'Amount'],
] as const;
const lovelySpringBreeze = [
[1469, 107401, 1, 41, 900741, 1],
[2521, 107401, 1, 62, 1, 0],
[2523, 107401, 1, 64, 1, 0],
[2522, 107401, 1, 65, 1, 0],
[1470, 107401, 2, 41, 900741, 2],
[2524, 107401, 2, 62, 2, 0],
[2526, 107401, 2, 64, 1, 0],
[2525, 107401, 2, 65, 1, 0],
[1471, 107401, 3, 41, 900741, 2],
[2528, 107401, 3, 62, 2, 0],
[2530, 107401, 3, 64, 1, 0],
[2529, 107401, 3, 65, 2, 0],
[2527, 107401, 4, 41, 900741, 3],
[2532, 107401, 4, 62, 2, 0],
[2534, 107401, 4, 64, 2, 0],
[2533, 107401, 4, 65, 2, 0],
] as const;
const successionInitialFactor = [
[1, 1, 1, 0, 5],
[2, 1, 2, 0, 12],
[3, 1, 3, 0, 21],
[4, 2, 1, 3, 1],
[5, 2, 4, 6, 2],
[6, 2, 7, 9, 3],
[7, 2, 10, 999, 4],
[8, 1, 1, 1, 4],
[9, 1, 2, 1, 9],
[10, 1, 3, 1, 16],
[11, 3, 1, 1, 0],
[12, 3, 2, 2, 0],
[13, 3, 3, 4, 0],
] as const;
const supportCardEffects = [
[1, 'Friendship Bonus'],
[2, 'Mood Effect'],
[3, 'Speed Bonus'],
[4, 'Stamina Bonus'],
[5, 'Power Bonus'],
[6, 'Guts Bonus'],
[7, 'Wit Bonus'],
[8, 'Training Effectiveness'],
[9, 'Initial Speed'],
[10, 'Initial Stamina'],
[11, 'Initial Power'],
[12, 'Initial Guts'],
[13, 'Initial Wit'],
[14, 'Initial Friendship Gauge'],
[15, 'Race Bonus'],
[16, 'Fan Bonus'],
[17, 'Hint Levels'],
[18, 'Hint Frequency'],
[19, 'Specialty Priority'],
[20, 'Max Speed'],
[21, 'Max Stamina'],
[22, 'Max Power'],
[23, 'Max Guts'],
[24, 'Max Wit'],
[25, 'Event Recovery'],
[26, 'Event Effectiveness'],
[27, 'Failure Protection'],
[28, 'Energy Cost Reduction'],
[29, 'Minigame Effectiveness'],
[30, 'Skill Point Bonus'],
[31, 'Wit Friendship Recovery'],
] as const;
</script>
<Article>
{#snippet head()}
<Sec h={1} id="top" class="text-center">Master Data</Sec>
<p>
This article is my personal notes on Umamusume's master data, a.k.a. master.mdb or just "the mdb." Most of the notes are
oriented toward my work on this website. It is an in-progress translation of <a
href="https://git.sunturtle.xyz/zephyr/horse/src/branch/main/doc">my broader but more sparse notes</a
>
into a more agreeable, navigable, and consistent form.
</p>
<p>
master.mdb is an SQLite database that the game downloads to your device. On Windows, you can find it at <Mono
>%USERPROFILE%\AppData\LocalLow\Cygames\Umamusume\master\master.mdb</Mono
>. Hakuraku has a
<a href="https://hakuraku.moe/masterdata" target="_blank" rel="noopener noreferrer">master data explorer</a> that you can use
on the website.
</p>
<p>
Generally speaking, I am only documenting what I feel is important, both in terms of tables and columns. That does
occasionally include documenting some things as being unimportant.
</p>
<p>
Roughly speaking, master data contains all the numbers needed to display the game's user interface, including during races.
Skill data, support card effects, Grand Concert lesson definitions, and so on are in there. If trying to access some data
in-game shows a "Connecting..." indicator while it loads, it probably isn't in the mdb. Notably, this includes everything
related to career event outcomes. (Event information on sites like GameTora and GameWith are massive crowd-sourced efforts.)
</p>
<p>
This document is oriented toward programmers with understanding of SQL databases. Mentions of <Mono>Gallop::</Mono> are references
to types visible in decompilations of the game, but no C# knowledge is needed.
</p>
{/snippet}
<Sec h={2} id="text_data">text_data</Sec>
<p>
The <Mono>text_data</Mono> table contains most non-story strings in the game. Queries look up entries by <Mono>category</Mono> and
<Mono>index</Mono>, i.e. <Mono>SELECT "text" FROM text_data WHERE category=? AND "index"=?</Mono>.
</p>
<p>Some important categories:</p>
<ul class="ml-4 list-disc pb-4">
<li>6 is character names, 4 is [variant] character name, 5 is [variant], 14 is clothing names.</li>
<li>47 is skill names, 48 is skill descriptions.</li>
<li>75 is support card names including variant, 76 is support card variant, 77 is support card character.</li>
<li>147 is spark names, 172 is spark descriptions.</li>
<li>33 is race name by race ID, 28 is race name by race instance ID, 31 is race courses, 111 is saddle names.</li>
<li>65 is player titles, 66 is title descriptions.</li>
<li>
119 is scenario full titles (e.g. <Mono>The Beginning: URA Finale</Mono>), 120 is scenario descriptions, 237 is scenario
names (e.g. <Mono>URA Finale</Mono>).
</li>
<li>130 is epithet names, 131 is epithet descriptions.</li>
</ul>
<p>
Other categories are usually easy enough to figure out by inspection. Text data categories have proper names given in <Mono
>Gallop::MasterString.Category</Mono
>, but a few categories that have useful strings are missing from there.
</p>
<Sec h={2} id="skills">Skills</Sec>
<p>
Relevant <Mono>text_data</Mono> categories include 47 for skill names and 48 for skill descriptions. Otherwise, info for skills
lives in <Mono>skill_data</Mono>.
</p>
<ul class="ml-4 list-disc pb-4">
<li>Skill ID is just <Mono>id</Mono>.</li>
<li>
<Mono>rarity</Mono> is 1 for common (white) skills, 2 for rare (gold) skills, 3 for 1&star;/2&star; uniques, 4 for 3&star; uniques
of horses upgraded from 1&star; or 2&star;, and 5 for full 3&star; uniques.
</li>
<li>
<Mono>group_id</Mono> groups skills that replace each other when purchased. E.g., <Skill skill={skills.fall1} />, <Skill
skill={skills.fall2}
/>, <Skill skill={skills.fallX} />, and <Skill skill={skills.fallR} />
all have a <Mono>group_id</Mono> of {fallGroup}. Unique skills are generally alone in their groups.
</li>
<li>
<Mono>group_rate</Mono> gives the relative strength of a skill within its group. Negative (purple) skills always have a <Mono
>group_rate</Mono
> of -1. For skills that have non-purple versions (i.e. not skills like <Skill skill={skills.g1Averse} />), the base version
has a <Mono>group_rate</Mono> of 1 and each successive upgrade is 1 higher. E.g., <Skill skill={skills.fall1} /> is 1, <Skill
skill={skills.fall2}
/> is 2, <Skill skill={skills.fallR} />, and <Skill skill={skills.fallX} /> is -1. Skills that have only common and rare versions
instead assign a <Mono>group_rate</Mono> of 2 to the rare.
</li>
<li>
<Mono>grade_value</Mono> is the amount the skill adds to a horse's rating when owned. Higher tiers of skills within a group replace
the lower ones rather than adding to them, so <Skill skill={skills.fall2} /> is worth its listed grade value of 174, not <Skill
skill={skills.fall1}
/>'s 129 plus 174.
</li>
<li>
<Mono>skill_category</Mono> gives the category where the skill is listed in the skills tab at the paddock:
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Category ID</th>
<th scope="col" class="px-2">Category</th>
</tr>
</thead>
<tbody>
{#each skillCategories as [cat, name] (cat)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{cat}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
</li>
<li>
<Mono>tag_id</Mono> gives a slash-separated list of three-digit tag values that various other mechanics can match against. E.g.,
Copano Rickey's unique skill <Skill skill={skills.rickey} /> counts the number of skills activated with tag values between 601
and 615 to determine the multiplier on its extra effects; mechanics like GL shop skills and MANT rivals match tags for running
style (101 to 104), distance (201 to 204), and/or surface (501 and 502); and I expect that support card unique effects like Mejiro
Ramonu will count "speed-increasing skills" by looking for tag 401.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Tag ID</th>
<th scope="col" class="px-2">Inferred Meaning</th>
</tr>
</thead>
<tbody>
{#each tagMeanings as [tag, name] (tag)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{tag}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
E.g., <Skill skill={skills.fallR} /> has a <Mono>tag_id</Mono> of <Mono>401/403/603</Mono> (as text, including the slashes). The
<a href={resolve('/doc/tagged-skills')}>tagged skills explorer</a> allows you to see skills by their tags.
</li>
<li>
For inherited unique skills, <Mono>unique_skill_id_1</Mono> holds the ID of the non-inherited version. When it is the inherited
unique from an upgraded 1&star;/2&star; horse, <Mono>unique_skill_id_2</Mono> holds the ID of the 1&star;/2&star; version of the
unique.
</li>
<li><Mono>exp_type</Mono> appears to be 1 when the skill is able to gain levels, i.e. is a unique skill.</li>
<li><Mono>activate_lot</Mono> is 1 if the skill uses a wit check.</li>
<li><Mono>disp_order</Mono> is the sort order used in the skill purchase menu.</li>
<li><Mono>icon_id</Mono> is the resource ID of the skill's icon.</li>
<li>There are quite a few other columns which may be useful but which I have not bothered to figure out.</li>
</ul>
<p>
<Mono>skill_data</Mono> additionally holds skill conditions and effects, described in the next section.
</p>
<Sec h={3} id="skill-details">Skill Details</Sec>
<p>
Skills have one or two <i>skill details</i>, also called <i>triggers</i>. The first skill detail has column names with <Mono
>_1</Mono
> appended, and the second has <Mono>_2</Mono>. Each skill detail has:
</p>
<ul class="ml-4 list-disc pb-4">
<li>
<Mono>precondition_1</Mono> (resp. <Mono>precondition_2</Mono>): Precondition text, if the skill has one, otherwise empty.
</li>
<li>
<Mono>condition_1</Mono>: Condition text. (Skills like <Skill skill={skills.restraint} /> which always activate have <Mono
>always==1</Mono
>.) If the skill has only one detail, then <Mono>condition_2</Mono> is empty.
</li>
<li>
<Mono>float_ability_time_1</Mono>: Base duration expressed in ten thousandths of a second, or -1 for passives, or 0 for
instantaneous skills (recoveries, <Skill skill={skills.focus} />).
</li>
<li>
<Mono>ability_time_usage_1</Mono>: Scaling type for the skill duration.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Duration Scaling</th>
</tr>
</thead>
<tbody>
{#each durScalingTypes as [id, name] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{id}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
GameTora and the race mechanics doc describe duration scaling types in more detail.
</li>
<li>
<Mono>float_cooldown_time_1</Mono>: Base cooldown duration expressed in ten thousandths of a second, or 5000000 (500s) for
skills with no cooldown, or 0 for passives.
</li>
<li>Columns for three <i>abilities</i>, described in the next section.</li>
</ul>
<p>
<a
href="https://hakuraku.moe/masterdata?q=WITH+RECURSIVE+condition+AS+%28%0A++++SELECT+DISTINCT+precondition_1+AS+c+FROM+skill_data%0A++++UNION%0A++++SELECT+DISTINCT+condition_1+AS+c+FROM+skill_data%0A++++UNION%0A++++SELECT+DISTINCT+precondition_2+AS+c+FROM+skill_data%0A++++UNION%0A++++SELECT+DISTINCT+condition_2+AS+c+FROM+skill_data%0A%29%2C+disj+AS+%28%0A++++SELECT%0A++++++++IIF%28INSTR%28c%2C+%27%40%27%29+%21%3D+0%2C+SUBSTR%28c%2C+1%2C+INSTR%28c%2C+%27%40%27%29-1%29%2C+c%29+AS+c%2C%0A++++++++IIF%28INSTR%28c%2C+%27%40%27%29+%21%3D+0%2C+SUBSTR%28c%2C+1+%2B+INSTR%28c%2C+%27%40%27%29%29%2C+%27%27%29+AS+rest%0A++++FROM+condition%0A++++UNION%0A++++SELECT%0A++++++++IIF%28INSTR%28rest%2C+%27%40%27%29+%21%3D+0%2C+SUBSTR%28rest%2C+1%2C+INSTR%28rest%2C+%27%40%27%29-1%29%2C+rest%29+AS+c%2C%0A++++++++IIF%28INSTR%28rest%2C+%27%40%27%29+%21%3D+0%2C+SUBSTR%28rest%2C+1+%2B+INSTR%28rest%2C+%27%40%27%29%29%2C+%27%27%29+AS+rest%0A++++FROM+disj%0A++++WHERE+rest+%21%3D+%27%27%0A%29%2C+term+AS+%28%0A++++SELECT%0A++++++++IIF%28INSTR%28c%2C+%27%26%27%29+%21%3D+0%2C+SUBSTR%28c%2C+1%2C+INSTR%28c%2C+%27%26%27%29-1%29%2C+c%29+AS+term%2C%0A++++++++IIF%28INSTR%28c%2C+%27%26%27%29+%21%3D+0%2C+SUBSTR%28c%2C+1+%2B+INSTR%28c%2C+%27%26%27%29%29%2C+%27%27%29+AS+rest%0A++++FROM+disj%0A++++UNION%0A++++SELECT%0A++++++++IIF%28INSTR%28rest%2C+%27%26%27%29+%21%3D+0%2C+SUBSTR%28rest%2C+1%2C+INSTR%28rest%2C+%27%26%27%29-1%29%2C+rest%29+AS+term%2C%0A++++++++IIF%28INSTR%28rest%2C+%27%26%27%29+%21%3D+0%2C+SUBSTR%28rest%2C+1+%2B+INSTR%28rest%2C+%27%26%27%29%29%2C+%27%27%29+AS+rest%0A++++FROM+term%0A++++WHERE+rest+%21%3D+%27%27%0A%29%2C+op+AS+%28%0A++++SELECT%0A++++++++term%2C%0A++++++++CASE%0A++++++++++++WHEN+INSTR%28term%2C+%27%3D%3D%27%29+%21%3D+0+THEN+%27%3D%3D%27%0A++++++++++++WHEN+INSTR%28term%2C+%27%21%3D%27%29+%21%3D+0+THEN+%27%21%3D%27%0A++++++++++++WHEN+INSTR%28term%2C+%27%3C%3D%27%29+%21%3D+0+THEN+%27%3C%3D%27%0A++++++++++++WHEN+INSTR%28term%2C+%27%3E%3D%27%29+%21%3D+0+THEN+%27%3E%3D%27%0A++++++++++++WHEN+INSTR%28term%2C+%27%3C%27%29+%21%3D+0+THEN+%27%3C%27%0A++++++++++++WHEN+INSTR%28term%2C+%27%3E%27%29+%21%3D+0+THEN+%27%3E%27%0A++++++++END+AS+op%0A++++FROM+term+WHERE+term+%21%3D+%27%27%0A%29%2C+type_op_arg+AS+%28%0A++++SELECT+DISTINCT%0A++++++++term%2C%0A++++++++SUBSTR%28term%2C+1%2C+INSTR%28term%2C+op%29+-+1%29+AS+%22type%22%2C%0A++++++++op%2C%0A++++++++CAST%28SUBSTR%28term%2C+INSTR%28term%2C+op%29+%2B+LENGTH%28op%29%29+AS+INTEGER%29+AS+arg%0A++++FROM+op%0A%29%0ASELECT+DISTINCT+type%2C+op%2C+arg+FROM+type_op_arg%0AORDER+BY+type%2C+op%2C+arg"
target="_blank"
rel="noopener noreferrer"
>
This SQL query
</a>
parses all conditions and preconditions to obtain all distinct combinations of skill condition types, operators, and arguments.
This may be useful to tool authors.
</p>
<Sec h={3} id="skill-abilities">Skill Abilities</Sec>
<p>
Each skill detail has up to three abilities, all of which apply for as long as the skill detail is active (except for
AdditionalActivate abilities). Column names append <Mono>_1</Mono>, <Mono>_2</Mono>, or <Mono>_3</Mono> after the skill detail's
own number.
</p>
<ul class="ml-4 list-disc pb-4">
<li>
<Mono>ability_type_1_1</Mono> (resp. <Mono>ability_type_1_2</Mono>, <Mono>ability_type_1_3</Mono>, <Mono
>ability_type_2_1</Mono
>, <Mono>ability_type_2_2</Mono>, <Mono>ability_type_2_3</Mono>) define what simulation parameter the ability affects.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Effect</th>
<th scope="col" class="px-2">Value Units</th>
</tr>
</thead>
<tbody>
{#each abilityTypes as [typ, name, units] (typ)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{typ}</td>
<td class="px-2">{name}</td>
<td class="px-2">{units}</td>
</tr>
{/each}
</tbody>
</table>
Types with (?) don't exist in my decomp but are used on skills in the copy of the JP mdb that I have, which I'm told is "from
like 3 weeks ago" at the time of writing. Those marked (unused) do exist in the decomp, but are not used for skills even on JP.
</li>
<li>
<Mono>ability_value_usage_1_1</Mono> defines special scaling for the ability's value.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Value Scaling</th>
</tr>
</thead>
<tbody>
{#each valScalingTypes as [id, name] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{id}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
GameTora describes value scaling types in more detail. (The race mechanics doc is missing most of them.)
</li>
<li>
<Mono>additional_activate_type_1_1</Mono> indicates abilities that only activate on additional events while the skill detail is
active.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Meaning</th>
</tr>
</thead>
<tbody>
{#each addlActivateTypes as [id, name] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{id}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
</li>
<li>
<Mono>ability_value_level_usage_1_1</Mono> indicates how the ability scales with skill level (i.e. unique level, not double circle
vs single circle).
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Meaning</th>
<th scope="col" class="px-2">Instances</th>
</tr>
</thead>
<tbody>
{#each levelUsageTypes as [id, name, inst] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{id}</td>
<td class="px-2">{name}</td>
<td class="px-2">{inst}</td>
</tr>
{/each}
</tbody>
</table>
</li>
<li>
<Mono>float_ability_value_1_1</Mono> is the base magnitude of the ability's effect, expressed in ten thousandths.
</li>
<li>
<Mono>target_type_1_1</Mono> describes which umas the skill targets. It takes <Mono>target_value_1_1</Mono> as an argument.
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type</th>
<th scope="col" class="px-2">Meaning</th>
<th scope="col" class="px-2">Argument</th>
</tr>
</thead>
<tbody>
{#each targetTypes as [id, name, arg] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{id}</td>
<td class="px-2">{name}</td>
<td class="px-2">{arg}</td>
</tr>
{/each}
</tbody>
</table>
Skills that take maximum targets as an argument use a <Mono>target_value_1_1</Mono> of 18 to mean unlimited. Type 23 is internally
named <Mono>ActivateHealSkill</Mono>, so it may instead mean something like "horses who activated a heal skill this frame."
It is only used for <Skill skill={skills.oppression} /> and its gold.
</li>
</ul>
<Sec h={3} id="skill-scaling">Skill Level Scaling</Sec>
<p>
The table <Mono>skill_level_value</Mono> defines the coefficients by which each ability's value scales with unique level. Curiously,
it defines coefficients up to <Mono>level = 10</Mono> for each <Mono>ability_type</Mono>, even though the maximum achievable
level is 6.
</p>
<p>
The <Mono>float_ability_value_coef</Mono> column is a multiplier on the corresponding skill ability type's <Mono
>float_ability_value_?_?</Mono
>, expressed in ten thousandths.
</p>
<Sec h={3} id="skill-sp">Skill Points</Sec>
<p>
The table <Mono>single_mode_skill_need_point</Mono> lists the SP cost of each skill.
</p>
<ul class="ml-4 list-disc pb-4">
<li><Mono>id</Mono> is the skill ID.</li>
<li><Mono>need_skill_point</Mono> is the SP cost.</li>
<li><Mono>status_type</Mono> and <Mono>status_value</Mono> are always zero.</li>
<li>
<Mono>solvable_type</Mono> is nonzero for some negative (purple) skills that are applied and later healed during careers, for
Sweep Tosho, Air Shakur, and Daitaku Helios. Some other JP negative skills that GameTora has no source information for also have
it.
</li>
</ul>
<Sec h={2} id="sparks">Sparks</Sec>
<p>
Internally called succession factors. Relevant <Mono>text_data</Mono> categories include 147 for spark names and 172 for spark descriptions,
both indexed by factor ID.
</p>
<Sec h={3} id="succession_factor">Spark Data</Sec>
<p>Queries should join with <Mono>succession_factor</Mono> inside.</p>
<ul class="ml-4 list-disc pb-4">
<li>Spark ID is <Mono>factor_id</Mono>. This identifies the combination of spark variety and its star level.</li>
<li>
<Mono>factor_group_id</Mono> gives the variety. E.g., the star levels of the February S. spark are factor IDs 1000101, 1000102,
and 1000103, but all three have group ID 10001.
</li>
<li><Mono>rarity</Mono> is the star level, 1-indexed.</li>
<li><Mono>grade</Mono> is 2 for unique (green) sparks and 1 otherwise.</li>
<li>
<Mono>factor_type</Mono> is roughly the color type of the spark plus extra subdivisions for white sparks:
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type ID</th>
<th scope="col" class="px-2">Spark Type</th>
<th scope="col" class="px-2">Color</th>
</tr>
</thead>
<tbody>
{#each sparkTypes as [typ, name, cls] (typ)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td>{typ}</td>
<td>{name}</td>
<td><div class={['bg-spark mx-auto h-4 w-4 border', cls]}></div></td>
</tr>
{/each}
</tbody>
</table>
Types 8 and above should release with 4anni.
</li>
<li>
<Mono>effect_group_id</Mono> may give something like the distribution of spark effects. For current Global data, it's in bijection
with <Mono>factor_type</Mono> &times; <Mono>rarity</Mono>. It doesn't join with anything else in the mdb.
</li>
</ul>
<Sec h={3} id="succession_factor_effect">Spark Effects</Sec>
<p>
<Mono>succession_factor_effect</Mono> defines all possible effects that each spark can yield (but not their distributions). Effects
are organized by <Mono>factor_group_id</Mono> rather than by <Mono>factor_id</Mono>, which implies to some degree that all
star levels of a given spark have the same possible results with different distributions, although it is possible that some
values have zero probability for some star levels.
</p>
<p>
The process by which the game determines the effects of a given spark proc during (Classic and Senior) inspiration is as
follows, or semantically equivalent.
</p>
<ol class="ml-4 list-decimal pb-4">
<li>
The server chooses a random <Mono>effect_id</Mono> from the hidden distribution for the spark's <Mono>effect_group_id</Mono
>.
</li>
<li>
The client (and server) selects <i>all</i> rows matching the spark's <Mono>factor_group_id</Mono> and the random rolled <Mono
>effect_id</Mono
>, i.e. <Mono
>SELECT target_type, value_1, value_2 FROM succession_factor_effect WHERE factor_group_id = $current_spark_factor_group_id
AND effect_id = $random_choice_effect</Mono
>. There may be several such effects.
</li>
<li>
For each result row, apply the effect according to the <Mono>target_type</Mono>, with <Mono>value_1</Mono> and <Mono
>value_2</Mono
> as its possible arguments:
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2"><Mono>target_type</Mono></th>
<th scope="col" class="px-2">Description</th>
<th scope="col" class="px-2"><Mono>value_1</Mono> Usage</th>
<th scope="col" class="px-2"><Mono>value_2</Mono> Usage</th>
</tr>
</thead>
<tbody>
{#each sparkEffects as [target, name, val1, val2] (target)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td>{target}</td>
<td>{name}</td>
<td>{val1}</td>
<td>{val2}</td>
</tr>
{/each}
</tbody>
</table>
When effect 41 applies to a skill which is already at its maximum hint value (including from previous spark procs in the same
inspiration), a small amount (1-5, perhaps?) of some number (one? two?) of random stats is applied instead, but the details of
this are not known.
</li>
</ol>
<p>
E.g., <a
href="https://hakuraku.moe/masterdata?q=SELECT+*+FROM+%22succession_factor_effect%22+WHERE+factor_group_id+%3D+107401+ORDER+BY+factor_group_id%2C+effect_id%2C+target_type"
target="_blank"
rel="noopener noreferrer">all <Mono>succession_factor_effect</Mono> data for Lovely Spring Breeze</a
> (Mejiro Bright's green spark) is:
</p>
<table class="mb-4 table-fixed border text-center">
<caption><Mono>succession_factor_effect</Mono></caption>
<thead>
<tr>
<th scope="col" class="px-2"><Mono>id</Mono></th>
<th scope="col" class="px-2"><Mono>factor_group_id</Mono></th>
<th scope="col" class="px-2"><Mono>effect_id</Mono></th>
<th scope="col" class="px-2"><Mono>target_type</Mono></th>
<th scope="col" class="px-2"><Mono>value_1</Mono></th>
<th scope="col" class="px-2"><Mono>value_2</Mono></th>
</tr>
</thead>
<tbody>
{#each lovelySpringBreeze as [id, group, effect, typ, val1, val2] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td>{id}</td>
<td>{group}</td>
<td>{effect}</td>
<td>{typ}</td>
<td>{val1}</td>
<td>{val2}</td>
</tr>
{/each}
</tbody>
</table>
<p>
When the spark procs, the server rolls an effect ID of 1, 2, 3, or 4 according to some hidden distribution, then it applies
all four effects with that effect ID.
</p>
<Sec h={3} id="succession_initial_factor">Initial Inspiration Effects</Sec>
<p>
<Mono>succession_initial_factor</Mono> defines thresholds for initial inspiration, i.e. the immediate effects of sparks upon starting
a run. There is not much consistency to its format, but it has only thirteen rows, reproduced here.
</p>
<table class="mb-4 table-fixed border text-center">
<caption><Mono>succession_initial_factor</Mono></caption>
<thead>
<tr>
<th scope="col" class="px-2"><Mono>id</Mono></th>
<th scope="col" class="px-2"><Mono>factor_type</Mono></th>
<th scope="col" class="px-2"><Mono>value_1</Mono></th>
<th scope="col" class="px-2"><Mono>value_2</Mono></th>
<th scope="col" class="px-2"><Mono>add_point</Mono></th>
</tr>
</thead>
<tbody>
{#each successionInitialFactor as [id, typ, val1, val2, add] (id)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td>{id}</td>
<td>{typ}</td>
<td>{val1}</td>
<td>{val2}</td>
<td>{add}</td>
</tr>
{/each}
</tbody>
</table>
<p>
There are six rows with <Mono>factor_type = 1</Mono>, referring to blue sparks.
<Mono>value_1</Mono> refers to the star level of each individual spark.
<Mono>value_2</Mono> identifies whether the effect applies to starting stats (0) or raised max stats (1).
<Mono>add_point</Mono> is the amount to add to the respective field.
</p>
<p>
Pink sparks, i.e. <Mono>factor_type = 2</Mono>, have four rows.
<Mono>value_1</Mono> is the lower threshold of total star count, and <Mono>value_2</Mono> is the upper threshold.
<Mono>add_point</Mono> is the number of levels to raise the corresponding aptitude.
</p>
<p>
<Mono>factor_type = 3</Mono> for unique sparks has three rows.
<Mono>add_point</Mono> is always 0. The (<Mono>value_1</Mono>, <Mono>value_2</Mono>) pairs are (1, 1), (2, 2), and (3, 4), but
the meaning is unclear; all rows for this factor type were added in the 1 Jul patch that introduced raised stat caps, so one
would expect it to be related to those. (In particular, it does not appear to be related to the starting skill hints.)
</p>
<p>
In all cases, there is no direct association between the spark and its actual initial effects. They must be derived from the
spark's normal effects instead.
</p>
<Sec h={2} id="supports">Support Cards</Sec>
<p>
Relevant <Mono>text_data</Mono> categories include 75 for name including variant, 76 for variant alone, 77 for character name. Category
151 gives support card effect names, 154 gives effect descriptions, 150 gives unique effect names, and 155 gives unique effect descriptions.
</p>
<Sec h={3} id="support_card_data">Support Card Data</Sec>
<p>
Queries should join with <Mono>support_card_data</Mono> inside.
</p>
<ul class="ml-4 list-disc pb-4">
<li>Support card ID is just <Mono>id</Mono>.</li>
<li>
<Mono>chara_id</Mono> links to character. Implements the "no supporting yourself" and "no duplicated characters" rules.
</li>
<li><Mono>rarity</Mono> is 1 for R, 2 for SR, 3 for SSR.</li>
<li><Mono>exchange_item_id</Mono> is the type of item you get for exchanging duplicates.</li>
<li><Mono>effect_table_id</Mono> joins <Mono>support_card_effect_table</Mono>.</li>
<li><Mono>unique_effect_id</Mono> joins <Mono>support_card_unique_effect</Mono>.</li>
<li>
<Mono>command_type</Mono> is generally 1 for normal support cards and 0 for pals and groups, but Throne is 1 as well. Probably
not important.
</li>
<li>
<Mono>command_id</Mono> gives the specialty type: 101 for speed, 102 for power, 103 for guts, 105 for stamina, 106 for wit, 0
for pal and group. There is no 104.
</li>
<li><Mono>support_card_type</Mono> is 1 for normal cards, 2 for pal, 3 for group.</li>
<li>
<Mono>skill_set_id</Mono> gives the hint list via <Mono
>support_card_data sc JOIN single_mode_hint_gain h ON sc.skill_set_id = h.hint_id AND sc.support_card_id =
h.support_card_id</Mono
>. Given that you have to use the support card ID anyway, this seems redundant.
</li>
<li><Mono>outing_max</Mono> is the number of recreation events with the card, for pals and groups.</li>
<li><Mono>effect_id</Mono> is the specific instance of Pure Passion the card can grant, for groups.</li>
</ul>
<Sec h={3} id="support_card_effect_table">Support Card Effects</Sec>
<p>
<Mono>support_card_effect_table</Mono> gives progressions of support card stats. There is one row per stat per support card. Unique
effects are not included.
</p>
<ul class="ml-4 list-disc pb-4">
<li><Mono>id</Mono> joins with support card ID.</li>
<li>
<Mono>type</Mono> is the stat type (<Mono>text_data</Mono> category 151):
<table class="table-fixed border text-center">
<thead>
<tr>
<th scope="col" class="px-2">Type ID</th>
<th scope="col" class="px-2">Effect</th>
</tr>
</thead>
<tbody>
{#each supportCardEffects as [typ, name] (typ)}
<tr class="even:bg-mist-300 dark:even:bg-mist-900">
<td class="px-2">{typ}</td>
<td class="px-2">{name}</td>
</tr>
{/each}
</tbody>
</table>
</li>
<li>
<Mono>init</Mono> is the card's value at level 1, then there are subsequent <Mono>limit_lv5</Mono>, <Mono>limit_lv10</Mono>,
&c. up to <Mono>limit_lv50</Mono>. Each value is either a threshold value for the corresponding level, or -1 to indicate
that the value should interpolate per level between the previous positive value (defaulting to 0) and the next (defaulting
to no interpolation).
</li>
</ul>
<Sec h={3} id="support_card_unique_effect">Unique Effects</Sec>
<p>Unique effect data is in <Mono>support_card_unique_effect</Mono>.</p>
<ul class="ml-4 list-disc pb-4">
<li>
<Mono>id</Mono> joins with <Mono>support_card_data.unique_effect_id</Mono>, but it's also always the same as the support
card ID, never deduplicated or anything. (This ID is also used for unique effect name lookup, after all.)
</li>
<li><Mono>lv</Mono> gives the level at which the unique effect becomes active, one of 25, 30, 35, or 40.</li>
<li>
<Mono>type_0</Mono> and <Mono>type_1</Mono> give the effect type ID with the corresponding magnitude in <Mono>value_0</Mono> or
<Mono>value_1</Mono>, largely same as for <Mono>support_card_effect_table.type</Mono>. However, there are some extra types:
<ul class="ml-8 list-[reset]">
<li>
101 is a friendship gauge check. <Mono>value_0</Mono> is the threshold value, and then <Mono>value_0_1</Mono> and <Mono
>value_0_3</Mono
> give the effects that become active at that threshold with their magnitudes in <Mono>value_0_2</Mono> and <Mono
>value_0_4</Mono
>, respectively.
</li>
<li>
102 is Guts Bakushin's special effect of Training Effectiveness (magnitude in <Mono>value_0_1</Mono>) when both above a
friendship threshold (<Mono>value_0</Mono>) and not on her own specialty.
</li>
<li>
103 is Agnes Ditigal's special effect of Training Effectiveness (magnitude in <Mono>value_0_1</Mono>) when the deck has
at least <Mono>value_0</Mono> different colors.
</li>
<li>
104 is Narita Top Road's special effect of Training Effectiveness of maximum magnitude <Mono>value_0_1</Mono> divided by each
group of <Mono>value_0</Mono> fans.
</li>
<li>
105 is +<Mono>value_0</Mono> initial stat to the specialty of each card in the deck and +<Mono>value_0</Mono> initial all
stats per pal and group card.
</li>
<li>
106 is stacking effect per rainbow training, where <Mono>value_0</Mono> is the maximum number of stacks, <Mono
>value_0_1</Mono
> is the effect type granted by the stack, and <Mono>value_0_2</Mono> is the value per stack.
</li>
<li>
107 is scaling with how low energy is. <Mono>value_0</Mono> is obviously the effect type ID that scales, but the other parameters
are unclear. Currently only Bamboo Memory Guts has this, with <Mono>value_0_1&el;4</Mono> being (10, 30, 15, 5).
</li>
<li>
108 is scaling with maximum energy. Similar to 107, <Mono>value_0</Mono> is the effect type ID that scales, but the rest is
unclear, with values of (100, 75, 5, 20) for Pearl.
</li>
<li>
109 is scaling with total friendship across all cards. <Mono>value_0</Mono> is the effect type ID that scales, <Mono
>value_0_1</Mono
> is the amount of bond required to increase the effect by 1.
</li>
<li>
110 is scaling with the number of cards at the training. <Mono>value_0</Mono> is the effect type ID that scales, <Mono
>value_0_1</Mono
> is the amount per card.
</li>
<li>
110 is scaling with the number of cards at the training. <Mono>value_0</Mono> is the effect type ID that scales, <Mono
>value_0_1</Mono
> is the amount per card.
</li>
<li>
111 is scaling with training facility level. <Mono>value_0</Mono> is the effect type ID that scales, <Mono
>value_0_1</Mono
> is the amount per level.
</li>
<li>112 is Nakayama Festa Wit's <Mono>value_0</Mono>% chance to disable failure chance.</li>
<li>
113 is an additional effect active for rainbow training. <Mono>value_0</Mono> is the additional effect ID, <Mono
>value_0_1</Mono
> is its value.
</li>
</ul>
Certainly more types will follow.
</li>
<li>
<Mono>idle_mode_sub_rate</Mono> is uncertain, but it is likely related to Independent Training (called idle single mode internally,
where "single mode" is in turn the internal name for career). It takes only a few values:
<ul class="ml-8 list-[reset]">
<li>For unique effects that don't vary, it is always 0.</li>
<li>For those that vary with the deck composition (Agnes Digital, Satono Diamond Wit), it is 10.</li>
<li>For effects that activate with 80+ friendship, it has a value of 20.</li>
<li>
For 100+ friendship effects, fan count scaling, rainbow training stacking effects, effects scaling with energy or max
energy, scaling with number of cards trained with, scaling with training level, and scaling with total deck friendship,
it has a value of 30.
</li>
</ul>
</li>
</ul>
<Sec h={3} id="single_mode_hint_gain">Hints</Sec>
<p>
Lastly, the table <Mono>single_mode_hint_gain</Mono> lists the effects of hints during career.
</p>
<ul class="ml-4 list-disc pb-4">
<li>
<Mono>hint_id</Mono> joins with <Mono>support_card_data.skill_set_id</Mono>, but it isn't unique; e.g., Seiun Sky SR has the
same <Mono>hint_id</Mono> as prior Seiun Sky cards, despite having a different hint list. Unclear what it actually does. Doesn't
seem useful for my purposes.
</li>
<li>
<Mono>support_card_id</Mono> is effectively the lookup key.
</li>
<li>
<Mono>hint_group</Mono> ties together the results of one hint. Skill hints are generally one each, but stat hints have two or
three rows with matching hint groups.
</li>
<li>
<Mono>hint_gain_type</Mono> is 0 for skill hints and 1 for stat hints.
</li>
<li>
For skill hints, <Mono>hint_value_1</Mono> is the skill ID, and <Mono>hint_value_2</Mono> is always 1; presumably the latter could
serve as the base value of hint levels. For stat hints, <Mono>hint_value_1</Mono> is the stat ID &ndash; 1 for speed through 5
for wit, or 30 for skill points &ndash; and <Mono>hint_value_2</Mono> is the amount to add.
</li>
<li>
<Mono>group_id</Mono>, <Mono>condition_set_id</Mono>, and <Mono>priority</Mono> are only nonzero for Haru Urara Power, whose unique
effect includes "hint event effectiveness" and who has upgraded stat hints. It's unclear exactly how it works or why it takes
three columns to implement.
</li>
</ul>
</Article>
<style>
.spark-color-white {
--spark-color: #e1e2e1;
}
.spark-color-blue {
--spark-color: #37b9f4;
}
.spark-color-pink {
--spark-color: #ff78b2;
}
.spark-color-green {
--spark-color: #91d02e;
}
.spark-color-red {
--spark-color: #f46052;
}
.bg-spark {
background-color: var(--spark-color);
}
.fg-spark {
color: var(--spark-color);
}
</style>