Fresh start: replace with naxIO/netfox-cs-sample foundation
Complete replacement of the tactical-shooter project with the netfox-cs-sample (MIT) — a CS 1.6 inspired multiplayer FPS built with Godot 4 and netfox. ## What's new - Full CS-style gameplay: teams (T/CT), rounds, economy, buy menu - 6 weapons: Knife, Glock, USP, AK-47, M4A1, AWP - Bomb plant/defuse with 2 bombsites - Flashbang & smoke grenades - Proper netfox rollback netcode at 64 tick - Network popup UI for host/join - HUD, crosshair, round timer, scoreboard - All netfox singletons registered as autoloads (works in exported builds) ## Architecture - Listen-server (host from client, no dedicated server binary) - Multiplayer-fps game lives at examples/multiplayer-fps/ - Netfox addons registered as autoloads for exported build compat - Godot 4.7 with Forward+ renderer ## Removed - Old headless-server architecture (client_main, server_main, player.gd, etc.) - Custom netfox bootstrap with ENet fallback - Old ChaffGames FPS template (2,420 lines, 844 KB) - SimulationServer GDExtension stub - Godot-jolt physics (netfox sample uses default Godot physics) - Duplicate weapon_data.gd, anti_cheat.gd, round_manager.gd, etc. - Server browser API Python venv (87 MB) - test_range map and modular assets ## Preserved - Git history - Server config at config/default_server_config.cfg - Windows export preset - Build directory (gitignored) Co-authored-by: naxIO <naxIO@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
extends Node3D
|
||||
class_name Grenade
|
||||
|
||||
enum GrenadeType { FLASH, SMOKE }
|
||||
|
||||
@export var grenade_type: GrenadeType = GrenadeType.FLASH
|
||||
@export var fuse_time: float = 1.5
|
||||
@export var throw_force: float = 20.0
|
||||
@export var gravity: float = 15.0
|
||||
@export var bounce_factor: float = 0.4
|
||||
|
||||
var velocity: Vector3 = Vector3.ZERO
|
||||
var _fuse_timer: float = 0.0
|
||||
var _detonated := false
|
||||
var _owner_id: int = -1
|
||||
|
||||
func setup(origin: Vector3, direction: Vector3, owner_id: int, type: GrenadeType):
|
||||
global_position = origin
|
||||
grenade_type = type
|
||||
_owner_id = owner_id
|
||||
velocity = direction * throw_force + Vector3.UP * 5.0
|
||||
|
||||
if type == GrenadeType.FLASH:
|
||||
fuse_time = 1.5
|
||||
else:
|
||||
fuse_time = 2.0
|
||||
|
||||
func _physics_process(delta: float):
|
||||
if _detonated:
|
||||
return
|
||||
|
||||
# Simple physics
|
||||
velocity.y -= gravity * delta
|
||||
var new_pos := global_position + velocity * delta
|
||||
|
||||
# Basic bounce off floor
|
||||
if new_pos.y < 0.1:
|
||||
new_pos.y = 0.1
|
||||
velocity.y = -velocity.y * bounce_factor
|
||||
velocity.x *= bounce_factor
|
||||
velocity.z *= bounce_factor
|
||||
|
||||
global_position = new_pos
|
||||
|
||||
# Fuse countdown
|
||||
_fuse_timer += delta
|
||||
if _fuse_timer >= fuse_time:
|
||||
_detonate()
|
||||
|
||||
func _detonate():
|
||||
if _detonated:
|
||||
return
|
||||
_detonated = true
|
||||
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
|
||||
match grenade_type:
|
||||
GrenadeType.FLASH:
|
||||
_flash_effect()
|
||||
GrenadeType.SMOKE:
|
||||
_smoke_effect()
|
||||
|
||||
func _flash_effect():
|
||||
var space := get_world_3d().direct_space_state
|
||||
var flash_pos := global_position
|
||||
|
||||
# Check all players for flash
|
||||
var spawner := get_tree().current_scene.get_node_or_null("Network/Player Spawner")
|
||||
if spawner == null:
|
||||
queue_free()
|
||||
return
|
||||
|
||||
for peer_id in spawner.avatars:
|
||||
var avatar = spawner.avatars[peer_id] as CharacterBody3D
|
||||
if avatar == null or avatar.is_dead:
|
||||
continue
|
||||
|
||||
var head_pos: Vector3 = avatar.global_position + Vector3.UP * 0.7
|
||||
var distance := flash_pos.distance_to(head_pos)
|
||||
if distance > 30.0:
|
||||
continue
|
||||
|
||||
# LOS check
|
||||
var query := PhysicsRayQueryParameters3D.create(flash_pos, head_pos)
|
||||
query.exclude = [avatar.get_rid()]
|
||||
var result := space.intersect_ray(query)
|
||||
|
||||
if result.is_empty():
|
||||
# Direct line of sight — calculate flash duration
|
||||
var duration := _calculate_flash_duration(distance, avatar, head_pos, flash_pos)
|
||||
if duration > 0.0:
|
||||
_apply_flash.rpc_id(peer_id, duration)
|
||||
|
||||
# Cleanup
|
||||
call_deferred("queue_free")
|
||||
|
||||
func _calculate_flash_duration(distance: float, avatar: CharacterBody3D, head_pos: Vector3, flash_pos: Vector3) -> float:
|
||||
# Base duration: closer = longer flash
|
||||
var base := lerpf(3.5, 0.5, clampf(distance / 25.0, 0.0, 1.0))
|
||||
|
||||
# Facing check: looking at flash = full duration, away = reduced
|
||||
var head := avatar.get_node_or_null("Head") as Node3D
|
||||
if head:
|
||||
var to_flash := (flash_pos - head_pos).normalized()
|
||||
var facing := -head.global_transform.basis.z
|
||||
var dot := facing.dot(to_flash)
|
||||
if dot < 0.0:
|
||||
base *= 0.3 # Looking away
|
||||
else:
|
||||
base *= lerpf(0.5, 1.0, dot)
|
||||
|
||||
return base
|
||||
|
||||
func _smoke_effect():
|
||||
# Spawn smoke zone at detonation position
|
||||
_spawn_smoke.rpc(global_position)
|
||||
# Smoke grenade stays alive for smoke duration, then frees itself
|
||||
set_physics_process(false)
|
||||
var smoke_duration := 18.0
|
||||
get_tree().create_timer(smoke_duration).timeout.connect(queue_free)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _apply_flash(duration: float) -> void:
|
||||
# Client-side: white screen effect
|
||||
var canvas := CanvasLayer.new()
|
||||
canvas.layer = 100
|
||||
var rect := ColorRect.new()
|
||||
rect.color = Color(1, 1, 1, 1)
|
||||
rect.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
canvas.add_child(rect)
|
||||
get_tree().current_scene.add_child(canvas)
|
||||
|
||||
# Fade out
|
||||
var tween := get_tree().create_tween()
|
||||
tween.tween_property(rect, "color:a", 0.0, duration)
|
||||
tween.tween_callback(canvas.queue_free)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _spawn_smoke(pos: Vector3) -> void:
|
||||
# Client-side: visual smoke particles
|
||||
var smoke_node := Node3D.new()
|
||||
smoke_node.name = "SmokeCloud"
|
||||
smoke_node.global_position = pos
|
||||
get_tree().current_scene.add_child(smoke_node)
|
||||
|
||||
# Simple visual: grey sphere mesh as placeholder
|
||||
var mesh_inst := MeshInstance3D.new()
|
||||
var sphere := SphereMesh.new()
|
||||
sphere.radius = 3.0
|
||||
sphere.height = 4.0
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.albedo_color = Color(0.6, 0.6, 0.6, 0.5)
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
sphere.material = mat
|
||||
mesh_inst.mesh = sphere
|
||||
smoke_node.add_child(mesh_inst)
|
||||
|
||||
get_tree().create_timer(18.0).timeout.connect(smoke_node.queue_free)
|
||||
Reference in New Issue
Block a user