diff --git a/Game/assets/fonts/OFL-Orbitron.txt b/Game/assets/fonts/OFL-Orbitron.txt deleted file mode 100644 index 91ede459..00000000 --- a/Game/assets/fonts/OFL-Orbitron.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2018 The Orbitron Project Authors (https://github.com/theleagueof/orbitron), with Reserved Font Name: "Orbitron" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Game/assets/fonts/Orbitron-Medium.ttf b/Game/assets/fonts/Orbitron-Medium.ttf deleted file mode 100644 index 91287a30..00000000 Binary files a/Game/assets/fonts/Orbitron-Medium.ttf and /dev/null differ diff --git a/Game/assets/fonts/Orbitron-Medium.ttf.import b/Game/assets/fonts/Orbitron-Medium.ttf.import deleted file mode 100644 index 7095b685..00000000 --- a/Game/assets/fonts/Orbitron-Medium.ttf.import +++ /dev/null @@ -1,36 +0,0 @@ -[remap] - -importer="font_data_dynamic" -type="FontFile" -uid="uid://bhbpb712ouagv" -path="res://.godot/imported/Orbitron-Medium.ttf-0d3cd88166f12a3096066c2eef570744.fontdata" - -[deps] - -source_file="res://assets/fonts/Orbitron-Medium.ttf" -dest_files=["res://.godot/imported/Orbitron-Medium.ttf-0d3cd88166f12a3096066c2eef570744.fontdata"] - -[params] - -Rendering=null -antialiasing=1 -generate_mipmaps=false -disable_embedded_bitmaps=true -multichannel_signed_distance_field=false -msdf_pixel_range=8 -msdf_size=48 -allow_system_fallback=true -force_autohinter=false -modulate_color_glyphs=false -hinting=3 -subpixel_positioning=4 -keep_rounding_remainders=true -oversampling=0.0 -Fallbacks=null -fallbacks=[] -Compress=null -compress=true -preload=[] -language_support={} -script_support={} -opentype_features={} diff --git a/Game/scenes/HUD.tscn b/Game/scenes/HUD.tscn index 70104c34..8624f321 100644 --- a/Game/scenes/HUD.tscn +++ b/Game/scenes/HUD.tscn @@ -1,10 +1,9 @@ -[gd_scene load_steps=10 format=3 uid="uid://c8kak2l3m4n5"] +[gd_scene load_steps=9 format=3 uid="uid://c8kak2l3m4n5"] [ext_resource type="Script" uid="uid://du7y176h5aaq4" path="res://scripts/HUDController.gd" id="1_hud_controller"] [ext_resource type="Script" path="res://scripts/hud_attitude_indicator.gd" id="2_adi"] [ext_resource type="Script" path="res://scripts/hud_heading_tape.gd" id="3_tape"] [ext_resource type="Script" path="res://scripts/hud_gauge.gd" id="4_gauge"] -[ext_resource type="Theme" path="res://themes/hud_theme.tres" id="5_theme"] [sub_resource type="LabelSettings" id="LabelSettings_hud"] font_size = 32 @@ -51,7 +50,6 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 mouse_filter = 2 -theme = ExtResource("5_theme") [node name="ScoreboardPanel" type="PanelContainer" parent="Control"] layout_mode = 1 diff --git a/Game/scenes/ship_camera_rig.tscn b/Game/scenes/ship_camera_rig.tscn index 4f4d401d..44cb7c07 100644 --- a/Game/scenes/ship_camera_rig.tscn +++ b/Game/scenes/ship_camera_rig.tscn @@ -1,17 +1,8 @@ -[gd_scene load_steps=5 format=3] +[gd_scene load_steps=4 format=3] [ext_resource type="Script" path="res://scripts/ship_camera.gd" id="1_rig"] [ext_resource type="Shader" path="res://shaders/post_process.gdshader" id="2_post"] -[sub_resource type="CameraAttributesPractical" id="CameraAttributes_gameplay"] -dof_blur_far_enabled = true -dof_blur_far_distance = 24.0 -dof_blur_far_transition = 14.0 -dof_blur_near_enabled = true -dof_blur_near_distance = 1.25 -dof_blur_near_transition = 0.75 -dof_blur_amount = 0.08 - [sub_resource type="ShaderMaterial" id="ShaderMaterial_post"] shader = ExtResource("2_post") @@ -20,7 +11,6 @@ script = ExtResource("1_rig") [node name="Camera3D" type="Camera3D" parent="."] current = true -attributes = SubResource("CameraAttributes_gameplay") [node name="PostProcess" type="CanvasLayer" parent="."] layer = 0 diff --git a/Game/scripts/hud_attitude_indicator.gd b/Game/scripts/hud_attitude_indicator.gd index 66a40b3e..b19dfba3 100644 --- a/Game/scripts/hud_attitude_indicator.gd +++ b/Game/scripts/hud_attitude_indicator.gd @@ -73,7 +73,7 @@ func _draw() -> void: draw_line(Vector2(-half_chord, horizon_y), Vector2(half_chord, horizon_y), LINE_COLOR, 2.0) # Pitch ladder: rungs every 10 degrees, labelled, scrolling with pitch. - var font := hud_font() + var font := ThemeDB.fallback_font for ladder_deg: int in [-30, -20, -10, 10, 20, 30]: var y := horizon_y - ladder_deg * px_per_deg if absf(y) > radius - 12.0: diff --git a/Game/scripts/hud_gauge.gd b/Game/scripts/hud_gauge.gd index c0e1811c..55cc9290 100644 --- a/Game/scripts/hud_gauge.gd +++ b/Game/scripts/hud_gauge.gd @@ -37,7 +37,7 @@ func _process(delta: float) -> void: func _draw() -> void: - var font := hud_font() + var font := ThemeDB.fallback_font var fraction := clampf(_value / max_value, 0.0, 1.0) var reading := value_format % _value diff --git a/Game/scripts/hud_heading_tape.gd b/Game/scripts/hud_heading_tape.gd index 61178739..858b6151 100644 --- a/Game/scripts/hud_heading_tape.gd +++ b/Game/scripts/hud_heading_tape.gd @@ -39,7 +39,7 @@ func _process(delta: float) -> void: func _draw() -> void: - var font := hud_font() + var font := ThemeDB.fallback_font var center_x := size.x / 2.0 var px_per_deg := size.x / VISIBLE_RANGE diff --git a/Game/scripts/hud_instrument.gd b/Game/scripts/hud_instrument.gd index 1b572b4a..4817c976 100644 --- a/Game/scripts/hud_instrument.gd +++ b/Game/scripts/hud_instrument.gd @@ -23,11 +23,3 @@ static func angle_delta_deg(from: float, to: float) -> float: func _smoothing_weight(delta: float) -> float: return 1.0 - exp(-SMOOTHING * delta) # frame-rate independent - - -# Procedural draw calls do not automatically consume a Control's theme font; -# resolve it explicitly so every instrument uses the same packaged typeface -# as ordinary Labels instead of ThemeDB's debug-looking fallback. -func hud_font() -> Font: - var font := get_theme_default_font() - return font if font else ThemeDB.fallback_font diff --git a/Game/scripts/ship.gd b/Game/scripts/ship.gd index cb6a226d..098d7f31 100644 --- a/Game/scripts/ship.gd +++ b/Game/scripts/ship.gd @@ -117,8 +117,7 @@ const ATTITUDE_THRESHOLD = 1.0 # degrees const THRUST_THRESHOLD = 1.0 # percent var _engine_cores: Array[MeshInstance3D] = [] -var _engine_plumes: Array[GPUParticles3D] = [] -var _turbo_flames: Array[GPUParticles3D] = [] +var _engine_flames: Array[MeshInstance3D] = [] var _engine_lights: Array[OmniLight3D] = [] var _impact_sparks: GPUParticles3D @@ -215,7 +214,7 @@ func _build_movement_vfx() -> void: for x in [-0.42, 0.42]: var engine_pos := Vector3(x, -0.05, 1.42) - var core_mat := _vfx_material(Color(0.35, 0.85, 1.0, 1.0), 1.0) + var core_mat := _vfx_material(Color(1.0, 0.48, 0.1, 1.0), 1.0) var core_mesh := SphereMesh.new() core_mesh.radius = 0.11 core_mesh.height = 0.22 @@ -228,28 +227,37 @@ func _build_movement_vfx() -> void: add_child(core) _engine_cores.append(core) - var plume := _make_particles( - "EnginePlumeL" if x < 0.0 else "EnginePlumeR", - Color(0.3, 0.82, 1.0, 0.72), Vector2(0.16, 0.42), 36, 0.24, - Vector3(0, 0, 1), 12.0, 2.0, 5.0 - ) - plume.position = engine_pos + Vector3(0, 0, 0.08) - add_child(plume) - _engine_plumes.append(plume) - - var turbo := _make_particles( - "TurboFlameL" if x < 0.0 else "TurboFlameR", - Color(0.78, 0.34, 1.0, 0.9), Vector2(0.22, 0.72), 48, 0.18, - Vector3(0, 0, 1), 7.0, 6.0, 10.0 - ) - turbo.position = engine_pos + Vector3(0, 0, 0.12) - add_child(turbo) - _turbo_flames.append(turbo) + # A single conventional orange flame replaces the layered particle plume + # and separate purple turbo effect. Turbo only lengthens and brightens the + # same flame, keeping the engine silhouette simple and readable. + var flame_mat := StandardMaterial3D.new() + flame_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + flame_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + flame_mat.cull_mode = BaseMaterial3D.CULL_DISABLED + flame_mat.albedo_color = Color(1.0, 0.34, 0.04, 0.92) + flame_mat.emission_enabled = true + flame_mat.emission = Color(1.0, 0.16, 0.015) + flame_mat.emission_energy_multiplier = 2.0 + var flame_mesh := CylinderMesh.new() + flame_mesh.top_radius = 0.015 + flame_mesh.bottom_radius = 0.14 + flame_mesh.height = 1.0 + flame_mesh.radial_segments = 12 + flame_mesh.material = flame_mat + var flame := MeshInstance3D.new() + flame.name = "EngineFlameL" if x < 0.0 else "EngineFlameR" + flame.position = engine_pos + Vector3(0, 0, 0.3) + flame.rotation_degrees.x = 90.0 + flame.mesh = flame_mesh + flame.visible = false + flame.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF + add_child(flame) + _engine_flames.append(flame) var light := OmniLight3D.new() light.name = "EngineLightL" if x < 0.0 else "EngineLightR" light.position = engine_pos - light.light_color = Color(0.3, 0.75, 1.0) + light.light_color = Color(1.0, 0.32, 0.08) light.omni_range = 3.5 light.omni_attenuation = 2.0 light.shadow_enabled = false @@ -308,18 +316,24 @@ func _make_particles( func _update_movement_vfx() -> void: - if _engine_plumes.is_empty(): + if _engine_flames.is_empty(): return # These are the two rear main engines, so lateral/vertical maneuvering jets # must not make them flare. Reverse thrust also comes from separate attitude - # jets conceptually; only positive Z drives this rear-facing plume. + # jets conceptually; only positive Z drives this rear-facing flame. var thrust := clampf(maxf(_current_action.thrust.z, 0.0), 0.0, 1.0) var turbo := _current_action.turbo and thrust > 0.05 - for i in _engine_plumes.size(): - _engine_plumes[i].emitting = thrust > 0.02 - _engine_plumes[i].amount_ratio = 0.25 + thrust * 0.75 - _engine_plumes[i].speed_scale = 0.65 + thrust * 0.75 - _turbo_flames[i].emitting = turbo + for i in _engine_flames.size(): + var flame := _engine_flames[i] + var flame_length := 0.28 + thrust * 0.82 + (0.62 if turbo else 0.0) + var flame_width := 0.72 + thrust * 0.32 + (0.12 if turbo else 0.0) + flame.visible = thrust > 0.02 + flame.scale = Vector3(flame_width, flame_length, flame_width) + # CylinderMesh is centred on local Y (rotated to ship +Z), so moving its + # centre by half the length keeps the flame root fixed at the engine bell. + flame.position.z = 1.48 + flame_length * 0.5 + var flame_mat := flame.mesh.material as StandardMaterial3D + flame_mat.emission_energy_multiplier = 1.8 + thrust * 2.2 + (1.8 if turbo else 0.0) _engine_lights[i].light_energy = 0.35 + thrust * 2.1 + (2.3 if turbo else 0.0) var core_mat := _engine_cores[i].mesh.material as StandardMaterial3D core_mat.emission_energy_multiplier = 0.65 + thrust * 2.0 + (2.0 if turbo else 0.0) diff --git a/Game/scripts/ship_camera.gd b/Game/scripts/ship_camera.gd index 09495f15..0d63c75a 100644 --- a/Game/scripts/ship_camera.gd +++ b/Game/scripts/ship_camera.gd @@ -183,9 +183,6 @@ func _update_speed_feel(delta: float) -> void: _effective_distance = lerpf( _effective_distance, camera_distance + _turbo_blend * turbo_distance_kick, feel_t ) - post_material.set_shader_parameter( - "motion_blur_amount", _speed_blend * _speed_blend * 0.006 + _turbo_blend * 0.006 - ) post_material.set_shader_parameter("chromatic_aberration", _turbo_blend * 0.007) post_material.set_shader_parameter("vignette_strength", 0.22 + _turbo_blend * 0.16) @@ -233,8 +230,7 @@ func begin_goal_cut(goal_position: Vector3) -> void: camera.fov = 58.0 camera.look_at(_goal_cut_look_at, Vector3.UP) # A hard, stationary cinematic camera must not inherit the scoring frame's - # ship-speed smear or turbo chroma. Keep only a deliberate cinematic edge. - post_material.set_shader_parameter("motion_blur_amount", 0.0) + # turbo chroma; keep only a deliberate cinematic edge. post_material.set_shader_parameter("chromatic_aberration", 0.0) post_material.set_shader_parameter("vignette_strength", 0.3) @@ -243,6 +239,5 @@ func end_goal_cut() -> void: _goal_cut_active = false _speed_blend = 0.0 _turbo_blend = 0.0 - post_material.set_shader_parameter("motion_blur_amount", 0.0) post_material.set_shader_parameter("chromatic_aberration", 0.0) post_material.set_shader_parameter("vignette_strength", 0.22) diff --git a/Game/shaders/post_process.gdshader b/Game/shaders/post_process.gdshader index 8d19ea6c..844dba4c 100644 --- a/Game/shaders/post_process.gdshader +++ b/Game/shaders/post_process.gdshader @@ -1,37 +1,21 @@ shader_type canvas_item; render_mode unshaded; -uniform sampler2D screen_texture : hint_screen_texture, filter_linear_mipmap; -uniform float motion_blur_amount : hint_range(0.0, 0.03) = 0.0; +uniform sampler2D screen_texture : hint_screen_texture, filter_linear; uniform float chromatic_aberration : hint_range(0.0, 0.02) = 0.0; uniform float vignette_strength : hint_range(0.0, 1.0) = 0.22; -vec4 motion_sample(vec2 uv) { - // A restrained centre-directed velocity smear. The camera script scales it - // with ship speed/turbo; mipmapped screen reads soften the outer samples. - if (motion_blur_amount < 0.0001) { - return textureLod(screen_texture, uv, 0.0); - } - vec2 step_uv = (uv - vec2(0.5)) * motion_blur_amount; - vec4 color = textureLod(screen_texture, uv, 0.0) * 0.36; - color += textureLod(screen_texture, uv - step_uv * 0.35, 0.5) * 0.25; - color += textureLod(screen_texture, uv - step_uv * 0.7, 1.0) * 0.21; - color += textureLod(screen_texture, uv - step_uv, 1.5) * 0.18; - return color; -} - - void fragment() { vec2 radial = UV - vec2(0.5); vec2 chroma = radial * chromatic_aberration; - vec4 center = motion_sample(UV); + vec4 center = texture(screen_texture, UV); vec3 color = center.rgb; // The branch is driven by one uniform for the whole draw, so non-turbo - // frames avoid eight redundant screen taps without causing warp divergence. + // frames avoid two redundant screen taps without causing warp divergence. if (chromatic_aberration > 0.0001) { - color.r = motion_sample(UV + chroma).r; - color.b = motion_sample(UV - chroma).b; + color.r = texture(screen_texture, UV + chroma).r; + color.b = texture(screen_texture, UV - chroma).b; } // Aspect-correct vignette, always subtle and tightened slightly on turbo. diff --git a/Game/themes/hud_theme.tres b/Game/themes/hud_theme.tres deleted file mode 100644 index 0c4f0e4d..00000000 --- a/Game/themes/hud_theme.tres +++ /dev/null @@ -1,29 +0,0 @@ -[gd_resource type="Theme" load_steps=3 format=3] - -[ext_resource type="FontFile" path="res://assets/fonts/Orbitron-Medium.ttf" id="1_font"] - -[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"] -bg_color = Color(0.035, 0.05, 0.085, 0.76) -border_width_left = 1 -border_width_top = 1 -border_width_right = 1 -border_width_bottom = 1 -border_color = Color(0.3, 0.7, 1, 0.38) -corner_radius_top_left = 8 -corner_radius_top_right = 8 -corner_radius_bottom_right = 8 -corner_radius_bottom_left = 8 -content_margin_left = 12.0 -content_margin_top = 8.0 -content_margin_right = 12.0 -content_margin_bottom = 8.0 - -[resource] -default_font = ExtResource("1_font") -default_font_size = 14 -Label/colors/font_color = Color(0.86, 0.94, 1, 1) -Label/colors/font_shadow_color = Color(0, 0, 0, 0.72) -Label/constants/outline_size = 1 -Label/constants/shadow_offset_x = 2 -Label/constants/shadow_offset_y = 2 -PanelContainer/styles/panel = SubResource("StyleBoxFlat_panel") diff --git a/TODO.md b/TODO.md index 46a1dc21..e8520dbe 100644 --- a/TODO.md +++ b/TODO.md @@ -16,14 +16,14 @@ The largest gap between this and a AAA-feeling product is presentation, not code - [ ] **Audio — there is none.** Zero sound files, zero `AudioStreamPlayer` nodes, no bus layout. Needs: engine hum pitched to throttle, turbo whoosh, ball impacts scaled by collision impulse, wall scrapes, goal explosion, crowd bed, UI clicks, countdown beeps, music. Can be driven off `Ship`'s existing telemetry signals. - [x] SSAO/SSIL in the arena Environments — cheapest single perceived-quality win available; grounds the ships against the deck and gives the fillets and goal recesses real depth. -- [x] VFX on anything that moves: throttle-reactive engine cores and plumes, distinct turbo flames, a speed-scaled ball trail, contact sparks and team-tinted goal bursts. +- [x] VFX on anything that moves: throttle-reactive engine cores and simple rear flames, a speed-scaled ball trail, contact sparks and team-tinted goal bursts. - [x] Impact feedback — intensity-scaled screen shake, unscaled hit-stop, HUD flash and controller rumble on ball contact. - [x] Camera feel in `ship_camera.gd` — speed-based FOV widening, turbo kick-back and decaying impact shake. - [x] Goal celebration sequence: goal burst, team-tinted flash/title card, dedicated camera cut and slow-motion beat after authoritative score registration and before reset/kickoff. - [x] Local lighting / dynamic GI — four local pitch lights plus SDFGI provide bounce and contact lighting for the runtime-generated arena shell; the shader's `hull_fill` stand-in is retired. (The shell is generated in `_ready`, so it cannot participate in an editor LightmapGI bake.) - [x] Dress `arena_03` — its asteroid field now has mining stations, debris clusters and a distant planet. -- [x] Custom font + a real `Theme` resource for the HUD. Packaged Orbitron typography, shared label styling and themed procedural-instrument text replace `ThemeDB.fallback_font`. -- [x] Post-processing beyond glow: camera DoF plus speed-scaled motion blur, a cinematic vignette and turbo-driven chromatic aberration. +- [ ] Custom font + a real `Theme` resource for the HUD. `ThemeDB.fallback_font` at 10-13 px reads as a debug overlay. +- [x] Post-processing beyond glow: a cinematic vignette and turbo-driven chromatic aberration. DoF and motion blur were intentionally omitted after playtesting. ## Multiplayer (long term)