Phase 7: Windows client export, preset fix, code fixes

- Fix: export_presets.cfg — platform='Windows Desktop' (not 'Windows'), Windows as [preset.0]
- Fix: plugin_manager.gd — removed class_name (autoload conflict), fixed multiline % formatting
- Fix: Disabled GDExtension temporarily for clean export
- Add: Windows PE32+ x86_64 client binary (109MB) at build/tactical-shooter-windows-x86_64/
- Add: tactical-shooter-windows.zip (portable zip package)
- Build: Linux server binary (78MB) rebuilt
This commit is contained in:
2026-07-01 19:46:52 -04:00
parent e385eae0f5
commit 9ea98aa7b8
36 changed files with 1094 additions and 64 deletions
@@ -0,0 +1 @@
uid://bjtf5jiedmwx8
@@ -92,6 +92,9 @@ var _input_dict: Dictionary = {}
var _mouse_captured: bool = false
var _mouse_clicked_this_frame: bool = false
## Weapon manager reference.
var _weapon: WeaponManager = null
# ---------------------------------------------------------------------------
# Node references (set in _ready)
# ---------------------------------------------------------------------------
@@ -152,6 +155,12 @@ func _ready() -> void:
_crouch_current = 0.0
_update_crouch(0.0)
# Find weapon manager child
for child in get_children():
if child is WeaponManager:
_weapon = child
break
func _input(event: InputEvent) -> void:
# Mouse look
@@ -227,6 +236,19 @@ func _physics_process(delta: float) -> void:
if _camera:
_camera.rotation.x = _pitch
# 5b. Weapon fire with rate limiting via WeaponManager
var should_fire: bool = false
var time_since_engine_start: float = Engine.get_process_ticks() * get_physics_process_delta_time()
if _weapon:
should_fire = _weapon.try_fire(time_since_engine_start)
elif shoot_pressed:
# No weapon manager — fire every tick (unlimited)
should_fire = true
if _server != null:
if should_fire:
_server.fire_weapon(entity_id)
# 6. Send input to simulation server
if _server != null and entity_id >= 0:
# Build input for SimulationServer (mutate cached dict to avoid alloc)
@@ -236,14 +258,18 @@ func _physics_process(delta: float) -> void:
_input_dict["jump"] = jump_pressed
_input_dict["crouch"] = _crouch_active
_input_dict["sprint"] = _sprint_active
_input_dict["shoot"] = shoot_pressed
_input_dict["shoot"] = should_fire
_input_dict["aim"] = aim_pressed
_input_dict["input_sequence"] = _input_sequence
_server.apply_input(entity_id, _input_dict)
if shoot_pressed:
_server.fire_weapon(entity_id)
# Check for hit feedback from last tick
if Engine.has_singleton("SimulationServer") or _server:
var hit_result: Dictionary = _server.get_last_hit_result()
if hit_result.get("hit", false) and _weapon:
var hit_pos := Vector3.ZERO # approximate — we don't have exact world hit position from server yet
_weapon.on_hit_confirmed(hit_pos, hit_result.get("damage", 0.0), hit_result.get("killed", false))
_input_sequence += 1
@@ -399,3 +425,15 @@ func reset_pose() -> void:
_crouch_current = 0.0
_crouch_target = 0.0
_update_crouch(0.0)
## Get the look direction as a normalized Vector3 in world space.
## Uses the current yaw/pitch to compute a forward vector.
func get_look_direction() -> Vector3:
var yaw_rad: float = _yaw
var pitch_rad: float = _pitch
return Vector3(
cos(pitch_rad) * sin(yaw_rad),
-sin(pitch_rad),
cos(pitch_rad) * cos(yaw_rad)
).normalized()
@@ -0,0 +1 @@
uid://cpbvk1itjonm