/
RaskolnickOFF
/
3D
Обзор
Документация
Войти
/
RaskolnickOFF
/
3D
Код
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
js/debug/Debug.js
390 строк
19 KB
RaskolnickOFF
feat: implement boundless 3x3 tile streaming manager and border tracking system
18 июн 2026, 03:54
18 июн 2026, 03:54
e8f9134
Код
Авторство
О чём код?
// js/Debug/Debug.js — дебаг-система import LIB from 'LIB'; import { GameEvents } from '../source/core/GameEvents.js'; import { runAllTests } from '../tests/runTests.js'; import { TimeDebugOverlay } from './TimeDebugOverlay.js'; import { WorldDebugOverlay } from './WorldDebugOverlay.js'; import { WeatherDebugOverlay } from './WeatherDebugOverlay.js'; import { CloudsDebugOverlay } from './CloudsDebugOverlay.js'; import { TilesDebugOverlay } from './TilesDebugOverlay.js'; import { TileChecker } from '../source/systems/world/generator/TileChecker.js'; import { TileGenerator } from '../source/systems/world/generator/TileGenerator.js'; export class Debug { constructor(SETTINGS, EVENTBUS, SCENE, environmentState) { this.settings = SETTINGS; this.dtAccum = 0; if (!this.settings.debug.status) return; this.interval = SETTINGS.debug.debugInterval; this.eventBus = EVENTBUS; this.environment = environmentState; this.cfg = SETTINGS.UI.debug; this.container = document.createElement('div'); this.timeDebugContainer = null; this.timeDebugOverlay = null; this.worldDebugContainer = null; this.worldDebugOverlay = null; this.weatherDebugContainer = null; this.weatherDebugOverlay = null; this.cloudsDebugOverlay = null; this.tilesDebugContainer = null; this.tilesDebugOverlay = null; this.skyMesh = SCENE.getObjectByName('SkyMesh'); this._renderDOM(); this._handleClick(); this._startDebugSystems(); } _startDebugSystems() { if (this.settings.debug.time) { this.timeDebugOverlay = new TimeDebugOverlay( this.settings.debug.timeInterval, this.settings, this.timeDebugContainer); } if (this.settings.debug.world) { this.worldDebugOverlay = new WorldDebugOverlay( this.settings.debug.worldInterval, this.settings, this.worldDebugContainer); } if (this.settings.debug.weather) { this.weatherDebugOverlay = new WeatherDebugOverlay( this.settings.debug.weatherInterval, this.settings, this.weatherDebugContainer); this.cloudsDebugOverlay = new CloudsDebugOverlay( this.settings, this.weatherDebugContainer); } if (this.settings.debug.tiles) { this.tilesDebugOverlay = new TilesDebugOverlay( this.settings.debug.tilesInterval, this.settings, this.tilesDebugContainer); } } _renderDOM() { if (!this.settings.debug.status) return; function createWrapper(parrent, name, className) { if (!parrent || !name || !className) { try { LIB.log.add('Отсутствие данных', true) } catch (e) { console.table(e); } } const li = document.createElement('li'); const button = document.createElement('button'); button.className = className; button.textContent = name; const div = document.createElement('div'); li.append(button, div); parrent.appendChild(li); return div; } this.container.className = this.cfg.class; const list = document.createElement('ul'); if (this.settings.debug.persistence) { const persistence = createWrapper( list, this.cfg.persistenceHeader, this.cfg.systemClass); const loadButton = document.createElement('button'); loadButton.textContent = this.cfg.loadButton; loadButton.className = this.cfg.loadClass; const saveButton = document.createElement('button'); saveButton.textContent = this.cfg.saveButton; saveButton.className = this.cfg.saveClass; persistence.classList.add(this.cfg.twoButtons); persistence.append(loadButton, saveButton); }; if (this.settings.debug.tests) { const tests = createWrapper( list, this.cfg.testsHeader, this.cfg.systemClass); const testsButton = document.createElement('button'); testsButton.textContent = this.cfg.testsButton; testsButton.className = this.cfg.testsClass; tests.appendChild(testsButton); }; if (this.settings.debug.time) { const time = createWrapper( list, this.cfg.timeHeader, this.cfg.systemClass); time.id = this.cfg.timeID; this.timeDebugContainer = time; const label = document.createElement('label'); const interval = LIB.dom.range( this.settings.debug.timeInterval, this.cfg.timeClass, null, // null is id this.settings.debug.timeIntervalStart, this.settings.debug.timeIntervalCap, this.settings.debug.timeInterval ); const span = document.createElement('span'); span.id = this.cfg.timeIntervalID; span.textContent = `${this.cfg.timeInterval} ${this.settings.debug.timeInterval / 1000} ${this.cfg.timeIntervalType}`; label.append(span, interval); time.appendChild(label); }; if (this.settings.debug.world) { const world = createWrapper( list, this.cfg.worldHeader, this.cfg.systemClass); world.id = this.cfg.worldID; this.worldDebugContainer = world; const label = document.createElement('label'); const interval = LIB.dom.range( this.settings.debug.worldInterval, this.cfg.worldClass, null, this.settings.debug.worldIntervalStart, this.settings.debug.worldIntervalCap, this.settings.debug.worldInterval ); const span = document.createElement('span'); span.id = this.cfg.worldIntervalID; span.textContent = `${this.cfg.worldInterval} ${this.settings.debug.worldInterval / 1000} ${this.cfg.worldIntervalType}`; label.append(span, interval); world.appendChild(label); }; if (this.settings.debug.weather) { const weather = createWrapper( list, this.cfg.weatherHeader, this.cfg.systemClass); weather.id = this.cfg.weatherID; this.weatherDebugContainer = weather; const label = document.createElement('label'); const interval = LIB.dom.range( this.settings.debug.weatherInterval, this.cfg.weatherClass, null, this.settings.debug.weatherIntervalStart, this.settings.debug.weatherIntervalCap, this.settings.debug.weatherInterval ); const span = document.createElement('span'); span.id = this.cfg.weatherIntervalID; span.textContent = `${this.cfg.weatherInterval} ${this.settings.debug.weatherInterval / 1000} ${this.cfg.weatherIntervalType}`; label.append(span, interval); weather.appendChild(label); }; if (this.settings.debug.tiles) { const tiles = createWrapper( list, this.cfg.tilesHeader, this.cfg.systemClass); tiles.id = this.cfg.tilesID; this.tilesDebugContainer = tiles; const label = document.createElement('label'); const interval = LIB.dom.range( this.settings.debug.tilesInterval, this.cfg.tilesClass, null, // null is id this.settings.debug.tilesIntervalStart, this.settings.debug.tilesIntervalCap, this.settings.debug.tilesInterval ); const span = document.createElement('span'); span.id = this.cfg.tilesIntervalID; span.textContent = `${this.cfg.tilesInterval} ${this.settings.debug.tilesInterval / 1000} ${this.cfg.tilesIntervalType}`; label.append(span, interval); tiles.appendChild(label); }; this.container.appendChild(list); document.body.appendChild(this.container); } _handleClick() { if (!this.settings.debug.status) return; this.container.addEventListener('click', (e) => { if (this.settings.debug.persistence) { if (e.target.classList.contains(this.cfg.loadClass)) { LIB.log.add(this.cfg.loadRequestLog, false); this.eventBus.emit(GameEvents.LOAD_REQUESTED); }; if (e.target.classList.contains(this.cfg.saveClass)) { LIB.log.add(this.cfg.saveRequestLog, false); this.eventBus.emit(GameEvents.SAVE_REQUESTED); } }; if (this.settings.debug.tests) { if (e.target.classList.contains(this.cfg.systemClass)) { this.container.querySelectorAll('.' + this.cfg.systemClass) .forEach((el) => { el.parentElement.classList.remove('open'); }); e.target.parentElement.classList.toggle('open'); } if (e.target.classList.contains(this.cfg.testsClass)) { runAllTests(LIB.log); } }; if (this.settings.debug.time) { if (e.target.classList.contains(this.cfg.timeButtonClass)) { switch (e.target.dataset.action) { case 'pause': this.eventBus.emit( GameEvents.TIME_PAUSED); break; case 'resume': this.eventBus.emit( GameEvents.TIME_RESUMED); break; case 'scale-0.1': this.eventBus.emit( GameEvents.TIME_SCALE_CHANGED, { scale: 0.1 }); break; case 'scale-1': this.eventBus.emit( GameEvents.TIME_SCALE_CHANGED, { scale: 1 }); break; case 'scale-10': this.eventBus.emit( GameEvents.TIME_SCALE_CHANGED, { scale: 10 }); break; case 'sunrise': this.eventBus.emit( GameEvents.TIME_SET, { time: 6 }); break; case 'noon': this.eventBus.emit( GameEvents.TIME_SET, { time: 12 }); break; case 'sunset': this.eventBus.emit( GameEvents.TIME_SET, { time: 18 }); break; case 'midnight': this.eventBus.emit( GameEvents.TIME_SET, { time: 0 }); break; } } }; if (this.settings.debug.weather) { if (e.target.classList.contains(this.cfg.weatherButtonClass)) { switch (e.target.dataset.action) { case 'force-rain': this.eventBus.emit( GameEvents.WEATHER_FORCE, { weather: 'rain' }); break; case 'force-snow': this.eventBus.emit( GameEvents.WEATHER_FORCE, { weather: 'snow' }); break; case 'force-clear': this.eventBus.emit( GameEvents.WEATHER_FORCE, { weather: 'clear' }); break; } } }; if (this.settings.debug.tiles) { if (e.target.classList.contains(this.cfg.tilesButtonClass)) { switch (e.target.dataset.action) { case 'check': TileChecker.onCheckTilesClick({ onComplete: () => { this.tilesDebugOverlay.renderGenerateButtons(); } }); break; } } if (e.target.classList.contains(this.cfg.tilesCellClass)) { const x = Number(e.target.dataset.coords.split('_')[0]); const z = Number(e.target.dataset.coords.split('_')[1]); TileChecker.onCheckTilesClick({ x, z, onComplete: () => { this.tilesDebugOverlay.renderGenerateButtons(); } }); } if (e.target.classList.contains(this.cfg.tilesGenerateClass)) { e.stopPropagation(); const cell = e.target.parentNode; if (!cell) return; const [x, z] = cell.dataset.coords.split('_').map(Number); TileGenerator.generateAndDownload(x, z); } } e.target.blur(); }); this.container.addEventListener('input', (e) => { if (this.settings.debug.time) { if (e.target.classList.contains(this.cfg.timeClass)) { const intervalID = this.cfg.timeIntervalID; if (e.target.previousElementSibling.id === intervalID) { try { const span = this.container.querySelector(`#${intervalID}`); span.textContent = `${this.cfg.timeInterval} ${e.target.value / 1000} ${this.cfg.timeIntervalType}`; this.timeDebugOverlay.updateInterval(Number(e.target.value)); } catch (e) { LIB.log.add('Ошибка интервала (время)', true) } } } if (e.target.classList.contains(this.cfg.timelineClass)) { this.eventBus.emit(GameEvents.TIME_SET, { time: Number(event.target.value) }); } }; if (this.settings.debug.world) { if (e.target.classList.contains(this.cfg.worldClass)) { const intervalID = this.cfg.worldIntervalID; if (e.target.previousElementSibling.id === intervalID) { try { const span = this.container.querySelector(`#${intervalID}`); span.textContent = `${this.cfg.worldInterval} ${e.target.value / 1000} ${this.cfg.worldIntervalType}`; this.worldDebugOverlay.updateInterval(Number(e.target.value)); } catch (e) { LIB.log.add('Ошибка интервала (мир)', true) } } } }; if (this.settings.debug.weather) { if (e.target.classList.contains(this.cfg.weatherClass)) { const intervalID = this.cfg.weatherIntervalID; if (e.target.previousElementSibling.id === intervalID) { try { const span = this.container.querySelector(`#${intervalID}`); span.textContent = `${this.cfg.weatherInterval} ${e.target.value / 1000} ${this.cfg.weatherIntervalType}`; this.weatherDebugOverlay.updateInterval(Number(e.target.value)); } catch (e) { LIB.log.add('Ошибка интервала (погода)', true); } } }; if (e.target.classList.contains(this.cfg.weatherIntensityClass)) { const intensity = parseFloat(e.target.value); const currentWeather = this.environment.weather; this.eventBus.emit(GameEvents.WEATHER_SET, { weather: currentWeather, intensity: intensity }); } if (e.target.classList.contains(this.cfg.weatherCloudsCoverageClass)) { // Coverage - Покрытие (или Заполнение): Определяет, какой процент неба занят облаками. try { this.skyMesh.cloudCoverage.value = Number(e.target.value); } catch (e) { LIB.log.add('Ошибка облачности', true); } } if (e.target.classList.contains(this.cfg.weatherCloudsDensityClass)) { // Density — Плотность (или Густота): Задает концентрацию водяного пара внутри самого облака. try { this.skyMesh.cloudDensity.value = Number(e.target.value); } catch (e) { LIB.log.add('Ошибка облачности', true); } } if (e.target.classList.contains(this.cfg.weatherCloudsElevationClass)) { // Elevation — Высота (или Высота над уровнем моря / Альтитуда): Определяет высоту расположения облачного слоя относительно земли. try { this.skyMesh.cloudElevation.value = Number(e.target.value); } catch (e) { LIB.log.add('Ошибка облачности', true); } } }; if (this.settings.debug.tiles) { if (e.target.classList.contains(this.cfg.tilesClass)) { const intervalID = this.cfg.tilesIntervalID; if (e.target.previousElementSibling.id === intervalID) { try { const span = this.container.querySelector(`#${intervalID}`); span.textContent = `${this.cfg.tilesInterval} ${e.target.value / 1000} ${this.cfg.tilesIntervalType}`; this.tilesDebugOverlay.updateInterval(Number(e.target.value)); } catch (e) { LIB.log.add('Ошибка интервала (тайлы)', true) } } } if (e.target.classList.contains(this.cfg.tilesRangeClass)) { this.settings.tiles.default = Number(e.target.value); } }; }); } update(engine, dt) { this.dtAccum += dt * 1000; if (this.dtAccum < this.interval) return; const acc = this.dtAccum; this.dtAccum -= this.interval; if (this.settings.debug.time) { this.timeDebugOverlay.update(this.environment, acc); }; if (this.settings.debug.world) { this.worldDebugOverlay.update(engine, acc); }; if (this.settings.debug.weather) { this.weatherDebugOverlay.update(this.environment, acc); this.cloudsDebugOverlay.update( this.skyMesh, this.settings.debug.weatherInterval, acc); }; if (this.settings.debug.tiles) { this.tilesDebugOverlay.update(this.environment, acc); }; } }