+
${this.showModal
? html`
-
-
- ${Countries.filter(
- (country) =>
- country.name
- .toLowerCase()
- .includes(this.search.toLowerCase()) ||
- country.code
- .toLowerCase()
- .includes(this.search.toLowerCase()),
- ).map(
- (country) => html`
-
- `,
- )}
+
+
+
+
+
+
+ ${this.activeTab === "real"
+ ? html`
+
+
+ ${Countries.filter(
+ (country) =>
+ country.name
+ .toLowerCase()
+ .includes(this.search.toLowerCase()) ||
+ country.code
+ .toLowerCase()
+ .includes(this.search.toLowerCase()),
+ ).map(
+ (country) => html`
+
+ `,
+ )}
+
+ `
+ : html`
+
+
+
+
+
+ ${this.colorOptions.map(
+ (color) => html`
+
+ `,
+ )}
+
+
+
+
+ Select a Layer
+
+
+ ${Object.entries(FlagMap).map(
+ ([name, src]) => html`
+
+ `,
+ )}
+
+
+
+
+
+
+ Preview
+
+
+ ${this.customLayers.map(({ name, color }) => {
+ const src = FlagMap[name];
+ if (!src) return null;
+
+ return html`
+
+ `;
+ })}
+
+
+
+
+
+
+
+ Layers
+
+
+ ${this.customLayers.map(
+ ({ name, color }, index) => html`
+ -
+
+
+
+ ${name}
+
+
+
+
+
+
+
+
+
+
+ ${this.openColorIndex === index
+ ? html`
+
+ ${this.colorOptions.map(
+ (c) => html`
+
+ `,
+ )}
+
+ `
+ : ""}
+
+ `,
+ )}
+
+
+
+
+ `}
`
: ""}
`;
}
+
+ private encodeCustomFlag(): string {
+ return (
+ "ctmfg" +
+ this.customLayers
+ .map(({ name, color }) => {
+ const shortName = LayerShortNames[name] || name;
+ const shortColor = ColorShortNames[color] || color.replace("#", "");
+ return `${shortName}-${shortColor}`;
+ })
+ .join("_")
+ );
+ }
+
+ private isCustomFlag(flag: string): boolean {
+ return flag.startsWith("ctmfg");
+ }
+
+ private decodeCustomFlag(code: string): { name: string; color: string }[] {
+ if (!this.isCustomFlag(code)) return [];
+
+ const short = code.replace("ctmfg", "");
+ const reverseNameMap = Object.fromEntries(
+ Object.entries(LayerShortNames).map(([k, v]) => [v, k]),
+ );
+ const reverseColorMap = Object.fromEntries(
+ Object.entries(ColorShortNames).map(([k, v]) => [v, k]),
+ );
+
+ return short.split("_").map((segment) => {
+ const [shortName, shortColor] = segment.split("-");
+ const name = reverseNameMap[shortName] || shortName;
+ const color = reverseColorMap[shortColor] || `#${shortColor}`;
+ return { name, color };
+ });
+ }
+
+ private renderFlagPreview(flag: string) {
+ if (!this.isCustomFlag(flag)) {
+ return html`

`;
+ }
+
+ const layers = this.decodeCustomFlag(flag);
+ return html`
+
+ ${layers.map(({ name, color }) => {
+ const src = FlagMap[name];
+ if (!src) return null;
+
+ return html`
+
+ `;
+ })}
+
+ `;
+ }
}
diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts
index 6166c1d76..3a57bf43e 100644
--- a/src/client/graphics/layers/NameLayer.ts
+++ b/src/client/graphics/layers/NameLayer.ts
@@ -180,6 +180,7 @@ export class NameLayer implements Layer {
flagImg.classList.add("player-flag");
flagImg.style.opacity = "0.8";
flagImg.src = "/flags/" + player.flag() + ".svg";
+ // this.renderPlayerFlag(player.flag(), flagImg);
flagImg.style.zIndex = "1";
flagImg.style.aspectRatio = "3/4";
nameDiv.appendChild(flagImg);
@@ -216,6 +217,102 @@ export class NameLayer implements Layer {
return element;
}
+ renderPlayerFlag(flagCode: string, target: HTMLElement) {
+ if (!flagCode.startsWith("ctmfg")) {
+ if (target instanceof HTMLImageElement) {
+ target.src = "/flags/" + flagCode + ".svg";
+ } else {
+ target.innerHTML = `

`;
+ }
+ return;
+ }
+
+ // ctmfg → カスタム旗表示に差し替え
+ const reverseNameMap = {
+ cc: "center_circle",
+ fr: "frame",
+ fu: "full",
+ ts: "test",
+ };
+
+ const reverseColorMap = {
+ r: "#ff0000",
+ o: "#ffa500",
+ y: "#ffff00",
+ g: "#008000",
+ c: "#00ffff",
+ b: "#0000ff",
+ p: "#800080",
+ h: "#ff69b4",
+ br: "#a52a2a",
+ gr: "#808080",
+ bl: "#000000",
+ w: "#ffffff",
+ t: "#20b2aa",
+ tm: "#ff6347",
+ sb: "#4682b4",
+ };
+
+ const flagMap: Record
= {
+ center_circle: "/flags/custom/center_circle.svg",
+ frame: "/flags/custom/frame.svg",
+ full: "/flags/custom/full.svg",
+ test: "/flags/custom/test.svg",
+ };
+
+ const code = flagCode.replace("ctmfg", "");
+ const layers = code.split("_").map((segment) => {
+ const [shortName, shortColor] = segment.split("-");
+ const name = reverseNameMap[shortName] || shortName;
+ const color = reverseColorMap[shortColor] || "#" + shortColor;
+ return { name, color };
+ });
+
+ // img要素だった場合、置き換える
+ if (target instanceof HTMLImageElement) {
+ const wrapper = document.createElement("div");
+ wrapper.style.width = target.width + "px";
+ wrapper.style.height = target.height + "px";
+ wrapper.className = target.className;
+ wrapper.style.position = target.style.position || "relative";
+ wrapper.style.zIndex = target.style.zIndex;
+ wrapper.style.opacity = target.style.opacity;
+
+ // 古い画像を削除
+ target.replaceWith(wrapper);
+ target = wrapper;
+ }
+
+ // divに描画
+ target.innerHTML = "";
+ target.style.backgroundColor = "white";
+ target.style.overflow = "hidden";
+ target.style.position = "relative";
+ target.style.aspectRatio = "3/4";
+ target.style.border = "1px solid gray";
+
+ for (const { name, color } of layers) {
+ const mask = flagMap[name];
+ 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%";
+ layer.style.backgroundColor = color;
+ 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);
+ }
+ }
renderPlayerInfo(render: RenderInfo) {
if (!render.player.nameLocation() || !render.player.isAlive()) {
this.renders = this.renders.filter((r) => r != render);