mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
feat(hud): Cyber HUD theme with GPU contact sonar (#706)
Add the opt-in Cyber HUD layout with coordinated map and cockpit panel styling, GPU-only contact sonar (rings, range, power, opacity, sector) with native-rendering fallback, and set_cyber_sonar voice controls. Sonar visual style inspired by kk376's tactical naval sonar (#214). Co-authored-by: Sameh Khamis <[email protected]> Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
74 files changed
+7320
-90
No files matched your search
@@ -32,6 +32,13 @@ import {
|
||||
shareCacheNeedsHeal,
|
||||
shareableDetectionState,
|
||||
} from '../contactsDetectionPolicy.js';
|
||||
import {
|
||||
applyCyberSonarSettings,
|
||||
isCyberSonarEnabled,
|
||||
readCyberSonarSettings,
|
||||
setCyberSonarEnabled,
|
||||
} from '../cyberSonar.js';
|
||||
import { cyberVisualDefaultsForHudTransition } from '../hudLayouts.js';
|
||||
const DETECTION_ALLOCATION_STORAGE_KEY = 'gev:detection-allocation:v1';
|
||||
|
||||
/** Own visual preferences, detection overrides and display-control state. */
|
||||
@@ -602,19 +609,87 @@ export class VisualSettings {
|
||||
this._syncShareState();
|
||||
}
|
||||
|
||||
_setHudVariant(variantName) {
|
||||
_setHudVariant(variantName, { applyVisualDefaults = false } = {}) {
|
||||
if (!variantName) return;
|
||||
const previousVariant = this.hud.getVariant();
|
||||
this.hud.setVariant(variantName);
|
||||
if (
|
||||
this._hudLayoutSelect &&
|
||||
this._hudLayoutSelect.value !== this.hud.getVariant()
|
||||
) {
|
||||
this._hudLayoutSelect.value = this.hud.getVariant();
|
||||
const nextVariant = this.hud.getVariant();
|
||||
if (this._hudLayoutSelect && this._hudLayoutSelect.value !== nextVariant) {
|
||||
this._hudLayoutSelect.value = nextVariant;
|
||||
}
|
||||
const visualDefaults = cyberVisualDefaultsForHudTransition(
|
||||
previousVariant,
|
||||
nextVariant,
|
||||
{ explicit: applyVisualDefaults },
|
||||
);
|
||||
if (visualDefaults) this._applyCyberVisualDefaults(visualDefaults);
|
||||
this._syncCyberSonarControl();
|
||||
this._syncShareState();
|
||||
this._scheduleAdaptivePanelLayout({ settle: true });
|
||||
}
|
||||
|
||||
_applyCyberVisualDefaults({ style, ironbow }) {
|
||||
this.setStyle(style, {
|
||||
applyPreset: false,
|
||||
revealParameters: false,
|
||||
});
|
||||
const thermalUniforms = this.stages?.thermal?.uniforms;
|
||||
if (!thermalUniforms || thermalUniforms.palette === undefined) return;
|
||||
thermalUniforms.palette = ironbow;
|
||||
this._updateSliderPanel('thermal', { reveal: false });
|
||||
this.services.governorRequestRender('cyber-visual-defaults');
|
||||
}
|
||||
|
||||
_syncCyberSonarControl() {
|
||||
if (!this._cyberSonarBtn) return;
|
||||
const enabled = isCyberSonarEnabled();
|
||||
const settings = applyCyberSonarSettings(readCyberSonarSettings());
|
||||
this._cyberSonarBtn.classList.toggle('active', enabled);
|
||||
this._cyberSonarBtn.setAttribute('aria-pressed', String(enabled));
|
||||
this._cyberSonarBtn.textContent = enabled ? 'ON' : 'OFF';
|
||||
for (const [input, output, value, suffix] of [
|
||||
[this._cyberSonarRings, this._cyberSonarRingsValue, settings.rings, ''],
|
||||
[this._cyberSonarRange, this._cyberSonarRangeValue, settings.range, '%'],
|
||||
[
|
||||
this._cyberSonarIntensity,
|
||||
this._cyberSonarIntensityValue,
|
||||
settings.intensity,
|
||||
'%',
|
||||
],
|
||||
[
|
||||
this._cyberSonarOpacity,
|
||||
this._cyberSonarOpacityValue,
|
||||
settings.opacity,
|
||||
'%',
|
||||
],
|
||||
[
|
||||
this._cyberSonarSector,
|
||||
this._cyberSonarSectorValue,
|
||||
settings.sector,
|
||||
'°',
|
||||
],
|
||||
]) {
|
||||
if (input) input.value = String(value);
|
||||
if (output) output.textContent = `${value}${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
_setCyberSonarEnabled(enabled = !isCyberSonarEnabled()) {
|
||||
const next = setCyberSonarEnabled(!!enabled);
|
||||
this._syncCyberSonarControl();
|
||||
this.services.governorRequestRender('cyber-sonar-toggle');
|
||||
return next;
|
||||
}
|
||||
|
||||
_setCyberSonarSetting(name, value) {
|
||||
const settings = readCyberSonarSettings();
|
||||
if (!Object.hasOwn(settings, name)) return settings;
|
||||
const next = applyCyberSonarSettings({ ...settings, [name]: value });
|
||||
this._syncCyberSonarControl();
|
||||
this.services.governorRequestRender(`cyber-sonar-${name}`);
|
||||
return next;
|
||||
}
|
||||
|
||||
_applyStylePresetDefaults(styleName) {
|
||||
const { governorRequestRender } = this.services;
|
||||
const preset = STYLE_PRESET_DEFAULTS[styleName];
|
||||
@@ -656,7 +731,9 @@ export class VisualSettings {
|
||||
this._setSharpenEnabled(sharpenInput.enabled);
|
||||
}
|
||||
|
||||
if (preset.hudVariant) {
|
||||
// Cyber is an explicit shell choice, independent of the imagery filter.
|
||||
// Scene/share restoration still applies its own HUD through _setHudVariant.
|
||||
if (preset.hudVariant && this.hud.getVariant() !== 'cyber') {
|
||||
this._setHudVariant(preset.hudVariant);
|
||||
}
|
||||
if (typeof preset.hudVisible === 'boolean') {
|
||||
|
||||
Reference in new issue
Block a user