[chu3] X-VERSE & LINKED VERSE (#204)

This commit is contained in:
Raymond
2026-02-22 15:23:21 -05:00
committed by GitHub
parent e755bc9380
commit ebb2e2e4a4
17 changed files with 285 additions and 69 deletions
@@ -3,9 +3,10 @@
import { CHU3_MATCHINGS } from "../../libs/config.js";
import type { ChusanMatchingOption, GameOption } from "../../libs/generalTypes.js";
import { t, ts } from "../../libs/i18n.js";
import { DATA, SETTING } from "../../libs/sdk.js";
import { DATA, GAME, SETTING, USER } from "../../libs/sdk.js";
import StatusOverlays from "../StatusOverlays.svelte";
import GameSettingFields from "./GameSettingFields.svelte";
import { EN_REF } from "../../libs/i18n/en_ref.js";
let custom = false
let overlay = false
@@ -16,6 +17,7 @@
let symbols: Record<number, number> = {};
let allItems: Record<string, Record<string, { name: string }>> = {}
let submitting: string | undefined | null;
let settings: Record<string, GameOption> = {};
let existingUrl = "";
SETTING.get().then(s => {
@@ -30,6 +32,7 @@
if (opt.key.substring(0, symbolKey.length) == symbolKey && opt.value)
symbols[parseInt(opt.key.substring(symbolKey.length))] = opt.value;
})
settings = Object.fromEntries(s.map(v => [v.key, v]))
})
async function fetchSymbolData() {
@@ -49,6 +52,14 @@
return true
}
async function updateLvDifficulty() {
if (submitting) return false
submitting = `chusanLvDifficulty`
await SETTING.set("chusanLvDifficulty", settings["chusanLvDifficulty"].value).catch(e => error = e.message).finally(() => submitting = null);
changed = changed.filter(v => v != `chusanLvDifficulty`)
return true
}
// Click on "Custom" option"
function clickCustom() {
custom = true
@@ -66,13 +77,47 @@
existingUrl = opt.matching
}).catch(e => error = e.message)
}
let linkedVerseAvailable = false;
USER.me().then(async me => {
let summary = await GAME.userSummary(me.username, "chu3")
let version = summary.lastVersion.split(".");
if (version[0] == "2" && parseInt(version[1]) >= 40)
linkedVerseAvailable = true;
})
</script>
<StatusOverlays {error} {loading}/>
<div class="matching">
<h2>{t("userbox.header.matching")}</h2>
<p class="notice">{t("settings.cabNotice")}</p>
<h2>{t("userbox.header.matching")}{linkedVerseAvailable ? ` & ${t("userbox.header.linkedVerse")}` : ""}</h2>
<blockquote class="info">
{t("settings.cabNotice")}
{#if linkedVerseAvailable}
{t("userbox.lv.diffnotice")}
{/if}
</blockquote>
{#if linkedVerseAvailable}
<GameSettingFields game="chu3-linked-verse" />
{#if settings["chusanLvDifficulty"]}
<div class="field">
<label for={`chusanLvDifficulty`}>{ts(`userbox.lv.difficulty`)}</label>
<div>
<select bind:value={settings["chusanLvDifficulty"].value} id={`chusanLvDifficulty`} on:change={() => {changed = [...changed, `chusanLvDifficulty`];}}>
{#each {length: 5}, i}
<option value={i + 1}>{t(`userbox.lv.difficulty.${i + 1}` as keyof typeof EN_REF)}</option>
{/each}
</select>
{#if changed.includes(`chusanLvDifficulty`)}
<button transition:slide={{axis: "x"}} disabled={!!submitting} on:click={updateLvDifficulty}>
{t("settings.profile.save")}
</button>
{/if}
</div>
</div>
{/if}
{/if}
<div class="matching-selector">
<button on:click={_ => overlay = true}>{t('userbox.matching.select')}</button>
@@ -31,7 +31,7 @@
// Available (unlocked) options for each kind of item
// In allItems: 'namePlate', 'frame', 'trophy', 'mapIcon', 'systemVoice', 'avatarAccessory'
let allItems: Record<string, Record<string, { name: string }>> = {}
let iKinds = { namePlate: 1, frame: 2, trophy: 3, trophySub1: 4, trophySub2: 5, mapIcon: 8, systemVoice: 9, avatarAccessory: 11 }
let iKinds = { namePlate: 1, frame: 2, trophy: 3, trophySub1: 4, trophySub2: 5, mapIcon: 8, systemVoice: 9, avatarAccessory: 11, stage: 13 }
// In userbox: 'nameplateId', 'frameId', 'trophyId', 'mapIconId', 'voiceId', 'avatar{Wear/Head/Face/Skin/Item/Front/Back}'
let userbox: UserBox
let avatarKinds = ['Wear', 'Head', 'Face', 'Skin', 'Item', 'Front', 'Back'] as const
@@ -256,7 +256,7 @@
type OnlyNumberPropsOf<T extends Record<string, any>> = {[Prop in keyof T as (T[Prop] extends number ? Prop : never)]: T[Prop]}
let userboxSelected: keyof OnlyNumberPropsOf<UserBox> = "avatarWear";
const userboxNewOptions = ["systemVoice", "frame", "trophy", "mapIcon"]
const userboxNewOptions = ["systemVoice", "frame", "trophy", "mapIcon", "stage"]
async function userboxSafeDrop(event: Event & { currentTarget: EventTarget & HTMLInputElement; }) {
if (!event.target) return null;
@@ -317,21 +317,23 @@
{#if !USERBOX_ENABLED.value || !USERBOX_INSTALLED}
<div class="fields">
{#each userItems as { iKey, ubKey, items }, i}
<div class="field">
<label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
<div>
<select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
{#each items as option}
<option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
{/each}
</select>
{#if changed.includes(ubKey)}
<button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
{t("settings.profile.save")}
</button>
{/if}
{#if items.length > 0}
<div class="field">
<label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
<div>
<select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
{#each items as option}
<option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
{/each}
</select>
{#if changed.includes(ubKey)}
<button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
{t("settings.profile.save")}
</button>
{/if}
</div>
</div>
</div>
{/if}
{/each}
</div>
{:else}
@@ -372,21 +374,23 @@
</div>
<div class="fields">
{#each userItems.filter(i => userboxNewOptions.includes(i.iKey)) as { iKey, ubKey, items }, i}
<div class="field">
<label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
<div>
<select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
{#each items as option}
<option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
{/each}
</select>
{#if changed.includes(ubKey)}
<button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
{t("settings.profile.save")}
</button>
{/if}
{#if items.length > 0}
<div class="field">
<label for={ubKey}>{ts(`userbox.${ubKey}`)}</label>
<div>
<select bind:value={userbox[ubKey]} id={ubKey} on:change={() => changed = [...changed, ubKey]}>
{#each items as option}
<option value={option.itemId}>{allItems[iKey][option.itemId]?.name || `(unknown ${option.itemId})`}</option>
{/each}
</select>
{#if changed.includes(ubKey)}
<button transition:slide={{axis: "x"}} on:click={() => submit(ubKey)} disabled={!!submitting}>
{t("settings.profile.save")}
</button>
{/if}
</div>
</div>
</div>
{/if}
{/each}
</div>
{/if}