yukinovel
Version:
Yukinovel is a simple web visual novel engine.
688 lines (679 loc) • 32.4 kB
JavaScript
export class MainMenuRenderer {
constructor(game, container) {
this.game = game;
this.mainMenuContainer = container;
}
show() {
this.createMainMenu();
this.mainMenuContainer.style.display = 'block';
}
hide() {
this.mainMenuContainer.style.display = 'none';
this.cleanupBackgroundElements();
}
cleanupBackgroundElements() {
const backgroundVideos = document.body.querySelectorAll('video[style*="position: fixed"][style*="z-index: -1"]');
backgroundVideos.forEach(video => video.remove());
const backgroundOverlays = document.body.querySelectorAll('div[style*="position: fixed"][style*="z-index: -1"]');
backgroundOverlays.forEach(overlay => overlay.remove());
}
createMainMenu() {
this.mainMenuContainer.innerHTML = '';
const config = this.game.getScript().settings?.mainMenu || {};
const langManager = this.game.getLanguageManager();
this.setupBackground(config);
// Main content container
const contentContainer = document.createElement('div');
contentContainer.id = 'container-content';
contentContainer.className = 'vn-main-menu-content';
const layout = config.layout || {};
const alignment = layout.alignment || 'center';
const padding = layout.padding || 50;
if (alignment === 'left') {
contentContainer.classList.add('align-left');
}
else if (alignment === 'right') {
contentContainer.classList.add('align-right');
}
if (layout.titlePosition === 'top') {
contentContainer.classList.add('title-top');
}
else if (layout.titlePosition === 'bottom') {
contentContainer.classList.add('title-bottom');
}
if (padding !== 50) {
contentContainer.style.padding = `${padding}px`;
}
this.createTitle(contentContainer, config, alignment, langManager);
this.createSubtitle(contentContainer, config, alignment, langManager);
this.createButtons(contentContainer, config, langManager);
this.mainMenuContainer.appendChild(contentContainer);
this.addAnimations();
}
setupBackground(config) {
this.mainMenuContainer.className = 'vn-main-menu-container';
let backgroundStyle = '';
if (config.backgroundColor) {
backgroundStyle = `background: ${config.backgroundColor};`;
this.mainMenuContainer.style.background = config.backgroundColor;
}
const backgroundUrl = config.backgroundVideo || config.background;
if (backgroundUrl) {
const backgroundType = this.detectBackgroundType(backgroundUrl);
if (backgroundType === 'video') {
const video = this.setupBackgroundVideo(backgroundUrl);
video.classList.add('vn-video-background');
document.body.appendChild(video);
backgroundStyle = 'background: transparent;';
}
else {
backgroundStyle = `
background-image: url('${backgroundUrl}');
`;
this.mainMenuContainer.classList.add('vn-background');
this.mainMenuContainer.style.backgroundImage = `url('${backgroundUrl}')`;
}
}
}
detectBackgroundType(url) {
const extension = url.toLowerCase().split('.').pop();
if (extension === 'mp4' || extension === 'webm' || extension === 'ogg') {
return 'video';
}
else if (extension === 'gif') {
return 'gif';
}
else {
return 'image';
}
}
setupBackgroundVideo(url) {
const video = document.createElement('video');
video.src = url;
video.autoplay = true;
video.loop = true;
video.muted = true;
video.playsInline = true;
return video;
}
createTitle(container, config, alignment, langManager) {
const titleConfig = config.title || {};
const titleDiv = document.createElement('div');
titleDiv.id = 'title-game';
titleDiv.className = 'vn-main-menu-title';
const titleText = langManager.getLocalizedText(titleConfig.text || this.game.getScript().title || 'Visual Novel');
titleDiv.textContent = titleText;
if (titleConfig.color && titleConfig.color !== 'white') {
titleDiv.style.color = titleConfig.color;
}
if (titleConfig.fontSize && titleConfig.fontSize !== 48) {
titleDiv.style.fontSize = `${titleConfig.fontSize}px`;
}
if (titleConfig.fontFamily && titleConfig.fontFamily !== 'Arial, sans-serif') {
titleDiv.style.fontFamily = titleConfig.fontFamily;
}
if (alignment !== 'center') {
titleDiv.style.textAlign = alignment;
}
if (titleConfig.gradient) {
titleDiv.style.background = titleConfig.gradient;
titleDiv.style.webkitBackgroundClip = 'text';
titleDiv.style.backgroundClip = 'text';
titleDiv.style.webkitTextFillColor = 'transparent';
}
// Animation
if (titleConfig.animation === 'fade') {
titleDiv.style.animation = 'fadeIn 2s ease-in-out';
}
else if (titleConfig.animation === 'slide') {
titleDiv.style.animation = 'slideIn 1s ease-out';
}
else if (titleConfig.animation === 'glow') {
titleDiv.style.animation = 'glow 2s ease-in-out infinite alternate';
}
container.appendChild(titleDiv);
}
createSubtitle(container, config, alignment, langManager) {
const subtitleConfig = config.subtitle || {};
if (subtitleConfig.show !== false) {
const subtitleDiv = document.createElement('div');
subtitleDiv.id = 'subtitle-game';
subtitleDiv.className = 'vn-main-menu-subtitle';
const subtitleText = langManager.getLocalizedText(subtitleConfig.text || '') || langManager.getSubtitleText();
subtitleDiv.textContent = subtitleText;
// Apply custom styles if specified
if (subtitleConfig.fontSize && subtitleConfig.fontSize !== 16) {
subtitleDiv.style.fontSize = `${subtitleConfig.fontSize}px`;
}
if (subtitleConfig.color && subtitleConfig.color !== '#cccccc') {
subtitleDiv.style.color = subtitleConfig.color;
}
if (alignment !== 'center') {
subtitleDiv.style.textAlign = alignment;
}
container.appendChild(subtitleDiv);
}
}
createButtons(container, config, langManager) {
const buttonsContainer = document.createElement('div');
buttonsContainer.id = 'container-button';
buttonsContainer.className = 'vn-main-menu-buttons';
const buttonConfig = config.buttons || {};
const buttonSpacing = buttonConfig.spacing || 15;
const buttonWidth = buttonConfig.width || 300;
if (buttonSpacing !== 15) {
buttonsContainer.style.gap = `${buttonSpacing}px`;
}
if (buttonWidth !== 300) {
buttonsContainer.style.minWidth = `${buttonWidth}px`;
}
const menuButtons = [
{ text: langManager.getText('menu.start'), action: () => this.game.startNewGame(), icon: '' },
{ text: langManager.getText('menu.continue'), action: () => this.game.continueGame(), icon: '' },
{ text: langManager.getText('menu.load'), action: () => this.game.getUIRenderer().showLoadPanel(), icon: '' },
{ text: langManager.getText('menu.settings'), action: () => this.showSettings(buttonsContainer), icon: '' },
{ text: langManager.getText('menu.credits'), action: () => this.showCredits(), icon: '' },
{ text: langManager.getText('menu.exit'), action: () => window.close(), icon: '' }
];
menuButtons.forEach(button => {
const buttonEl = this.createMenuButton(button.text, button.action, button.icon, config);
buttonsContainer.appendChild(buttonEl);
});
container.appendChild(buttonsContainer);
}
createMenuButton(text, action, icon, config) {
const buttonConfig = config.buttons || {};
const button = document.createElement('button');
button.className = 'vn-main-menu-button';
const style = buttonConfig.style || 'modern';
const color = buttonConfig.color || '#4A90E2';
const hoverColor = buttonConfig.hoverColor || '#357ABD';
const textColor = buttonConfig.textColor || '#ffffff';
const fontSize = buttonConfig.fontSize || 18;
const borderRadius = buttonConfig.borderRadius || 8;
if (color !== '#4A90E2') {
button.style.setProperty('--button-bg', color);
}
if (hoverColor !== '#357ABD') {
button.style.setProperty('--button-hover-bg', hoverColor);
}
if (textColor !== '#ffffff') {
button.style.color = textColor;
}
if (fontSize !== 18) {
button.style.fontSize = `${fontSize}px`;
}
if (borderRadius !== 8) {
button.style.borderRadius = `${borderRadius}px`;
}
if (style === 'glass') {
button.classList.add('glass-style');
}
else if (style === 'minimal') {
button.classList.add('minimal-style');
}
else if (style === 'classic') {
button.classList.add('classic-style');
}
button.innerHTML = `<span style="font-size: 20px;">${icon}</span><span>${text}</span>`;
// Animation effects
if (buttonConfig.animation === 'bounce') {
button.classList.add('bounce-animation');
}
else if (buttonConfig.animation === 'slide') {
button.classList.add('slide-animation');
}
button.onclick = action;
return button;
}
addAnimations() {
const style = document.createElement('style');
style.innerHTML = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideIn {
from { transform: translateY(-50px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes glow {
from { text-shadow: 0 0 10px rgba(74,144,226,0.5); }
to { text-shadow: 0 0 20px rgba(74,144,226,1), 0 0 30px rgba(74,144,226,0.8); }
}
.vn-main-menu-button.glass-style {
background: rgba(255,255,255,0.1) !important;
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.2);
}
.vn-main-menu-button.minimal-style {
background: transparent !important;
border: 2px solid var(--button-bg, #4A90E2);
color: var(--button-bg, #4A90E2);
}
.vn-main-menu-button.minimal-style:hover {
background: var(--button-bg, #4A90E2) !important;
color: white;
}
.vn-main-menu-button.classic-style {
background: linear-gradient(45deg, var(--button-bg, #4A90E2), var(--button-hover-bg, #357ABD)) !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.vn-main-menu-button.bounce-animation:hover {
transform: scale(1.05);
}
.vn-main-menu-button.slide-animation:hover {
transform: translateX(10px);
}
`;
document.head.appendChild(style);
}
showSettings(menuOverlay) {
const langManager = this.game.getLanguageManager();
const config = this.game.getScript().settings?.mainMenu || {};
const settingsConfig = config.settings || {};
if (!document.querySelector('link[href*="font-awesome"]')) {
const fontAwesome = document.createElement('link');
fontAwesome.rel = 'stylesheet';
fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
document.head.appendChild(fontAwesome);
}
const settingsContainer = document.createElement('div');
settingsContainer.id = 'settings-container';
settingsContainer.classList.add('vn-settings-container');
this.setupSettingsBackground(settingsContainer, settingsConfig, config);
const mainWrapper = document.createElement('div');
mainWrapper.classList.add('vn-mainwrapper');
const header = document.createElement('div');
header.classList.add('vn-header-page');
const title = document.createElement('h1');
title.classList.add('title');
title.textContent = langManager.getText('menu.settings');
const subtitle = document.createElement('p');
subtitle.classList.add('subtitle');
subtitle.textContent = langManager.getText('settings.subtitle', 'Tùy chỉnh trải nghiệm game của bạn');
header.appendChild(title);
header.appendChild(subtitle);
mainWrapper.appendChild(header);
const settingsContent = document.createElement('div');
settingsContent.id = 'vn-settings-content';
const languageSection = this.createSimpleSettingsSection('fas fa-globe', langManager.getText('settings.language', 'Ngôn ngữ'), langManager.getText('settings.language.desc', 'Chọn ngôn ngữ hiển thị'));
const languageControl = document.createElement('div');
languageControl.classList.add('section-content');
const languageSelect = document.createElement('select');
languageSelect.id = 'language-select';
const availableLanguages = this.game.getAvailableLanguages();
const currentLanguage = this.game.getCurrentLanguage();
availableLanguages.forEach(lang => {
const option = document.createElement('option');
option.value = lang.code;
option.textContent = lang.name;
option.classList.add('language-option');
if (lang.code === currentLanguage) {
option.selected = true;
}
languageSelect.appendChild(option);
});
languageSelect.addEventListener('change', (e) => {
const target = e.target;
this.game.setLanguage(target.value);
});
languageControl.appendChild(languageSelect);
languageSection.appendChild(languageControl);
settingsContent.appendChild(languageSection);
const speedSection = this.createSimpleSettingsSection('fas fa-tachometer-alt', langManager.getText('settings.textSpeed', 'Tốc độ text'), langManager.getText('settings.textSpeed.desc', 'Điều chỉnh tốc độ hiển thị văn bản'));
const speedControl = document.createElement('div');
speedControl.classList.add('section-content');
const sliderContainer = document.createElement('div');
sliderContainer.classList.add('slider-container');
const speedSlider = document.createElement('input');
speedSlider.classList.add('speed-slider');
speedSlider.type = 'range';
speedSlider.min = '5';
speedSlider.max = '100';
speedSlider.value = this.game.getUIRenderer().getTypewriterSpeed().toString();
const speedValue = document.createElement('div');
speedValue.textContent = this.game.getUIRenderer().getTypewriterSpeed() + 'ms';
speedValue.classList.add('value-frame');
speedSlider.addEventListener('input', (e) => {
const target = e.target;
const value = parseInt(target.value);
this.game.getUIRenderer().setTypewriterSpeed(value);
speedValue.textContent = value + 'ms';
});
sliderContainer.appendChild(speedSlider);
sliderContainer.appendChild(speedValue);
const presetsContainer = document.createElement('div');
presetsContainer.classList.add('presets-container');
const presets = [
{ label: langManager.getText('ui.slow', 'Chậm'), value: 60 },
{ label: langManager.getText('ui.normal', 'Bình thường'), value: 20 },
{ label: langManager.getText('ui.fast', 'Nhanh'), value: 15 },
{ label: langManager.getText('ui.veryfast', 'Rất nhanh'), value: 5 }
];
presets.forEach(preset => {
const presetBtn = document.createElement('button');
presetBtn.classList.add('preset-button');
presetBtn.textContent = preset.label;
presetBtn.addEventListener('click', () => {
this.game.getUIRenderer().setTypewriterSpeed(preset.value);
speedSlider.value = preset.value.toString();
speedValue.textContent = preset.value + 'ms';
});
presetsContainer.appendChild(presetBtn);
});
speedControl.appendChild(sliderContainer);
speedControl.appendChild(presetsContainer);
speedSection.appendChild(speedControl);
settingsContent.appendChild(speedSection);
const shortcutsSection = this.createSimpleSettingsSection('fas fa-keyboard', langManager.getText('settings.shortcuts', 'Phím tắt'), langManager.getText('settings.shortcuts.desc', 'Danh sách các phím tắt hữu ích'));
const shortcutsGrid = document.createElement('div');
shortcutsGrid.classList.add('shortcuts-container');
const shortcuts = [
{ key: 'Space / Enter', action: langManager.getText('ui.next', 'Tiếp tục') },
{ key: 'Esc', action: langManager.getText('ui.home', 'Về menu') },
{ key: 'S', action: langManager.getText('ui.save', 'Lưu game') },
{ key: 'L', action: langManager.getText('ui.load', 'Tải game') },
{ key: 'H', action: langManager.getText('ui.history', 'Lịch sử') },
{ key: 'M', action: langManager.getText('ui.menu', 'Menu') }
];
shortcuts.forEach(shortcut => {
const shortcutItem = document.createElement('div');
shortcutItem.classList.add('shortcut-item');
const keyBadge = document.createElement('span');
keyBadge.classList.add('value-frame');
keyBadge.textContent = shortcut.key;
const actionText = document.createElement('span');
actionText.textContent = shortcut.action;
shortcutItem.appendChild(keyBadge);
shortcutItem.appendChild(actionText);
shortcutsGrid.appendChild(shortcutItem);
});
shortcutsSection.appendChild(shortcutsGrid);
settingsContent.appendChild(shortcutsSection);
mainWrapper.appendChild(settingsContent);
const footer = document.createElement('div');
footer.classList.add('footer');
const backButton = document.createElement('button');
backButton.classList.add('back-button');
backButton.innerHTML = `<i class="fas fa-arrow-left"></i> ${langManager.getText('ui.back', 'Quay lại')}`;
backButton.addEventListener('click', () => {
this.cleanupBackgroundElements();
this.game.showMainMenu();
});
footer.appendChild(backButton);
mainWrapper.appendChild(footer);
settingsContainer.appendChild(mainWrapper);
menuOverlay.innerHTML = '';
menuOverlay.appendChild(settingsContainer);
}
createSimpleSettingsSection(iconClass, title, description) {
const section = document.createElement('div');
section.classList.add('section');
const header = document.createElement('div');
header.classList.add('header');
const iconElement = document.createElement('i');
iconElement.className = iconClass;
iconElement.classList.add('icon');
const titleElement = document.createElement('h3');
titleElement.classList.add('title');
titleElement.textContent = title;
header.appendChild(iconElement);
header.appendChild(titleElement);
const descElement = document.createElement('p');
descElement.classList.add('description');
descElement.textContent = description;
section.appendChild(header);
section.appendChild(descElement);
return section;
}
setupSettingsBackground(container, settingsConfig, mainConfig) {
const settingsBackgroundUrl = settingsConfig.backgroundVideo || settingsConfig.background ||
mainConfig.backgroundVideo || mainConfig.background;
const backgroundColor = settingsConfig.backgroundColor || mainConfig.backgroundColor;
if (settingsBackgroundUrl) {
const backgroundType = this.detectBackgroundType(settingsBackgroundUrl);
if (backgroundType === 'video') {
container.style.background = 'transparent';
const video = this.setupBackgroundVideo(settingsBackgroundUrl);
video.classList.add('vn-video-background');
container.appendChild(video);
}
else {
container.style.backgroundImage = `url('${settingsBackgroundUrl}')`;
container.style.backgroundSize = 'cover';
container.style.backgroundPosition = 'center';
container.style.backgroundRepeat = 'no-repeat';
container.style.backgroundAttachment = 'fixed';
container.style.background = `url('${settingsBackgroundUrl}') center/cover no-repeat fixed`;
}
}
if (backgroundColor) {
container.style.background = backgroundColor;
}
}
showCredits() {
const langManager = this.game.getLanguageManager();
const config = this.game.getScript().settings?.mainMenu || {};
const creditsConfig = config.credits || {};
const creditsContainer = document.createElement('div');
creditsContainer.id = 'credits-container';
this.setupCreditsBackground(creditsContainer, creditsConfig, config);
const contentContainer = document.createElement('div');
contentContainer.id = 'credits-content';
this.createCreditsTitle(contentContainer, creditsConfig, langManager);
this.createCreditsSections(contentContainer, creditsConfig, langManager);
this.createCreditsFooter(contentContainer, langManager);
creditsContainer.appendChild(contentContainer);
this.mainMenuContainer.innerHTML = '';
this.mainMenuContainer.appendChild(creditsContainer);
if (creditsConfig.autoScroll) {
this.startAutoScroll(contentContainer, creditsConfig.scrollSpeed || 50);
}
if (creditsConfig.music) {
this.game.getAudioManager().playMusic(creditsConfig.music);
}
}
setupCreditsBackground(container, creditsConfig, mainConfig) {
// let backgroundStyle = 'background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);';
const backgroundUrl = creditsConfig.backgroundVideo || creditsConfig.background ||
mainConfig.backgroundVideo || mainConfig.background;
const backgroundColor = creditsConfig.backgroundColor || mainConfig.backgroundColor;
if (backgroundColor) {
container.style.background = backgroundColor;
}
if (backgroundUrl) {
const backgroundType = this.detectBackgroundType(backgroundUrl);
if (backgroundType === 'video') {
container.classList.add('video');
const video = this.setupBackgroundVideo(backgroundUrl);
video.classList.add('vn-background-video');
container.appendChild(video);
}
else {
container.classList.add('non-video');
container.style.backgroundImage = `url('${backgroundUrl}')`;
}
}
}
createCreditsTitle(container, creditsConfig, langManager) {
const style = creditsConfig.style || {};
const titleText = langManager.getLocalizedText(creditsConfig.title ?? 'en') ||
langManager.getText('credits.title', 'Credits');
const titleElement = document.createElement('h1');
titleElement.className = 'credits-title';
titleElement.textContent = titleText;
titleElement.style.cssText = `
font-size: ${style.titleSize || 48}px;
color: ${style.titleColor || '#ffffff'};
font-family: ${style.fontFamily || 'Arial, sans-serif'};
`;
container.appendChild(titleElement);
}
createCreditsSections(container, creditsConfig, langManager) {
const sections = creditsConfig.sections || this.getDefaultCreditsSections(langManager);
const style = creditsConfig.style || {};
const sectionsContainer = document.createElement('div');
sectionsContainer.classList.add('credits-sections');
const allItems = [];
sections.forEach((section) => {
section.items.forEach((item) => {
allItems.push(item);
});
});
const leftColumnItems = allItems.filter((_, index) => index % 2 === 0);
const rightColumnItems = allItems.filter((_, index) => index % 2 === 1);
const leftColumn = document.createElement('div');
leftColumn.classList.add('credits-column');
leftColumn.style.animation = `creditsSlideIn 0.8s ease-out 0.5s forwards`;
leftColumnItems.forEach((item) => {
const itemElement = this.createSimpleCreditItem(item, style, langManager);
leftColumn.appendChild(itemElement);
});
const rightColumn = document.createElement('div');
rightColumn.classList.add('credits-column');
rightColumn.style.animation = `creditsSlideIn 0.8s ease-out 0.5s forwards`;
rightColumnItems.forEach((item) => {
const itemElement = this.createSimpleCreditItem(item, style, langManager);
rightColumn.appendChild(itemElement);
});
sectionsContainer.appendChild(leftColumn);
sectionsContainer.appendChild(rightColumn);
container.appendChild(sectionsContainer);
}
createSimpleCreditItem(item, globalStyle, langManager) {
const itemElement = document.createElement('div');
itemElement.classList.add('credit-item-simple');
itemElement.style.cursor = `cursor: ${item.link ? 'pointer' : 'default'}`;
if (item.link) {
itemElement.classList.add('hasLink');
itemElement.addEventListener('click', () => {
window.open(item.link, '_blank');
});
}
if (item.role) {
const roleElement = document.createElement('div');
roleElement.className = 'item-role-simple';
roleElement.textContent = langManager.getLocalizedText(item.role);
roleElement.style.color = item.style?.roleColor || globalStyle.linkColor || '#CCCCCC';
itemElement.appendChild(roleElement);
}
const nameElement = document.createElement('div');
nameElement.className = 'item-name-simple';
nameElement.textContent = item.name;
nameElement.style.cssText = `
font-size: ${globalStyle.textSize || 24}px;
color: ${item.style?.nameColor || globalStyle.textColor || '#FFFFFF'};
font-family: ${globalStyle.fontFamily || 'Arial, sans-serif'};
margin-bottom: ${item.description ? '8px' : '0'};
`;
nameElement.style.fontSize = `font-size: ${globalStyle.textSize || 24}px`;
nameElement.style.color = `color: ${item.style?.nameColor || globalStyle.textColor || '#FFFFFF'}`;
nameElement.style.fontFamily = `${globalStyle.fontFamily || 'Arial, sans-serif'}`;
nameElement.style.marginBottom = `${item.description ? '8px' : '0'}`;
itemElement.appendChild(nameElement);
if (item.description) {
const descElement = document.createElement('div');
descElement.className = 'item-description-simple';
descElement.textContent = langManager.getLocalizedText(item.description);
descElement.style.cssText = `
font-size: ${(globalStyle.textSize || 24) - 8}px;
color: ${item.style?.descriptionColor || '#AAAAAA'};
font-family: ${globalStyle.fontFamily || 'Arial, sans-serif'};
`;
itemElement.appendChild(descElement);
}
return itemElement;
}
createCreditsFooter(container, langManager) {
const footerElement = document.createElement('div');
footerElement.classList.add('credits-footer');
const backButton = document.createElement('button');
backButton.classList.add('back-button');
backButton.textContent = `← ${langManager.getText('ui.back', 'Quay lại')}`;
backButton.addEventListener('click', () => {
this.cleanupBackgroundElements();
this.game.showMainMenu();
});
footerElement.appendChild(backButton);
container.appendChild(footerElement);
}
getDefaultCreditsSections(langManager) {
const gameScript = this.game.getScript();
return [
{
title: langManager.getText('credits.development', 'Credits'),
items: [
{
role: langManager.getText('credits.created_directed', 'Created and Directed by'),
name: gameScript.author || 'Unknown Author'
},
{
role: langManager.getText('credits.executive_producer', 'Executive Producer'),
name: 'Visual Novel Team',
description: 'Yuki',
},
{
role: langManager.getText('credits.producer', 'Producer'),
name: 'Game Studio'
},
{
role: langManager.getText('credits.art', 'Art'),
name: 'Art Team'
},
{
role: langManager.getText('credits.sound_designer', 'Sound Designer and Composer'),
name: 'Audio Team'
},
{
role: langManager.getText('credits.programming', 'Programming & Animation'),
name: 'Development Team'
},
{
role: langManager.getText('credits.gameplay_designer', 'Lead Gameplay Designer'),
name: 'Design Team'
},
{
role: langManager.getText('credits.design_scripting', 'Design and Scripting'),
name: 'Script Writers'
},
{
role: langManager.getText('credits.special_thanks', 'Special Thanks'),
name: langManager.getText('credits.you', 'Yukinovel')
},
{
role: langManager.getText('credits.version', 'Version'),
name: gameScript.version || '1.0.0'
}
]
}
];
}
startAutoScroll(container, speed) {
let isScrolling = true;
let scrollPosition = 0;
const maxScroll = container.scrollHeight - container.clientHeight;
const scroll = () => {
if (!isScrolling)
return;
scrollPosition += 1;
container.scrollTop = scrollPosition;
if (scrollPosition >= maxScroll) {
setTimeout(() => {
scrollPosition = 0;
container.scrollTop = 0;
}, 3000);
}
setTimeout(scroll, speed);
};
setTimeout(scroll, 2000);
container.addEventListener('wheel', () => {
isScrolling = false;
setTimeout(() => { isScrolling = true; }, 5000);
});
container.addEventListener('touchstart', () => {
isScrolling = false;
setTimeout(() => { isScrolling = true; }, 5000);
});
}
}