diff --git a/resources/cosmetics/cosmetics.json b/resources/cosmetics/cosmetics.json index d30a6fa0e..a364d4e3f 100644 --- a/resources/cosmetics/cosmetics.json +++ b/resources/cosmetics/cosmetics.json @@ -80,5 +80,101 @@ "name": "openfront", "role_group": "creator" } + }, + "flag": { + "layers": { + "a": { "name": "center_circle" }, + "b": { "name": "center_hline" }, + "c": { "name": "center_vline" }, + "d": { "name": "center_star" }, + "e": { "name": "center_flower" }, + "f": { "name": "flower_tl" }, + "g": { "name": "flower_tc" }, + "h": { "name": "flower_tr" }, + "i": { "name": "diag_br" }, + "j": { "name": "diag_bl" }, + "k": { "name": "frame" }, + "l": { "name": "full" }, + "m": { "name": "triangle_tl" }, + "n": { "name": "triangle_bl" }, + "o": { "name": "triangle_tr" }, + "p": { "name": "triangle_br" }, + "q": { "name": "half_l" }, + "r": { "name": "half_r" }, + "s": { "name": "half_t" }, + "t": { "name": "half_b" }, + "u": { "name": "mini_tr_bl" }, + "v": { "name": "mini_tr_br" }, + "w": { "name": "mini_tr_tl" }, + "x": { "name": "mini_tr_tr" }, + "y": { "name": "triangle_t" }, + "z": { "name": "triangle_l" }, + "aa": { "name": "triangle_b" }, + "ab": { "name": "triangle_r" }, + "ac": { "name": "tricolor_l" }, + "ad": { "name": "tricolor_c" }, + "ae": { "name": "tricolor_r" }, + "af": { "name": "tricolor_t" }, + "ag": { "name": "tricolor_m" }, + "ah": { "name": "tricolor_b" }, + "ai": { "name": "nato_emblem" }, + "aj": { "name": "eu_star" }, + "ak": { "name": "laurel_wreath" }, + "al": { "name": "ofm_2025" }, + "am": { "name": "octagram" }, + "an": { "name": "octagram_2" }, + "ao": { "name": "og" }, + "ap": { "name": "og_plus" }, + "aq": { "name": "beta_tester" }, + "ar": { "name": "beta_tester_circle" }, + "as": { "name": "rocket" }, + "at": { "name": "rocket_mini" }, + "au": { "name": "translator" }, + "av": { "name": "admin_shield" }, + "aw": { "name": "admin_shield_r" }, + "ax": { "name": "admin_evan" } + }, + "color": { + "a": { "color": "#ff0000", "name": "red" }, + "b": { "color": "#ffa500", "name": "orange" }, + "c": { "color": "#ffff00", "name": "yellow" }, + "d": { "color": "#008000", "name": "green" }, + "e": { "color": "#00ffff", "name": "cyan" }, + "f": { "color": "#0000ff", "name": "blue" }, + "g": { "color": "#000000", "name": "black" }, + "h": { "color": "#ffffff", "name": "white" }, + "i": { "color": "#800080", "name": "purple" }, + "j": { "color": "#ff69b4", "name": "hotpink" }, + "k": { "color": "#a52a2a", "name": "brown" }, + "l": { "color": "#808080", "name": "gray" }, + "m": { "color": "#20b2aa", "name": "teal" }, + "n": { "color": "#ff6347", "name": "tomato" }, + "o": { "color": "#4682b4", "name": "steelblue" }, + "p": { "color": "#90ee90", "name": "lightgreen" }, + "q": { "color": "#8b0000", "name": "darkred" }, + "r": { "color": "#191970", "name": "navy" }, + "s": { "color": "#ffd700", "name": "gold" }, + "t": { "color": "#add8e6", "name": "lightblue" }, + "u": { "color": "#f5f5dc", "name": "beige" }, + "v": { "color": "#ffb6c1", "name": "lightpink" }, + "w": { "color": "#708090", "name": "slategray" }, + "x": { "color": "#00ff7f", "name": "springgreen" }, + "y": { "color": "#dc143c", "name": "crimson" }, + "z": { "color": "#ffbf00", "name": "amber" }, + "0": { "color": "#3d9970", "name": "olive_green" }, + "1": { "color": "#87ceeb", "name": "sky_blue" }, + "2": { "color": "#6a5acd", "name": "slate_blue" }, + "3": { "color": "#ff66cc", "name": "rose_pink" }, + "4": { "color": "#36454f", "name": "charcoal" }, + "5": { "color": "#fffff0", "name": "ivory" }, + "A": { "color": "rainbow", "name": "rainbow" }, + "B": { "color": "bright-rainbow", "name": "bright_rainbow" }, + "C": { "color": "gold-glow", "name": "gold_glow" }, + "D": { "color": "silver-glow", "name": "silver_glow" }, + "E": { "color": "copper-glow", "name": "copper_glow" }, + "F": { "color": "neon", "name": "neon" }, + "G": { "color": "lava", "name": "lava" }, + "H": { "color": "water", "name": "water" } + } } } diff --git a/resources/flags/custom/admin_contributors.svg b/resources/flags/custom/admin_contributors.svg new file mode 100644 index 000000000..b0319230d --- /dev/null +++ b/resources/flags/custom/admin_contributors.svg @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/admin_evan.svg b/resources/flags/custom/admin_evan.svg new file mode 100644 index 000000000..34da232a2 --- /dev/null +++ b/resources/flags/custom/admin_evan.svg @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/admin_shield.svg b/resources/flags/custom/admin_shield.svg new file mode 100644 index 000000000..6511539a4 --- /dev/null +++ b/resources/flags/custom/admin_shield.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/admin_shield_r.svg b/resources/flags/custom/admin_shield_r.svg new file mode 100644 index 000000000..a7ec7e785 --- /dev/null +++ b/resources/flags/custom/admin_shield_r.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/beta_tester.svg b/resources/flags/custom/beta_tester.svg new file mode 100644 index 000000000..3e33c34f5 --- /dev/null +++ b/resources/flags/custom/beta_tester.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/beta_tester_circle.svg b/resources/flags/custom/beta_tester_circle.svg new file mode 100644 index 000000000..7beaa298e --- /dev/null +++ b/resources/flags/custom/beta_tester_circle.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/center_circle.svg b/resources/flags/custom/center_circle.svg new file mode 100644 index 000000000..10d994540 --- /dev/null +++ b/resources/flags/custom/center_circle.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/center_flower.svg b/resources/flags/custom/center_flower.svg new file mode 100644 index 000000000..d1e728416 --- /dev/null +++ b/resources/flags/custom/center_flower.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/center_hline.svg b/resources/flags/custom/center_hline.svg new file mode 100644 index 000000000..0642e382a --- /dev/null +++ b/resources/flags/custom/center_hline.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/center_star.svg b/resources/flags/custom/center_star.svg new file mode 100644 index 000000000..b10dd53a3 --- /dev/null +++ b/resources/flags/custom/center_star.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/center_vline.svg b/resources/flags/custom/center_vline.svg new file mode 100644 index 000000000..cc99b35e3 --- /dev/null +++ b/resources/flags/custom/center_vline.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/diag_bl.svg b/resources/flags/custom/diag_bl.svg new file mode 100644 index 000000000..289eaaba6 --- /dev/null +++ b/resources/flags/custom/diag_bl.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/diag_br.svg b/resources/flags/custom/diag_br.svg new file mode 100644 index 000000000..79ff3cca6 --- /dev/null +++ b/resources/flags/custom/diag_br.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/eu_star.svg b/resources/flags/custom/eu_star.svg new file mode 100644 index 000000000..73e4b5097 --- /dev/null +++ b/resources/flags/custom/eu_star.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/flower_tc.svg b/resources/flags/custom/flower_tc.svg new file mode 100644 index 000000000..feeb1899f --- /dev/null +++ b/resources/flags/custom/flower_tc.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/flower_tl.svg b/resources/flags/custom/flower_tl.svg new file mode 100644 index 000000000..74c3c06b0 --- /dev/null +++ b/resources/flags/custom/flower_tl.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/flower_tr.svg b/resources/flags/custom/flower_tr.svg new file mode 100644 index 000000000..7cfe15d61 --- /dev/null +++ b/resources/flags/custom/flower_tr.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/frame.svg b/resources/flags/custom/frame.svg new file mode 100644 index 000000000..42a52d8b2 --- /dev/null +++ b/resources/flags/custom/frame.svg @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/full.svg b/resources/flags/custom/full.svg new file mode 100644 index 000000000..479b6501c --- /dev/null +++ b/resources/flags/custom/full.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/half_b.svg b/resources/flags/custom/half_b.svg new file mode 100644 index 000000000..2c5cd1521 --- /dev/null +++ b/resources/flags/custom/half_b.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/half_l.svg b/resources/flags/custom/half_l.svg new file mode 100644 index 000000000..280aeb128 --- /dev/null +++ b/resources/flags/custom/half_l.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/half_r.svg b/resources/flags/custom/half_r.svg new file mode 100644 index 000000000..b42af0d62 --- /dev/null +++ b/resources/flags/custom/half_r.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/half_t.svg b/resources/flags/custom/half_t.svg new file mode 100644 index 000000000..14b04d67f --- /dev/null +++ b/resources/flags/custom/half_t.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/jp.svg b/resources/flags/custom/jp.svg new file mode 100644 index 000000000..06da6928c --- /dev/null +++ b/resources/flags/custom/jp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/flags/custom/laurel_wreath.svg b/resources/flags/custom/laurel_wreath.svg new file mode 100644 index 000000000..d96c59ffa --- /dev/null +++ b/resources/flags/custom/laurel_wreath.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/mini_tr_bl.svg b/resources/flags/custom/mini_tr_bl.svg new file mode 100644 index 000000000..592503995 --- /dev/null +++ b/resources/flags/custom/mini_tr_bl.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/mini_tr_br.svg b/resources/flags/custom/mini_tr_br.svg new file mode 100644 index 000000000..d4bd87b5c --- /dev/null +++ b/resources/flags/custom/mini_tr_br.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/mini_tr_tl.svg b/resources/flags/custom/mini_tr_tl.svg new file mode 100644 index 000000000..9099684e5 --- /dev/null +++ b/resources/flags/custom/mini_tr_tl.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/mini_tr_tr.svg b/resources/flags/custom/mini_tr_tr.svg new file mode 100644 index 000000000..d211b893f --- /dev/null +++ b/resources/flags/custom/mini_tr_tr.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/nato_emblem.svg b/resources/flags/custom/nato_emblem.svg new file mode 100644 index 000000000..44b4ce6c6 --- /dev/null +++ b/resources/flags/custom/nato_emblem.svg @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/octagram.svg b/resources/flags/custom/octagram.svg new file mode 100644 index 000000000..604a24e8a --- /dev/null +++ b/resources/flags/custom/octagram.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/octagram_2.svg b/resources/flags/custom/octagram_2.svg new file mode 100644 index 000000000..7ea31b7ed --- /dev/null +++ b/resources/flags/custom/octagram_2.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/ofm_2025.svg b/resources/flags/custom/ofm_2025.svg new file mode 100644 index 000000000..fe9bd04c4 --- /dev/null +++ b/resources/flags/custom/ofm_2025.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/flags/custom/og.svg b/resources/flags/custom/og.svg new file mode 100644 index 000000000..44a35f29a --- /dev/null +++ b/resources/flags/custom/og.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/og_plus.svg b/resources/flags/custom/og_plus.svg new file mode 100644 index 000000000..74b0184c9 --- /dev/null +++ b/resources/flags/custom/og_plus.svg @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/rocket.svg b/resources/flags/custom/rocket.svg new file mode 100644 index 000000000..0d8e21d2d --- /dev/null +++ b/resources/flags/custom/rocket.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/rocket_mini.svg b/resources/flags/custom/rocket_mini.svg new file mode 100644 index 000000000..cec9d5e94 --- /dev/null +++ b/resources/flags/custom/rocket_mini.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/translator.svg b/resources/flags/custom/translator.svg new file mode 100644 index 000000000..1891b67da --- /dev/null +++ b/resources/flags/custom/translator.svg @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_b.svg b/resources/flags/custom/triangle_b.svg new file mode 100644 index 000000000..787ed75d3 --- /dev/null +++ b/resources/flags/custom/triangle_b.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_bl.svg b/resources/flags/custom/triangle_bl.svg new file mode 100644 index 000000000..11727a453 --- /dev/null +++ b/resources/flags/custom/triangle_bl.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_br.svg b/resources/flags/custom/triangle_br.svg new file mode 100644 index 000000000..44b1b8496 --- /dev/null +++ b/resources/flags/custom/triangle_br.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_l.svg b/resources/flags/custom/triangle_l.svg new file mode 100644 index 000000000..75ed081f2 --- /dev/null +++ b/resources/flags/custom/triangle_l.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_r.svg b/resources/flags/custom/triangle_r.svg new file mode 100644 index 000000000..a9b25aff7 --- /dev/null +++ b/resources/flags/custom/triangle_r.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_t.svg b/resources/flags/custom/triangle_t.svg new file mode 100644 index 000000000..b7320d1fd --- /dev/null +++ b/resources/flags/custom/triangle_t.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_tl.svg b/resources/flags/custom/triangle_tl.svg new file mode 100644 index 000000000..5f10f0c2f --- /dev/null +++ b/resources/flags/custom/triangle_tl.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/triangle_tr.svg b/resources/flags/custom/triangle_tr.svg new file mode 100644 index 000000000..9b3c3795b --- /dev/null +++ b/resources/flags/custom/triangle_tr.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_b.svg b/resources/flags/custom/tricolor_b.svg new file mode 100644 index 000000000..8c82fcd88 --- /dev/null +++ b/resources/flags/custom/tricolor_b.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_c.svg b/resources/flags/custom/tricolor_c.svg new file mode 100644 index 000000000..5f6e39a46 --- /dev/null +++ b/resources/flags/custom/tricolor_c.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_l.svg b/resources/flags/custom/tricolor_l.svg new file mode 100644 index 000000000..61a183a03 --- /dev/null +++ b/resources/flags/custom/tricolor_l.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_m.svg b/resources/flags/custom/tricolor_m.svg new file mode 100644 index 000000000..39a91031d --- /dev/null +++ b/resources/flags/custom/tricolor_m.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_r.svg b/resources/flags/custom/tricolor_r.svg new file mode 100644 index 000000000..15fd3c458 --- /dev/null +++ b/resources/flags/custom/tricolor_r.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/flags/custom/tricolor_t.svg b/resources/flags/custom/tricolor_t.svg new file mode 100644 index 000000000..5a59b2898 --- /dev/null +++ b/resources/flags/custom/tricolor_t.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index d322f76f4..bb5056b3b 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -10,6 +10,7 @@ import nukeWhiteIcon from "../../../../resources/images/NukeIconWhite.svg"; import shieldIcon from "../../../../resources/images/ShieldIconBlack.svg"; import targetIcon from "../../../../resources/images/TargetIcon.svg"; import traitorIcon from "../../../../resources/images/TraitorIcon.svg"; +import { renderPlayerFlag } from "../../../core/CustomFlag"; import { PseudoRandom } from "../../../core/PseudoRandom"; import { Theme } from "../../../core/configuration/Config"; import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game"; @@ -190,14 +191,26 @@ export class NameLayer implements Layer { element.appendChild(iconsDiv); const nameDiv = document.createElement("div"); + const applyFlagStyles = (element: HTMLElement): void => { + element.classList.add("player-flag"); + element.style.opacity = "0.8"; + element.style.zIndex = "1"; + element.style.aspectRatio = "3/4"; + }; + if (player.flag()) { - const flagImg = document.createElement("img"); - flagImg.classList.add("player-flag"); - flagImg.style.opacity = "0.8"; - flagImg.src = "/flags/" + player.flag() + ".svg"; - flagImg.style.zIndex = "1"; - flagImg.style.aspectRatio = "3/4"; - nameDiv.appendChild(flagImg); + const flag = player.flag(); + if (flag !== undefined && flag !== null && flag.startsWith("!")) { + const flagWrapper = document.createElement("div"); + applyFlagStyles(flagWrapper); + renderPlayerFlag(flag, flagWrapper); + nameDiv.appendChild(flagWrapper); + } else if (flag !== undefined && flag !== null) { + const flagImg = document.createElement("img"); + applyFlagStyles(flagImg); + flagImg.src = "/flags/" + flag + ".svg"; + nameDiv.appendChild(flagImg); + } } nameDiv.classList.add("player-name"); nameDiv.style.color = this.theme.textColor(player); diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index ec907f951..5ae2c81a0 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -1,6 +1,8 @@ import { LitElement, TemplateResult, html } from "lit"; +import { ref } from "lit-html/directives/ref.js"; import { customElement, property, state } from "lit/decorators.js"; import { translateText } from "../../../client/Utils"; +import { renderPlayerFlag } from "../../../core/CustomFlag"; import { EventBus } from "../../../core/EventBus"; import { PlayerProfile, @@ -206,11 +208,22 @@ export class PlayerInfoOverlay extends LitElement implements Layer { : "text-white"}" > ${player.flag() - ? html`` - : ""} + ? player.flag()!.startsWith("!") + ? html`
{ + if (el instanceof HTMLElement) { + requestAnimationFrame(() => { + renderPlayerFlag(player.flag()!, el); + }); + } + })} + >
` + : html`` + : html``} ${player.name()} ${player.team() !== null diff --git a/src/client/styles.css b/src/client/styles.css index c90750863..05240ef5b 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -1,6 +1,7 @@ @tailwind base; @tailwind components; @tailwind utilities; +@import url("./styles/core/flag-animation.css"); @import url("./styles/core/variables.css"); @import url("./styles/core/typography.css"); @import url("./styles/layout/header.css"); diff --git a/src/client/styles/core/flag-animation.css b/src/client/styles/core/flag-animation.css new file mode 100644 index 000000000..47a42d183 --- /dev/null +++ b/src/client/styles/core/flag-animation.css @@ -0,0 +1,236 @@ +@keyframes rainbow { + 0% { + background-color: #990033; + } + 16% { + background-color: #996600; + } + 32% { + background-color: #336600; + } + 48% { + background-color: #008080; + } + 64% { + background-color: #1c3f99; + } + 80% { + background-color: #5e0099; + } + 100% { + background-color: #990033; + } +} + +.flag-color-rainbow { + animation: rainbow 7s infinite; +} + +@keyframes brightRainbow { + 0% { + background-color: #ff0000; + } /* Red */ + 16% { + background-color: #ffa500; + } /* Orange */ + 32% { + background-color: #ffff00; + } /* Yellow */ + 48% { + background-color: #00ff00; + } /* Green */ + 64% { + background-color: #00ffff; + } /* Cyan */ + 80% { + background-color: #0000ff; + } /* Blue */ + 100% { + background-color: #ff0000; + } /* Back to red */ +} + +.flag-color-bright-rainbow { + animation: brightRainbow 7s linear infinite; +} + +@keyframes copperGlow { + 0%, + 100% { + background-color: #b87333; + filter: brightness(1); + } + 50% { + background-color: #cd7f32; + filter: brightness(1.4); + } +} + +.flag-color-copper-glow { + animation: copperGlow 3s ease-in-out infinite; +} + +@keyframes silverGlow { + 0%, + 100% { + background-color: #c0c0c0; + filter: brightness(1); + } + 50% { + background-color: #e0e0e0; + filter: brightness(1.5); + } +} + +.flag-color-silver-glow { + animation: silverGlow 3s ease-in-out infinite; +} + +@keyframes goldGlow { + 0%, + 100% { + background-color: #ffd700; + filter: brightness(1); + } + 50% { + background-color: #fff8dc; + filter: brightness(1.6); + } +} + +.flag-color-gold-glow { + animation: goldGlow 3s ease-in-out infinite; +} + +@keyframes neonPulseGreen { + 0%, + 100% { + background-color: #39ff14; + box-shadow: + 0 0 4px #39ff14, + 0 0 8px #39ff14; + filter: brightness(1); + transform: scale(1); + opacity: 1; + } + 25% { + background-color: #2aff60; + box-shadow: + 0 0 8px #2aff60, + 0 0 12px #2aff60; + filter: brightness(1.2); + transform: scale(1.05); + opacity: 0.9; + } + 50% { + background-color: #00ff88; + box-shadow: + 0 0 12px #00ff88, + 0 0 20px #00ff88; + filter: brightness(1.4); + transform: scale(1.12); + opacity: 0.7; + } + 75% { + background-color: #2aff60; + box-shadow: + 0 0 8px #2aff60, + 0 0 12px #2aff60; + filter: brightness(1.2); + transform: scale(1.05); + opacity: 0.9; + } +} + +.flag-color-neon { + animation: neonPulseGreen 3s ease-in-out infinite; + will-change: transform, opacity, filter; +} + +@keyframes waterFlicker { + 0% { + transform: translateY(0px) scale(1); + filter: brightness(1); + opacity: 0.9; + background-color: #00bfff; + } + 12% { + transform: translateY(-1px) scale(1.01); + filter: brightness(1.05); + opacity: 0.95; + background-color: #1e90ff; + } + 27% { + transform: translateY(1px) scale(1.02); + filter: brightness(1.15); + opacity: 1; + background-color: #87cefa; + } + 45% { + transform: translateY(-0.5px) scale(1.01); + filter: brightness(1.05); + opacity: 0.93; + background-color: #4682b4; + } + 63% { + transform: translateY(0.7px) scale(1.03); + filter: brightness(1.2); + opacity: 1; + background-color: #87cefa; + } + 80% { + transform: translateY(-1px) scale(1); + filter: brightness(1); + opacity: 0.88; + background-color: #1e90ff; + } + 100% { + transform: translateY(0px) scale(1); + filter: brightness(1); + opacity: 0.9; + background-color: #00bfff; + } +} + +.flag-color-water { + animation: waterFlicker 6.2s ease-in-out infinite; + will-change: transform, opacity, filter; +} + +@keyframes lavaFlow { + 0% { + background-color: #ff4500; + filter: brightness(1.1); + transform: scale(1); + } + 20% { + background-color: #ff6347; + filter: brightness(1.2); + transform: scale(1.02); + } + 40% { + background-color: #ff8c00; + filter: brightness(1.3); + transform: scale(1.03); + } + 60% { + background-color: #ff4500; + filter: brightness(1.4); + transform: scale(1.01); + } + 80% { + background-color: #ff0000; + filter: brightness(1.2); + transform: scale(1); + } + 100% { + background-color: #ff4500; + filter: brightness(1.1); + transform: scale(1); + } +} + +.flag-color-lava { + animation: lavaFlow 6s ease-in-out infinite; + will-change: background-color, filter, transform; +} diff --git a/src/core/CosmeticSchemas.ts b/src/core/CosmeticSchemas.ts index d2feec229..07d97b126 100644 --- a/src/core/CosmeticSchemas.ts +++ b/src/core/CosmeticSchemas.ts @@ -12,6 +12,21 @@ export const CosmeticsSchema = z.object({ role_group: z.string().optional(), }), ), + flag: z.object({ + layers: z.record( + z.string(), + z.object({ + name: z.string(), + }), + ), + color: z.record( + z.string(), + z.object({ + color: z.string(), + name: z.string(), + }), + ), + }), }); export type Cosmetics = z.infer; export const COSMETICS: Cosmetics = CosmeticsSchema.parse(cosmetics_json); diff --git a/src/core/CustomFlag.ts b/src/core/CustomFlag.ts new file mode 100644 index 000000000..efe89615a --- /dev/null +++ b/src/core/CustomFlag.ts @@ -0,0 +1,69 @@ +import { COSMETICS } from "./CosmeticSchemas"; + +const ANIMATION_DURATIONS: Record = { + rainbow: 4000, + "bright-rainbow": 4000, + "copper-glow": 3000, + "silver-glow": 3000, + "gold-glow": 3000, + neon: 3000, + lava: 6000, + water: 6200, +}; + +export function renderPlayerFlag(flag: string, target: HTMLElement) { + if (!flag.startsWith("!")) return; + + const code = flag.slice("!".length); + const layers = code.split("_").map((segment) => { + const [layerKey, colorKey] = segment.split("-"); + return { layerKey, colorKey }; + }); + + target.innerHTML = ""; + target.style.overflow = "hidden"; + target.style.position = "relative"; + target.style.aspectRatio = "3/4"; + + for (const { layerKey, colorKey } of layers) { + const layerName = COSMETICS.flag.layers[layerKey]?.name ?? layerKey; + + const mask = `/flags/custom/${layerName}.svg`; + if (!mask) continue; + + const layer = document.createElement("div"); + layer.style.position = "absolute"; + layer.style.top = "0"; + layer.style.left = "0"; + layer.style.width = "100%"; + layer.style.height = "100%"; + + const colorValue = COSMETICS.flag.color[colorKey]?.color ?? colorKey; + const isSpecial = + !colorValue.startsWith("#") && + !/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/.test(colorValue); + + if (isSpecial) { + const duration = ANIMATION_DURATIONS[colorValue] ?? 5000; + const now = performance.now(); + const offset = now % duration; + if (!duration) console.warn(`No animation duration for: ${colorValue}`); + layer.classList.add(`flag-color-${colorValue}`); + layer.style.animationDelay = `-${offset}ms`; + } else { + layer.style.backgroundColor = colorValue; + } + + layer.style.maskImage = `url(${mask})`; + layer.style.maskRepeat = "no-repeat"; + layer.style.maskPosition = "center"; + layer.style.maskSize = "contain"; + + layer.style.webkitMaskImage = `url(${mask})`; + layer.style.webkitMaskRepeat = "no-repeat"; + layer.style.webkitMaskPosition = "center"; + layer.style.webkitMaskSize = "contain"; + + target.appendChild(layer); + } +}