mirror of
https://github.com/MewoLab/AquaDX.git
synced 2026-10-04 04:38:06 +03:00
[chu3] X-VERSE & LINKED VERSE (#204)
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -29,6 +29,14 @@ export const HAS_USERBOX_ASSETS = true
|
||||
|
||||
// Matching servers
|
||||
export const CHU3_MATCHINGS: ChusanMatchingOption[] = [
|
||||
{
|
||||
name: "Yukiotoko",
|
||||
ui: "https://yukiotoko.metatable.sh/",
|
||||
guide: "https://github.com/MewoLab/AquaDX/blob/v1-dev/docs/chu3-national-matching.md",
|
||||
matching: "http://yukiotoko.chara.lol:9004/",
|
||||
reflector: "http://yukiotoko.chara.lol:50201/",
|
||||
coop: ["Missless", "CozyNet", "GMG"]
|
||||
},
|
||||
{
|
||||
name: "林国对战",
|
||||
ui: "https://chu3-match.sega.ink/rooms",
|
||||
@@ -37,12 +45,4 @@ export const CHU3_MATCHINGS: ChusanMatchingOption[] = [
|
||||
reflector: "http://reflector.naominet.live:18080/",
|
||||
coop: ["RinNET", "MysteriaNET"],
|
||||
},
|
||||
{
|
||||
name: "Yukiotoko",
|
||||
ui: "https://yukiotoko.metatable.sh/",
|
||||
guide: "https://github.com/MewoLab/AquaDX/blob/v1-dev/docs/chu3-national-matching.md",
|
||||
matching: "http://yukiotoko.chara.lol:9004/",
|
||||
reflector: "http://yukiotoko.chara.lol:50201/",
|
||||
coop: ["Missless", "CozyNet", "GMG"]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -194,6 +194,8 @@ export const EN_REF_SETTINGS = {
|
||||
'settings.fields.chusanTeamName.desc': 'Customize the text displayed on the top of your profile.',
|
||||
'settings.fields.chusanInfinitePenguins.name': 'Infinite Penguins',
|
||||
'settings.fields.chusanInfinitePenguins.desc': 'Set penguin statues for character level prompting to 999.',
|
||||
'settings.fields.chusanLvUnlockAll.name': 'Unlock Linked Gates',
|
||||
'settings.fields.chusanLvUnlockAll.desc': 'Incurs a long animated sequence, cannot be undone',
|
||||
'settings.fields.chusanMatchingReflector.name': 'Matching Server Reflector',
|
||||
'settings.fields.chusanMatchingReflector.desc': 'URL of the national matching server\'s UDP reflector.',
|
||||
'settings.fields.chusanMatchingServer.name': 'Matching Server',
|
||||
@@ -224,7 +226,7 @@ export const EN_REF_SETTINGS = {
|
||||
'settings.profile.unchanged': 'Unchanged',
|
||||
'settings.export': 'Export Player Data',
|
||||
'settings.batchManualExport': "Export in Batch Manual (for Tachi)",
|
||||
'settings.cabNotice': "Note: These settings will only affect your own cab/setup. If you're playing on someone else's setup, please contact them to change these settings.",
|
||||
'settings.cabNotice': "These settings only apply for your keychip. If you're playing on someone else's setup, please ask them to change these settings.",
|
||||
'settings.siteNotice': "These settings only apply to the website.",
|
||||
'settings.regionNotice': "These settings are shared amongst Mai, Ongeki and Chuni.",
|
||||
'settings.regionSelector.title': "Prefecture Selector",
|
||||
@@ -236,6 +238,7 @@ export const EN_REF_USERBOX = {
|
||||
'userbox.header.general': 'General Settings',
|
||||
'userbox.header.matching': 'National Matching',
|
||||
'userbox.header.matching.symbolChat': 'Chat Symbols (Matching)',
|
||||
'userbox.header.linkedVerse': 'Linked Verse',
|
||||
'userbox.header.userbox': 'UserBox Settings',
|
||||
'userbox.header.preview': 'UserBox Preview',
|
||||
'userbox.nameplateId': 'Nameplate',
|
||||
@@ -245,6 +248,7 @@ export const EN_REF_USERBOX = {
|
||||
'userbox.trophyIdSub2': 'Trophy Sub #2 (Title)',
|
||||
'userbox.mapIconId': 'Map Icon',
|
||||
'userbox.voiceId': 'System Voice',
|
||||
'userbox.stageId': 'Stage',
|
||||
'userbox.avatarWear': 'Avatar Wear',
|
||||
'userbox.avatarHead': 'Avatar Head',
|
||||
'userbox.avatarFace': 'Avatar Face',
|
||||
@@ -265,6 +269,14 @@ export const EN_REF_USERBOX = {
|
||||
'userbox.matching.symbolChat': 'Message Choice',
|
||||
'userbox.matching.symbolChat.default': 'Default',
|
||||
|
||||
'userbox.lv.diffnotice': 'In Linked Verse, you will be matched separately based on your difficulty.',
|
||||
'userbox.lv.difficulty': 'Linked Verse Difficulty',
|
||||
'userbox.lv.difficulty.1': 'LEVEL V (1000 life, MASTER only)',
|
||||
'userbox.lv.difficulty.2': 'LEVEL IV (3000 life, MASTER only)',
|
||||
'userbox.lv.difficulty.3': 'LEVEL III (5000 life, MASTER only)',
|
||||
'userbox.lv.difficulty.4': 'LEVEL II (5000 life, MASTER & EXPERT only)',
|
||||
'userbox.lv.difficulty.5': 'LEVEL I (5000 life, ALL difficulties)',
|
||||
|
||||
'userbox.new.name': 'AquaBox',
|
||||
'userbox.new.setup': 'Drag and drop your Chuni game folder (Lumi or newer) into the box below to display UserBoxes with their nameplate & avatar. All files are handled in-browser.',
|
||||
'userbox.new.setup.notice': 'Select the highest folder containing your game data.',
|
||||
@@ -286,7 +298,7 @@ export const EN_REF_USERBOX = {
|
||||
|
||||
export const EN_REF_MAI_PHOTO = {
|
||||
'maiphoto.title': 'Mai Memorial Photo Gallery',
|
||||
'maiphoto.url_warning': 'Note: If you want to share a photo with your friend, please save the photo. Do not copy image URL because the URL contains sensitive information.',
|
||||
'maiphoto.url_warning': 'If you want to share a photo with your friend, please save the photo. Do not copy image URL because the URL contains sensitive information.',
|
||||
'maiphoto.none': 'No photo found. You can upload photo by clicking upload at the end of each game session.',
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user