feat: импорт Roblox .rbxl карт (тест-фича МИНа) #33
@ -725,22 +725,28 @@ class Converter:
|
|||||||
return None # top-level в ScreenGui = parentId=None в Rublox
|
return None # top-level в ScreenGui = parentId=None в Rublox
|
||||||
return f'rbx_gui_{parent_ref}'
|
return f'rbx_gui_{parent_ref}'
|
||||||
|
|
||||||
def _udim2_to_rublox(self, udim2, default_scale=None) -> Tuple[float, float]:
|
def _udim2_to_rublox(self, udim2, axis: str = 'x') -> int:
|
||||||
"""Roblox UDim2(scale, offset) → pixel x/y для Rublox GUI.
|
"""Roblox UDim2(scale, offset) → pixel размер для Rublox GUI.
|
||||||
Берём только offset (scale*screen игнорим: точную viewport-ширину не знаем
|
Reference viewport: 1280×720 (стандарт Roblox при импорте).
|
||||||
в момент импорта). Если offset=0 а scale≠0 — used 1280×720 как референс.
|
axis='x' → используется ширина 1280, 'y' → высота 720.
|
||||||
"""
|
"""
|
||||||
if udim2 is None:
|
if udim2 is None:
|
||||||
|
return 0
|
||||||
|
ref = 1280 if axis == 'x' else 720
|
||||||
|
if isinstance(udim2, dict):
|
||||||
|
scale = udim2.get('scale', 0) or 0
|
||||||
|
offset = udim2.get('offset', 0) or 0
|
||||||
|
return int(offset + scale * ref)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _udim2_pair(self, udim2) -> Tuple[int, int]:
|
||||||
|
"""UDim2 = {x: UDim, y: UDim} → (px, py)."""
|
||||||
|
if not isinstance(udim2, dict):
|
||||||
return (0, 0)
|
return (0, 0)
|
||||||
# Формат: UDim2 = {x: UDim(scale, offset), y: UDim(scale, offset)}
|
return (
|
||||||
x_obj = udim2.get('x') if isinstance(udim2, dict) else None
|
self._udim2_to_rublox(udim2.get('x'), 'x'),
|
||||||
y_obj = udim2.get('y') if isinstance(udim2, dict) else None
|
self._udim2_to_rublox(udim2.get('y'), 'y'),
|
||||||
def resolve(u):
|
)
|
||||||
if not u: return 0
|
|
||||||
scale = u.get('scale', 0) if isinstance(u, dict) else 0
|
|
||||||
offset = u.get('offset', 0) if isinstance(u, dict) else 0
|
|
||||||
return int(offset + scale * 1280)
|
|
||||||
return (resolve(x_obj), resolve(y_obj))
|
|
||||||
|
|
||||||
def _color3_to_hex(self, c3) -> str:
|
def _color3_to_hex(self, c3) -> str:
|
||||||
if c3 is None:
|
if c3 is None:
|
||||||
@ -770,12 +776,19 @@ class Converter:
|
|||||||
else:
|
else:
|
||||||
r_type = 'frame'
|
r_type = 'frame'
|
||||||
|
|
||||||
pos_x, pos_y = self._udim2_to_rublox(props.get('Position'))
|
pos_x, pos_y = self._udim2_pair(props.get('Position'))
|
||||||
size_x, size_y = self._udim2_to_rublox(props.get('Size'))
|
size_x, size_y = self._udim2_pair(props.get('Size'))
|
||||||
# Size может быть 0×0 (если только scale) — дефолтим в 100×30
|
# Size может быть 0×0 (если только scale) — дефолтим в 100×30
|
||||||
if size_x <= 0: size_x = 100
|
if size_x <= 0: size_x = 100
|
||||||
if size_y <= 0: size_y = 30
|
if size_y <= 0: size_y = 30
|
||||||
|
|
||||||
|
# Фильтр Roblox CDN URL'ов: rbxasset://, rbxassetid://, rbxhttp:// —
|
||||||
|
# браузер их не поймёт, даём пустую строку. В будущем asset_downloader
|
||||||
|
# может подменить на cached_url.
|
||||||
|
raw_image = str(props.get('Image', '') or '')
|
||||||
|
if raw_image.startswith(('rbxasset://', 'rbxassetid://', 'rbxhttp://', 'rbxthumb://')):
|
||||||
|
raw_image = ''
|
||||||
|
|
||||||
element = {
|
element = {
|
||||||
'id': f'rbx_gui_{inst.referent}',
|
'id': f'rbx_gui_{inst.referent}',
|
||||||
'type': r_type,
|
'type': r_type,
|
||||||
@ -797,7 +810,7 @@ class Converter:
|
|||||||
'textSize': int(props.get('TextSize', 14) or 14),
|
'textSize': int(props.get('TextSize', 14) or 14),
|
||||||
'textAlign': 'center',
|
'textAlign': 'center',
|
||||||
'fontWeight': 700 if cls in ('TextButton',) else 500,
|
'fontWeight': 700 if cls in ('TextButton',) else 500,
|
||||||
'imageUrl': str(props.get('Image', '') or ''),
|
'imageUrl': raw_image,
|
||||||
'imageAsset': None,
|
'imageAsset': None,
|
||||||
'zIndex': int(props.get('ZIndex', 1) or 1),
|
'zIndex': int(props.get('ZIndex', 1) or 1),
|
||||||
'origin': 'roblox-' + cls.lower(),
|
'origin': 'roblox-' + cls.lower(),
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { RobloxLuaSharedSandbox } from './RobloxLuaSharedSandbox.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Распаковывает Lua-исходник из поля code упакованного rbxl-importer'ом.
|
* Распаковывает Lua-исходник из поля code упакованного rbxl-importer'ом.
|
||||||
* Формат: `// @roblox-lua\n// {meta json}\n/* lua_source:\n<код>\n*/`
|
* Формат: "// @roblox-lua\\n// {meta json}\\n/[*] lua_source:\\n<код>\\n[*]/"
|
||||||
*/
|
*/
|
||||||
export function unpackRobloxLuaCode(code) {
|
export function unpackRobloxLuaCode(code) {
|
||||||
const openTag = '/* lua_source:\n';
|
const openTag = '/* lua_source:\n';
|
||||||
@ -153,8 +153,3 @@ export function handleLuaCommand(_scriptId, cmd, payload, runtime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ──────── Legacy single-script API (для обратной совместимости) ──────── */
|
|
||||||
// Старая логика per-script Worker оставлена в RobloxLuaSandbox.js + RobloxLuaWorker.js,
|
|
||||||
// но GameRuntime теперь использует startRobloxLuaShared. Эти экспорты не удалены
|
|
||||||
// чтобы тесты в player/tests/ продолжали работать.
|
|
||||||
export { startRobloxLuaShared as startRobloxLuaScript };
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user