226 lines
6.8 KiB
GDScript
226 lines
6.8 KiB
GDScript
extends Node
|
|
|
|
# SettingsManager - Global singleton for handling game settings persistence
|
|
# Autoloaded as "SettingsManager"
|
|
|
|
const SETTINGS_FILE = "user://game_settings.cfg"
|
|
|
|
const RESOLUTIONS = [
|
|
Vector2i(1024, 576),
|
|
Vector2i(1280, 720),
|
|
Vector2i(1366, 768),
|
|
Vector2i(1600, 900),
|
|
Vector2i(1920, 1080),
|
|
Vector2i(2560, 1440)
|
|
]
|
|
|
|
# Default values
|
|
var settings = {
|
|
"video": {
|
|
"fullscreen": true,
|
|
"vsync": true,
|
|
"resolution_idx": 1,
|
|
"msaa": 0, # 0: Disabled, 1: 2x, 2: 4x, 3: 8x
|
|
"shadow_quality": 2, # 0: Low, 1: Med, 2: High, 3: Ultra
|
|
"fps_cap": 2 # 0: Unlimited, 1: 30, 2: 60, 3: 120, 4: 144
|
|
},
|
|
"audio": {
|
|
"master_volume": 0.8,
|
|
"music_volume": 0.7,
|
|
"sfx_volume": 0.9
|
|
},
|
|
"controls": {
|
|
"use_controller": false,
|
|
# Movement
|
|
"move_up": KEY_W,
|
|
"move_up_alt": KEY_UP,
|
|
"move_down": KEY_S,
|
|
"move_down_alt": KEY_DOWN,
|
|
"move_left": KEY_A,
|
|
"move_left_alt": KEY_LEFT,
|
|
"move_right": KEY_D,
|
|
"move_right_alt": KEY_RIGHT,
|
|
|
|
# Actions
|
|
"grab": KEY_SPACE,
|
|
"grab_alt": KEY_J,
|
|
"put": KEY_R,
|
|
"put_alt": KEY_K,
|
|
"tekton_grab": KEY_G,
|
|
"tekton_grab_alt": KEY_L,
|
|
|
|
# Power-Up Controls
|
|
"use_powerup": KEY_F,
|
|
"use_powerup_alt": KEY_SHIFT,
|
|
|
|
# Power Bar Controls / Special
|
|
"attack_mode": KEY_Q,
|
|
"attack_mode_alt": KEY_U
|
|
}
|
|
}
|
|
|
|
signal settings_applied
|
|
signal control_remapped(action: String, key: int)
|
|
|
|
func _ready():
|
|
load_settings()
|
|
apply_all_settings()
|
|
|
|
func load_settings():
|
|
var config = ConfigFile.new()
|
|
var err = config.load(SETTINGS_FILE)
|
|
|
|
if err == OK:
|
|
for section in settings.keys():
|
|
for key in settings[section].keys():
|
|
settings[section][key] = config.get_value(section, key, settings[section][key])
|
|
print("[Settings] Loaded.")
|
|
else:
|
|
print("[Settings] Using defaults.")
|
|
|
|
func save_settings():
|
|
var config = ConfigFile.new()
|
|
for section in settings.keys():
|
|
for key in settings[section].keys():
|
|
config.set_value(section, key, settings[section][key])
|
|
config.save(SETTINGS_FILE)
|
|
|
|
func apply_all_settings():
|
|
apply_video_settings()
|
|
apply_audio_settings()
|
|
apply_control_settings()
|
|
emit_signal("settings_applied")
|
|
|
|
func apply_video_settings():
|
|
var video = settings.video
|
|
|
|
if video.fullscreen:
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
|
else:
|
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
|
|
|
# Apply resolution size when windowed
|
|
var res_idx = video.resolution_idx
|
|
if res_idx >= 0 and res_idx < RESOLUTIONS.size():
|
|
var target_res = RESOLUTIONS[res_idx]
|
|
DisplayServer.window_set_size(target_res)
|
|
# Center window after resize
|
|
var screen = DisplayServer.window_get_current_screen()
|
|
var screen_rect = DisplayServer.screen_get_usable_rect(screen)
|
|
var window_size = DisplayServer.window_get_size()
|
|
DisplayServer.window_set_position(screen_rect.position + (screen_rect.size - window_size) / 2)
|
|
|
|
if video.vsync:
|
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED)
|
|
else:
|
|
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
|
|
|
# Apply MSAA
|
|
var viewport = get_viewport()
|
|
match video.msaa:
|
|
0: viewport.msaa_3d = Viewport.MSAA_DISABLED
|
|
1: viewport.msaa_3d = Viewport.MSAA_2X
|
|
2: viewport.msaa_3d = Viewport.MSAA_4X
|
|
3: viewport.msaa_3d = Viewport.MSAA_8X
|
|
|
|
# Apply FPS Cap
|
|
match video.fps_cap:
|
|
0: Engine.max_fps = 0
|
|
1: Engine.max_fps = 30
|
|
2: Engine.max_fps = 60
|
|
3: Engine.max_fps = 120
|
|
4: Engine.max_fps = 144
|
|
|
|
# Apply Shadow Quality (Simplified for Forward+)
|
|
match video.shadow_quality:
|
|
0: # Low
|
|
RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_LOW)
|
|
RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_LOW)
|
|
1: # Medium
|
|
RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_MEDIUM)
|
|
RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_MEDIUM)
|
|
2: # High
|
|
RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_HIGH)
|
|
RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_HIGH)
|
|
3: # Ultra
|
|
RenderingServer.directional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_ULTRA)
|
|
RenderingServer.positional_soft_shadow_filter_set_quality(RenderingServer.SHADOW_QUALITY_SOFT_ULTRA)
|
|
|
|
func apply_audio_settings():
|
|
var audio = settings.audio
|
|
set_bus_volume("Master", audio.master_volume)
|
|
set_bus_volume("Music", audio.music_volume)
|
|
set_bus_volume("SFX", audio.sfx_volume)
|
|
|
|
func set_bus_volume(bus_name: String, volume_linear: float):
|
|
var bus_idx = AudioServer.get_bus_index(bus_name)
|
|
if bus_idx != -1:
|
|
AudioServer.set_bus_volume_db(bus_idx, linear_to_db(volume_linear))
|
|
AudioServer.set_bus_mute(bus_idx, volume_linear <= 0.001)
|
|
|
|
func apply_control_settings():
|
|
# Sync custom settings with InputMap
|
|
var mapping = {
|
|
"move_up": "move_north",
|
|
"move_down": "move_south",
|
|
"move_left": "move_west",
|
|
"move_right": "move_east",
|
|
"grab": "action_grab",
|
|
"put": "action_put",
|
|
"use_powerup": "use_powerup",
|
|
"tekton_grab": "action_grab_tekton",
|
|
"attack_mode": "action_knock_tekton"
|
|
}
|
|
|
|
for setting_key in mapping.keys():
|
|
var action_name = mapping[setting_key]
|
|
if not InputMap.has_action(action_name):
|
|
InputMap.add_action(action_name)
|
|
|
|
InputMap.action_erase_events(action_name)
|
|
|
|
# Add Primary
|
|
var primary_key = settings.controls.get(setting_key)
|
|
if primary_key:
|
|
var event = InputEventKey.new()
|
|
event.keycode = primary_key
|
|
InputMap.action_add_event(action_name, event)
|
|
|
|
# Add Secondary
|
|
var secondary_key = settings.controls.get(setting_key + "_alt")
|
|
if secondary_key:
|
|
var event = InputEventKey.new()
|
|
event.keycode = secondary_key
|
|
InputMap.action_add_event(action_name, event)
|
|
|
|
# Add Joypad defaults for movement
|
|
if action_name.begins_with("move_"):
|
|
var joy_axis = -1
|
|
var axis_val = 0.0
|
|
match action_name:
|
|
"move_north": joy_axis = JOY_AXIS_LEFT_Y; axis_val = -1.0
|
|
"move_south": joy_axis = JOY_AXIS_LEFT_Y; axis_val = 1.0
|
|
"move_west": joy_axis = JOY_AXIS_LEFT_X; axis_val = -1.0
|
|
"move_east": joy_axis = JOY_AXIS_LEFT_X; axis_val = 1.0
|
|
|
|
if joy_axis != -1:
|
|
var joy_event = InputEventJoypadMotion.new()
|
|
joy_event.axis = joy_axis
|
|
joy_event.axis_value = axis_val
|
|
InputMap.action_add_event(action_name, joy_event)
|
|
|
|
func set_control(action_name: String, keycode: int):
|
|
if settings.controls.has(action_name):
|
|
settings.controls[action_name] = keycode
|
|
apply_control_settings() # Apply immediately
|
|
emit_signal("control_remapped", action_name, keycode)
|
|
save_settings()
|
|
|
|
func get_control_keycode(action_name: String) -> int:
|
|
return settings.controls.get(action_name, -1)
|
|
|
|
func get_control_text(action_name: String) -> String:
|
|
var code = get_control_keycode(action_name)
|
|
if code == -1: return "Unbound"
|
|
return OS.get_keycode_string(code)
|