export type VoicevoxVoice = Readonly<{
speakerName: string;
styleName: string;
}>;
export type AvatarDefinition = Readonly<{
kind: "zundamon" | "sayo";
accentColor: string;
imagePath?: string;
}>;
export type CharacterDefinition = Readonly<{
displayName: string;
voicevox: VoicevoxVoice;
avatar: AvatarDefinition;
}>;
export const characters = {
zundamon: {
displayName: "ずんだもん",
voicevox: {
speakerName: "ずんだもん",
styleName: "ノーマル",
},
avatar: {
kind: "zundamon",
accentColor: "#79d36f",
},
},
sayo: {
displayName: "小夜",
voicevox: {
speakerName: "小夜/SAYO",
styleName: "ノーマル",
},
avatar: {
kind: "sayo",
accentColor: "#6b5f83",
},
},
} as const satisfies Record<string, CharacterDefinition>;
export type CharacterId = keyof typeof characters;
export type SpeechOptions = Readonly<{
subtitle?: string;
voicevox?: Partial<VoicevoxVoice>;
}>;
export type ShowOptions = Readonly<{
caption?: string;
durationSeconds?: number;
}>;
export type SpeechEvent = Readonly<{
type: "say";
id: string;
character: CharacterId;
text: string;
subtitle?: string;
voicevox?: Partial<VoicevoxVoice>;
}>;
export type ShowEvent = Readonly<{
type: "show";
character: CharacterId;
caption?: string;
durationSeconds?: number;
}>;
export type TimelineEvent = SpeechEvent | ShowEvent;
export const say = (
id: string,
character: CharacterId,
text: string,
options: SpeechOptions = {}
): SpeechEvent => ({
type: "say",
id,
character,
text,
...options,
});
export const show = (
character: CharacterId,
options: ShowOptions = {}
): ShowEvent => ({
type: "show",
character,
...options,
});
export const initialVisibleCharacters: CharacterId[] = ["zundamon"];
export const timeline: TimelineEvent[] = [
say("zunda-001", "zundamon", "みなさんこんにちは、ずんだもんなのだ!"),
say(
"zunda-002",
"zundamon",
"今日のテーマは「ネコミミはなぜかわいいのか?」なのだ。"
),
say(
"zunda-003",
"zundamon",
"まず大きな理由は、丸みのあるシルエットと動きなのだ。"
),
say(
"zunda-004",
"zundamon",
"そして感情が伝わりやすくて、親近感が増すのだ!"
),
show("sayo", {
caption: "ネコミミ代表として、小夜が登場!",
}),
say(
"sayo-001",
"sayo",
"小夜です。ネコミミ代表として、猫耳のかわいさを証明しに来ました。"
),
say("zunda-005", "zundamon", "それじゃあ、また次回なのだ!"),
];
export const isSpeechEvent = (
event: TimelineEvent
): event is SpeechEvent => event.type === "say";
export const script = timeline.filter(isSpeechEvent);