Skip to main content

launcher/pages/display/
ui.rs

1use eframe::egui;
2use std::path::Path;
3use supreme_config::models::{DetailConfig, DisplayConfig, TrainerConfig};
4
5/// Scans the *effective* contents of the game root for renderer driver
6/// DLLs and returns the renderer names available to select, derived from
7/// the `srDD_*.dll` naming pattern (e.g. `srDD_OpenGL.dll` -> `"OpenGL"`).
8///
9/// "Effective" means: real files on disk, minus any the currently-applied
10/// mods hide/replace, plus any a currently-applied mod injects, exactly
11/// what the game's own directory-listing hook would show. This is
12/// [`supreme_fs::GameStorage::list_files`]'s job (it owns all game-file
13/// I/O, mod-awareness included), not something this page resolves itself.
14/// So a mod that ships its own `srDD_*.dll` renderer shows up here too,
15/// not just the vanilla ones physically sitting in `game_path`, as long
16/// as `storage` was actually built hook-aware; with a plain
17/// [`supreme_fs::GameStorage::new`] instance, `list_files` just falls
18/// back to the real files on disk, same as before.
19///
20/// Sorted alphabetically (and deduplicated, courtesy of `list_files`) so
21/// the combo box's entry order is stable across runs.
22fn available_renderers(storage: &supreme_fs::GameStorage) -> Vec<String> {
23    const PREFIX: &str = "srDD_";
24    const SUFFIX: &str = ".dll";
25
26    let mut renderers: Vec<String> = storage
27        .list_files(Path::new(""))
28        .unwrap_or_default()
29        .iter()
30        .filter_map(|name| {
31            name.strip_prefix(PREFIX)
32                .and_then(|s| s.strip_suffix(SUFFIX))
33        })
34        .map(str::to_string)
35        .collect();
36
37    renderers.sort();
38    renderers.dedup();
39    renderers
40}
41
42pub fn display_page(
43    ui: &mut egui::Ui,
44    storage: &supreme_fs::GameStorage,
45    display_config: &mut DisplayConfig,
46    detail_config: &mut DetailConfig,
47    helper_config: &mut TrainerConfig,
48) {
49    ui.heading("Display Configuration");
50    ui.add_space(10.0);
51
52    ui.group(|ui| {
53        ui.label("Renderer:");
54        let renderers = available_renderers(storage);
55        egui::ComboBox::from_id_salt("renderer")
56            .selected_text(&display_config.api_name)
57            .show_ui(ui, |ui| {
58                for renderer in &renderers {
59                    ui.selectable_value(
60                        &mut display_config.api_name,
61                        renderer.clone(),
62                        renderer.as_str(),
63                    );
64                }
65            });
66    });
67
68    ui.add_space(10.0);
69
70    ui.group(|ui| {
71        ui.label("Resolution:");
72
73        let mut resolution_changed = false;
74
75        ui.horizontal(|ui| {
76            ui.label("Width:");
77            if ui
78                .add(egui::DragValue::new(&mut display_config.width).range(640..=3840))
79                .changed()
80            {
81                resolution_changed = true;
82            }
83            ui.label("Height:");
84            if ui
85                .add(egui::DragValue::new(&mut display_config.height).range(480..=2160))
86                .changed()
87            {
88                resolution_changed = true;
89            }
90        });
91
92        ui.horizontal(|ui| {
93            ui.label("Common resolutions:");
94            if ui.button("1920x1080").clicked() {
95                display_config.width = 1920;
96                display_config.height = 1080;
97                resolution_changed = true;
98            }
99            if ui.button("2560x1440").clicked() {
100                display_config.width = 2560;
101                display_config.height = 1440;
102                resolution_changed = true;
103            }
104            if ui.button("3840x2160").clicked() {
105                display_config.width = 3840;
106                display_config.height = 2160;
107                resolution_changed = true;
108            }
109        });
110
111        // Update FOV when resolution changes
112        if resolution_changed {
113            helper_config.fov_width = display_config.width;
114            helper_config.fov_height = display_config.height;
115        }
116
117        ui.add_space(5.0);
118
119        let mut fullscreen = display_config.fullscreen == 1;
120        if ui.checkbox(&mut fullscreen, "Fullscreen").changed() {
121            display_config.fullscreen = u32::from(fullscreen);
122        }
123    });
124
125    ui.add_space(10.0);
126
127    ui.group(|ui| {
128        ui.horizontal(|ui| {
129            ui.label("View Distance:");
130            ui.add(
131                egui::Slider::new(&mut detail_config.visibility_cube_width, 200..=1000).text("units"),
132            );
133            if detail_config.visibility_cube_width > 400 {
134                ui.colored_label(ui.visuals().warn_fg_color, egui::RichText::new("⚠").size(16.0))
135                    .on_hover_text("Warning: Setting this above 400 might cause the game to crash with a kernel error. It depends on the level, in-game location, and resolution.");
136            }
137        });
138    });
139
140    ui.add_space(10.0);
141
142    ui.group(|ui| {
143        ui.horizontal(|ui| {
144            ui.label("Ground detail:");
145            ui.add(egui::DragValue::new(&mut detail_config.ground_detail).range(1..=4));
146            if detail_config.ground_detail >= 4 {
147                ui.colored_label(ui.visuals().warn_fg_color, egui::RichText::new("⚠").size(16.0))
148                    .on_hover_text("Warning: Ground detail level 4 will create clipping issues between different ground materials, in the distance.");
149            }
150        });
151    });
152}