Не получилось открыть игру. Проверь, что ты вошёл в аккаунт,
@@ -492,6 +508,81 @@ const LessonPage = ({ game, navigate }) => {
);
};
+// ══════════════════════════════════════════════════════════════════
+// Модалка выбора языка скриптов при «Открыть копию»
+// ══════════════════════════════════════════════════════════════════
+const LangChoiceModal = ({ onPick, onCancel }) => {
+ return (
+
+
e.stopPropagation()}>
+
На каком языке открыть копию?
+
+ Скрипты в твоей копии будут написаны на выбранном языке.
+ Логика игры одинаковая — отличается только запись кода.
+
+
+
+
+
+
+
+
+ );
+};
+
+/**
+ * Конвертирует все JS-скрипты в project_data в Lua-эквивалент.
+ * Сейчас простая стратегия: если в скрипте есть code_lua слот, делает его
+ * активным. Иначе ставит флаг language='lua' и пустой Lua-шаблон с TODO.
+ * Полноценная транспиляция JS→Lua невозможна без AST-анализа.
+ */
+function convertProjectScriptsToLua(projectData) {
+ const scene = projectData?.scene;
+ if (!scene || !Array.isArray(scene.scripts)) return projectData;
+ scene.scripts = scene.scripts.map(s => {
+ if (s.language === 'lua') return s;
+ // Если уже есть готовый Lua-слот — используем его
+ if (s.code_lua && s.code_lua.trim()) {
+ return {
+ ...s,
+ language: 'lua',
+ code: s.code_lua,
+ code_js: s.code_js || s.code,
+ code_lua: s.code_lua,
+ };
+ }
+ // Иначе ставим заглушку с подсказкой
+ const luaStub = `-- TODO: версия этого скрипта на Lua пока не готова.
+-- Оригинальный JS-код сохранён ниже (переключи язык назад на JS в редакторе).
+-- Доступные API: game:GetService("Players"), game.Workspace, script.Parent
+--
+-- Например, простой пример:
+local Players = game:GetService("Players")
+print("Привет от Lua-скрипта")
+`;
+ return {
+ ...s,
+ language: 'lua',
+ code: luaStub,
+ code_js: s.code_js || s.code,
+ code_lua: luaStub,
+ };
+ });
+ return projectData;
+}
+
// ══════════════════════════════════════════════════════════════════
// Инлайн-стили
// ══════════════════════════════════════════════════════════════════
diff --git a/src/community/docsData.jsx b/src/community/docsData.jsx
index ae4fcc7..e802e4c 100644
--- a/src/community/docsData.jsx
+++ b/src/community/docsData.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import DocIcon from './docsIcons';
+import { LangTabs } from './docsLang';
/**
* docsData.jsx — контент вики редактора Рублокс (разделы A-J).
@@ -1122,6 +1123,119 @@ game.gui.onSubmit(boxId, (text) => {
title: 'Скрипты — основы',
summary: 'Самый важный раздел: что такое скрипт, переменные, события, таймеры.',
sections: [
+ {
+ id: 'js-or-lua',
+ title: 'D0. Скриптинг: JS или Lua — что выбрать?',
+ body: (
+ <>
+
+ В Рублоксе можно писать скрипты на двух языках:
+ JavaScript и Lua. Оба работают одинаково
+ хорошо. Игра не отличает их между собой — внутри одного
+ проекта одни скрипты могут быть на JS, другие на Lua,
+ и они общаются между собой как будто это один язык.
+
+
Чем они отличаются?
+
+
+
JavaScript
Lua
+
+
+
+
Где ещё используется
+
Сайты, мобильные приложения, серверы.
+ Самый популярный язык в мире.
+
Roblox, World of Warcraft (моды),
+ многие игры. Простой и быстрый.
+
+
+
Главный API
+
game.* (game.player,
+ game.log, game.scene)
+
game.* в Roblox-стиле
+ (game:GetService("Players"), workspace)
+
+
+
Похож на
+
Roblox-LUA если знаешь Roblox
+
Roblox-Studio — те же команды
+
+
+
Когда выбрать
+
Если планируешь делать сайты и приложения
+ — JS пригодится везде.
+
Если играешь в Roblox и видел там скрипты
+ — Lua тебе знаком.
+
+
+
+
+
Один и тот же пример на двух языках:
+
Когда игрок касается синего блока — печатаем «Привет».
+ {`// JS — глобальный скрипт
+game.onTouch('синий-блок', (player) => {
+ game.log('Привет, ' + player.name + '!');
+});`}}
+ lua={{`-- Lua — скрипт на самом блоке
+local part = script.Parent
+
+part.Touched:Connect(function(hit)
+ local player = game.Players:GetPlayerFromCharacter(hit.Parent)
+ if player then
+ print("Привет, " .. player.Name .. "!")
+ end
+end)`}}
+ />
+
+ Видишь — оба варианта делают одно и то же. Но
+ запись отличается. JS короче для простых вещей через
+ game.onTouch, Lua даёт точный Roblox-стиль
+ через :Connect и события.
+
+
+
Что выбирать новичку?
+
+ Если ты совсем новичок — бери JavaScript.
+ Команды game.* в JS короче и проще читать.
+ Большая часть уроков в этой вике написана с примерами
+ на JS — все они работают, просто копируй.
+
+
+ Если ты уже играл в Roblox и видел там скрипты
+ — бери Lua. Команды почти один в один как
+ в Roblox Studio: game:GetService,
+ :Connect, workspace,
+ script.Parent.
+
+
+
Можно ли менять язык в одном скрипте?
+
+ Да. В редакторе скрипта вверху есть две кнопки —
+ JS и Lua. Просто нажми на нужную.
+ Твой код на текущем языке сохранится, а
+ на другом языке откроется пустой шаблон или то, что
+ ты писал там раньше. Никогда ничего не теряется.
+
+
+ Создай новый скрипт. Напиши пару строк на JS. Нажми
+ кнопку Lua — JS-код спрячется, появится
+ Lua-шаблон. Напиши там что-то. Нажми JS
+ обратно — твой JS-код вернётся. Магия!
+
+
+
А что под капотом?
+
+ JS-скрипты исполняются в WebWorker —
+ это отдельный поток в браузере, чтобы скрипт не
+ тормозил саму игру. Lua-скрипты исполняются через
+ wasmoon — Lua-интерпретатор, скомпилированный
+ в WebAssembly. Оба варианта работают на любом
+ устройстве без установки.
+
+ >
+ ),
+ },
{
id: 'what-is-script',
title: 'D1. Что такое скрипт и как его создать',
diff --git a/src/community/docsGamesBuilders.js b/src/community/docsGamesBuilders.js
index 1e92045..77b2da3 100644
--- a/src/community/docsGamesBuilders.js
+++ b/src/community/docsGamesBuilders.js
@@ -6158,8 +6158,37 @@ export function hasGameBuilder(id) {
return typeof GAME_BUILDERS[id] === 'function';
}
-/** Построить project_data для игры-урока. Возвращает объект или null. */
-export function buildGameProject(id) {
+/** Построить project_data для игры-урока. Возвращает объект или null.
+ * opts.lang: 'js' (default) | 'lua' — на каком языке скрипты в копии.
+ */
+export function buildGameProject(id, opts = {}) {
const fn = GAME_BUILDERS[id];
- return fn ? fn() : null;
+ if (!fn) return null;
+ const project = fn();
+ if (opts.lang === 'lua' && project) {
+ // Если в скрипте есть code_lua слот — делаем его активным.
+ // Иначе ставим stub с заметкой что Lua-версия в работе.
+ const scene = project.scene || {};
+ if (Array.isArray(scene.scripts)) {
+ scene.scripts = scene.scripts.map(s => {
+ if (s.language === 'lua') return s;
+ if (s.code_lua && s.code_lua.trim()) {
+ return { ...s, language: 'lua', code: s.code_lua, code_js: s.code_js || s.code };
+ }
+ const luaStub = `-- TODO: Lua-версия этого скрипта пока не готова.
+-- Переключи язык на JS в редакторе (кнопка JS вверху), чтобы увидеть рабочий код.
+-- Lua-API: game:GetService("Players"), workspace, script.Parent
+print("Lua-скрипт запущен (заглушка)")
+`;
+ return {
+ ...s,
+ language: 'lua',
+ code: luaStub,
+ code_js: s.code_js || s.code,
+ code_lua: luaStub,
+ };
+ });
+ }
+ }
+ return project;
}
diff --git a/src/community/docsLang.jsx b/src/community/docsLang.jsx
new file mode 100644
index 0000000..55df3ec
--- /dev/null
+++ b/src/community/docsLang.jsx
@@ -0,0 +1,324 @@
+/**
+ * docsLang.jsx — поддержка вкладок JS/Lua в статьях вики.
+ *
+ * Компоненты:
+ * — оборачивает страницу статьи, хранит выбранный язык
+ * в localStorage 'rublox.docs.lang' ('js' | 'lua').
+ * — большой переключатель JS/Lua над статьёй.
+ * — вкладка-переключатель внутри статьи. Показывает
+ * либо js, либо lua, согласно текущему языку.
+ * useDocsLang() — хук: возвращает {lang, setLang}.
+ *
+ * Если в статье нет ни одного — она одинаково выглядит на обоих
+ * языках (общая теория, не зависящая от языка скриптов).
+ */
+import React, { createContext, useContext, useEffect, useState } from 'react';
+
+const LS_KEY = 'rublox.docs.lang';
+const DEFAULT_LANG = 'js';
+
+const DocsLangContext = createContext({
+ lang: DEFAULT_LANG,
+ setLang: () => {},
+});
+
+export function DocsLangProvider({ children }) {
+ const [lang, setLangState] = useState(() => {
+ try {
+ const v = localStorage.getItem(LS_KEY);
+ return v === 'lua' ? 'lua' : 'js';
+ } catch (_) {
+ return DEFAULT_LANG;
+ }
+ });
+ const setLang = (next) => {
+ const v = next === 'lua' ? 'lua' : 'js';
+ setLangState(v);
+ try { localStorage.setItem(LS_KEY, v); } catch (_) {}
+ };
+ useEffect(() => {
+ // Слушаем смену из других вкладок
+ const onStorage = (e) => {
+ if (e.key === LS_KEY && (e.newValue === 'js' || e.newValue === 'lua')) {
+ setLangState(e.newValue);
+ }
+ };
+ window.addEventListener('storage', onStorage);
+ return () => window.removeEventListener('storage', onStorage);
+ }, []);
+ return (
+
+ {children}
+
+ );
+}
+
+export function useDocsLang() {
+ return useContext(DocsLangContext);
+}
+
+/** Большой переключатель над статьёй: «На каком языке смотреть код?» */
+export function DocsLangPicker() {
+ const { lang, setLang } = useDocsLang();
+ return (
+
+
+ Язык скриптов в этой статье:
+
+
+
+
+
+
+ Не знаешь что выбрать? Смотри статью D0. Скриптинг: JS или Lua?
+
+
+ );
+}
+
+/**
+ * Локальный переключатель вкладок внутри статьи. Если js/lua —
+ * прямой контент (children), если на странице нет —
+ * показываем оба заголовками.
+ *
+ * Использование:
+ * game.log('Привет')}
+ * lua={print('Привет')}
+ * />
+ */
+export function LangTabs({ js, lua }) {
+ const { lang, setLang } = useDocsLang();
+ const hasJs = js !== undefined && js !== null;
+ const hasLua = lua !== undefined && lua !== null;
+ if (!hasJs && !hasLua) return null;
+ // Если есть только один язык — показываем без переключателя
+ if (hasJs && !hasLua) return <>{js}>;
+ if (!hasJs && hasLua) return <>{lua}>;
+ return (
+