commit 956a15a5255fd4f43333f86306eab5dc48e87096 Author: enne2 Date: Sat Oct 10 23:11:13 2026 +0200 Add industrial metal soundtrack with seamless loop - assets/audio/music/zombie_industrial_loop.ogg: 57.60 s Ogg Vorbis loop generated locally with YuE2-3B on the Tesla V100 (score written for this track; vocals removed with HTDemucs). The loop is spliced at the track's structural repeat with a 0.6 s crossfade: measured seam dip <= 1.6 dB. - scripts/main.gd: "Music" AudioStreamPlayer at -11 dB, started at boot on desktop and on the start click on Web (browser autoplay policy). It forces stream.loop at runtime because the Web staging step re-imports the OGG with loop=false. - tools/test_music.gd: soundtrack test (loop, length, volume, playback, still playing after player death). - docs/MUSIC.md: source, loop construction, verification, licence. - docs/WEB-DEPLOY.md: release ab6054111f5919a8 notes. Initial commit of the project. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c02ab2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Godot 4 — cache di import e metadata dell'editor +.godot/ + +# Artefatti di build ed export Web (rigenerabili con docs/WEB-DEPLOY.md) +build/ + +# Sistema +.DS_Store +*~ diff --git a/addons/goldsrc/bin/libgoldsrc.linux.template_debug.x86_64.so b/addons/goldsrc/bin/libgoldsrc.linux.template_debug.x86_64.so new file mode 100755 index 0000000..a8bc4fb Binary files /dev/null and b/addons/goldsrc/bin/libgoldsrc.linux.template_debug.x86_64.so differ diff --git a/addons/goldsrc/bin/libgoldsrc.linux.template_release.x86_64.so b/addons/goldsrc/bin/libgoldsrc.linux.template_release.x86_64.so new file mode 100755 index 0000000..e48ec35 Binary files /dev/null and b/addons/goldsrc/bin/libgoldsrc.linux.template_release.x86_64.so differ diff --git a/addons/goldsrc/goldsrc.gdextension b/addons/goldsrc/goldsrc.gdextension new file mode 100644 index 0000000..eb227ce --- /dev/null +++ b/addons/goldsrc/goldsrc.gdextension @@ -0,0 +1,22 @@ +[configuration] +entry_symbol = "goldsrc_library_init" +compatibility_minimum = "4.6" +reloadable = true + +[libraries] + +macos.debug.arm64 = "res://addons/goldsrc/bin/libgoldsrc.macos.template_debug.arm64.dylib" +macos.release.arm64 = "res://addons/goldsrc/bin/libgoldsrc.macos.template_release.arm64.dylib" +macos.debug.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.macos.template_debug.x86_64.dylib" +macos.release.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.macos.template_release.x86_64.dylib" + +windows.debug.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.windows.template_debug.x86_64.dll" +windows.release.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.windows.template_release.x86_64.dll" + +linux.debug.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.linux.template_debug.x86_64.so" +linux.release.x86_64 = "res://addons/goldsrc/bin/libgoldsrc.linux.template_release.x86_64.so" +linux.debug.arm64 = "res://addons/goldsrc/bin/libgoldsrc.linux.template_debug.arm64.so" +linux.release.arm64 = "res://addons/goldsrc/bin/libgoldsrc.linux.template_release.arm64.so" + +android.debug.arm64 = "res://addons/goldsrc/bin/libgoldsrc.android.template_debug.arm64.so" +android.release.arm64 = "res://addons/goldsrc/bin/libgoldsrc.android.template_release.arm64.so" diff --git a/addons/goldsrc/goldsrc.gdextension.uid b/addons/goldsrc/goldsrc.gdextension.uid new file mode 100644 index 0000000..74f395a --- /dev/null +++ b/addons/goldsrc/goldsrc.gdextension.uid @@ -0,0 +1 @@ +uid://dsy8u3hdjay5p diff --git a/addons/goldsrc/importer/bsp_importer.gd b/addons/goldsrc/importer/bsp_importer.gd new file mode 100644 index 0000000..b4dd7f8 --- /dev/null +++ b/addons/goldsrc/importer/bsp_importer.gd @@ -0,0 +1,221 @@ +@tool +extends EditorImportPlugin + + +func _get_importer_name() -> String: + return "goldsrc.bsp" + + +func _get_visible_name() -> String: + return "GoldSrc BSP" + + +func _get_recognized_extensions() -> PackedStringArray: + return PackedStringArray(["bsp"]) + + +func _get_save_extension() -> String: + return "scn" + + +func _get_resource_type() -> String: + return "PackedScene" + + +func _get_preset_count() -> int: + return 1 + + +func _get_preset_name(preset_index: int) -> String: + return "Default" + + +func _get_priority() -> float: + return 1.0 + + +func _get_import_order() -> int: + return 1 # Must run after MDL/SPR importers (order 0) so their scenes are available. + + +# Must run on main thread — ArrayMesh/ImageTexture serialization deadlocks +# when ResourceSaver.save() is called from import worker threads. +func _can_import_threaded() -> bool: + return false + + +func _get_import_options(_path: String, _preset_index: int) -> Array[Dictionary]: + return [ + { + "name": "wad_directory", + "default_value": "", + "property_hint": PROPERTY_HINT_DIR, + "hint_string": "", + }, + { + "name": "scale_factor", + "default_value": 0.025, + "property_hint": PROPERTY_HINT_RANGE, + "hint_string": "0.001,1.0,0.001", + }, + # Base res:// path for resolving entity model/sprite references. + # Set this to the folder that mirrors your GoldSrc game root inside the + # project (e.g. res://res), so "models/headcrab.mdl" resolves correctly. + # Leave empty when your project root already mirrors the game root. + { + "name": "model_directory", + "default_value": "", + "property_hint": PROPERTY_HINT_DIR, + "hint_string": "", + }, + ] + + +func _get_option_visibility(_path: String, _option_name: StringName, _options: Dictionary) -> bool: + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, + _platform_variants: Array[String], _gen_files: Array[String]) -> Error: + var wad_directory: String = options.get("wad_directory", "") + var scale_factor: float = options.get("scale_factor", 0.025) + var model_directory: String = options.get("model_directory", "") + + var bsp := GoldSrcBSP.new() + bsp.set_scale_factor(scale_factor) + + bsp.load_bsp(source_file) + + # Resolve WAD files. If an explicit directory is given, scan it; otherwise + # read the WADs the BSP itself declares in worldspawn's "wad" key and look + # them up next to the .bsp and at the project root — so a stock project + # textures correctly with no per-file import config. + if wad_directory != "": + var dir := DirAccess.open(wad_directory) + if dir: + dir.list_dir_begin() + var file_name := dir.get_next() + while file_name != "": + if not dir.current_is_dir() and file_name.get_extension().to_lower() == "wad": + var wad := GoldSrcWAD.new() + var wad_path := wad_directory.path_join(file_name) + wad.load_wad(wad_path) + bsp.add_wad(wad) + file_name = dir.get_next() + dir.list_dir_end() + else: + push_warning("BSP Importer: Could not open WAD directory '%s'" % wad_directory) + else: + _load_declared_wads(bsp, source_file) + + bsp.build_mesh() + + # Reparent children to a plain Node3D so the saved scene is GDExtension-independent + var root := Node3D.new() + var root_name := source_file.get_file().get_basename() + if root_name.is_empty(): + root_name = "bsp_root" + root.name = root_name + + var children := bsp.get_children() + for child in children: + bsp.remove_child(child) + root.add_child(child) + + _instantiate_entity_models(root, model_directory) + + # Bake the stripped BSP (PLANES/NODES/CLIPNODES/LEAFS/VISIBILITY/MODELS) so + # VisibilityManager (PVS) and hull collision can both initialise from the .scn + # without the original .bsp. Collision bodies carry only a "bsp_model" index + # and walk up the tree to find this. build_mesh() already parked it on the BSP + # node, so read it back rather than rebuilding the whole payload — + # PackedByteArray is copy-on-write, so this is a refcount. + var bsp_blob: PackedByteArray = bsp.get_meta("bsp_data", PackedByteArray()) + if not bsp_blob.is_empty(): + root.set_meta("bsp_data", bsp_blob) + + _set_owner_recursive(root, root) + + var scene := PackedScene.new() + var pack_err := scene.pack(root) + if pack_err != OK: + push_error("BSP Importer: Failed to pack scene: %s" % error_string(pack_err)) + return pack_err + + var save_err := ResourceSaver.save(scene, save_path + "." + _get_save_extension()) + return save_err + + +# Load the WADs a BSP declares in worldspawn's "wad" key (a ';'-separated list +# of GoldSrc paths). Only the basename is used; each is searched for beside the +# .bsp first, then at the project root. This mirrors the runtime WAD lookup so +# the editor and the game texture maps identically without any import options. +func _load_declared_wads(bsp: GoldSrcBSP, source_file: String) -> void: + var wad_string := "" + for ent in bsp.get_entities(): + if ent.get("classname", "") == "worldspawn": + wad_string = ent.get("wad", "") + break + + var search_dirs := [source_file.get_base_dir(), "res://"] + var seen := {} + for wad_path in wad_string.split(";", false): + var wad_name := wad_path.get_file().to_lower() + if wad_name.is_empty() or seen.has(wad_name): + continue + seen[wad_name] = true + for base in search_dirs: + var full_path: String = base.path_join(wad_name) + if FileAccess.file_exists(full_path): + var wad := GoldSrcWAD.new() + if wad.load_wad(full_path) == OK: + bsp.add_wad(wad) + break + + +func _set_owner_recursive(node: Node, owner: Node) -> void: + for child in node.get_children(): + child.owner = owner + # Don't recurse into instanced sub-scenes — their internal nodes belong + # to that scene, not to this one. Setting owner on the root is enough. + if child.scene_file_path == "": + _set_owner_recursive(child, owner) + + +# Instantiate MDL/SPR scenes for each point entity that has a "model" key. +# Resolves entity paths relative to model_dir (if set) or res:// (if empty), +# mirroring the GoldSrc layout at that root. +# Falls back to a placeholder box if the asset is not found. +func _instantiate_entity_models(root: Node3D, model_dir: String) -> void: + var base := model_dir if model_dir != "" else "res://" + for child in root.get_children(): + if not child.has_meta("entity"): + continue + var ent: Dictionary = child.get_meta("entity") + var model_path: String = ent.get("model", "") + + # Brush entities reference internal BSP sub-models ("*N") — skip them. + if model_path.is_empty() or model_path.begins_with("*"): + continue + + var res_path := base.path_join(model_path) + var instance: Node3D = null + + if ResourceLoader.exists(res_path): + var scene := load(res_path) as PackedScene + if scene: + instance = scene.instantiate() as Node3D + + if instance == null: + var mi := MeshInstance3D.new() + var box := BoxMesh.new() + box.size = Vector3(0.3, 0.3, 0.3) + mi.mesh = box + instance = mi + push_warning("BSP Importer: model not found, using placeholder for '%s'" % res_path) + + var node_name := model_path.get_file().get_basename() + if node_name.is_empty(): + node_name = "unnamed_entity_model" + instance.name = node_name + child.add_child(instance) diff --git a/addons/goldsrc/importer/bsp_importer.gd.uid b/addons/goldsrc/importer/bsp_importer.gd.uid new file mode 100644 index 0000000..3a1873d --- /dev/null +++ b/addons/goldsrc/importer/bsp_importer.gd.uid @@ -0,0 +1 @@ +uid://bkjulqpbo8sjd diff --git a/addons/goldsrc/importer/mdl_importer.gd b/addons/goldsrc/importer/mdl_importer.gd new file mode 100644 index 0000000..012087d --- /dev/null +++ b/addons/goldsrc/importer/mdl_importer.gd @@ -0,0 +1,112 @@ +@tool +extends EditorImportPlugin + + +func _get_importer_name() -> String: + return "goldsrc.mdl" + + +func _get_visible_name() -> String: + return "GoldSrc MDL" + + +func _get_recognized_extensions() -> PackedStringArray: + return PackedStringArray(["mdl"]) + + +func _get_save_extension() -> String: + return "scn" + + +func _get_resource_type() -> String: + return "PackedScene" + + +func _get_preset_count() -> int: + return 1 + + +func _get_preset_name(preset_index: int) -> String: + return "Default" + + +## What shape of scene this importer builds. 1: meshes merged into one MeshInstance3D per +## bodypart. 2: triangle corners welded back into shared vertices. +## +## Godot records it in each .import file but does not act on a bump — neither --import nor an +## editor session reimports on its own (checked on 4.7). Changing the built scene means clearing +## .godot/imported/*.mdl-* by hand; the number is here so it is visible which build a cache came +## from. +func _get_format_version() -> int: + return 2 + + +func _get_priority() -> float: + return 1.0 + + +func _get_import_order() -> int: + return 0 + + +func _can_import_threaded() -> bool: + return false + + +func _get_import_options(_path: String, _preset_index: int) -> Array[Dictionary]: + return [ + { + "name": "scale_factor", + "default_value": 0.025, + "property_hint": PROPERTY_HINT_RANGE, + "hint_string": "0.001,1.0,0.001", + }, + ] + + +func _get_option_visibility(_path: String, _option_name: StringName, _options: Dictionary) -> bool: + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, + _platform_variants: Array[String], _gen_files: Array[String]) -> Error: + var scale_factor: float = options.get("scale_factor", 0.025) + + var mdl := GoldSrcMDL.new() + mdl.set_scale_factor(scale_factor) + + var err = mdl.load_mdl(source_file) + if err != OK: + push_error("MDL Importer: Failed to load '%s': %s" % [source_file, error_string(err)]) + return err + + mdl.build_model() + + # Reparent children to a plain Node3D so the saved scene is GDExtension-independent + var root := Node3D.new() + var root_name := source_file.get_file().get_basename() + if root_name.is_empty(): + root_name = "mdl_root" + root.name = root_name + # GoldSrc models face -Z after axis swizzle; rotate to match GoldSrc game orientation + root.rotation_degrees.y = 90.0 + + var children := mdl.get_children() + for child in children: + mdl.remove_child(child) + root.add_child(child) + _set_owner_recursive(root, root) + + var scene := PackedScene.new() + var pack_err := scene.pack(root) + if pack_err != OK: + push_error("MDL Importer: Failed to pack scene: %s" % error_string(pack_err)) + return pack_err + + return ResourceSaver.save(scene, save_path + "." + _get_save_extension()) + + +func _set_owner_recursive(node: Node, owner: Node) -> void: + for child in node.get_children(): + child.owner = owner + _set_owner_recursive(child, owner) diff --git a/addons/goldsrc/importer/mdl_importer.gd.uid b/addons/goldsrc/importer/mdl_importer.gd.uid new file mode 100644 index 0000000..479152d --- /dev/null +++ b/addons/goldsrc/importer/mdl_importer.gd.uid @@ -0,0 +1 @@ +uid://bp0377di4pbve diff --git a/addons/goldsrc/importer/spr_importer.gd b/addons/goldsrc/importer/spr_importer.gd new file mode 100644 index 0000000..5f074f9 --- /dev/null +++ b/addons/goldsrc/importer/spr_importer.gd @@ -0,0 +1,103 @@ +@tool +extends EditorImportPlugin + + +func _get_importer_name() -> String: + return "goldsrc.spr" + + +func _get_visible_name() -> String: + return "GoldSrc SPR" + + +func _get_recognized_extensions() -> PackedStringArray: + return PackedStringArray(["spr"]) + + +func _get_save_extension() -> String: + return "scn" + + +func _get_resource_type() -> String: + return "PackedScene" + + +func _get_preset_count() -> int: + return 1 + + +func _get_preset_name(preset_index: int) -> String: + return "Default" + + +func _get_priority() -> float: + return 1.0 + + +func _get_import_order() -> int: + return 0 + + +func _can_import_threaded() -> bool: + return false + + +func _get_import_options(_path: String, _preset_index: int) -> Array[Dictionary]: + return [ + { + "name": "animation_fps", + "default_value": 10.0, + "property_hint": PROPERTY_HINT_RANGE, + "hint_string": "1.0,60.0,0.5", + }, + { + "name": "loop", + "default_value": true, + }, + ] + + +func _get_option_visibility(_path: String, _option_name: StringName, _options: Dictionary) -> bool: + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, + _platform_variants: Array[String], _gen_files: Array[String]) -> Error: + var fps: float = options.get("animation_fps", 10.0) + var loop: bool = options.get("loop", true) + + var spr := GoldSrcSPR.new() + var err = spr.load_spr(source_file) + if err != OK: + push_error("SPR Importer: Failed to load '%s': %s" % [source_file, error_string(err)]) + return err + + var frame_count = spr.get_frame_count() + if frame_count == 0: + push_error("SPR Importer: No frames in '%s'" % source_file) + return ERR_INVALID_DATA + + var sprite = spr.build_scene() + if not sprite: + push_error("SPR Importer: build_scene() failed for '%s'" % source_file) + return ERR_CANT_CREATE + + var sprite_name := source_file.get_file().get_basename() + if sprite_name.is_empty(): + sprite_name = "sprite_root" + sprite.name = sprite_name + + # Bake import options into the SpriteAnimationPlayer child + var sap = sprite.get_node_or_null(^"SpriteAnimationPlayer") + if sap: + sap.fps = fps + sap.loop_animation = loop + + var scene := PackedScene.new() + var pack_err := scene.pack(sprite) + sprite.free() + if pack_err != OK: + push_error("SPR Importer: Failed to pack scene for '%s': %s" % [source_file, error_string(pack_err)]) + return pack_err + + return ResourceSaver.save(scene, save_path + "." + _get_save_extension()) diff --git a/addons/goldsrc/importer/spr_importer.gd.uid b/addons/goldsrc/importer/spr_importer.gd.uid new file mode 100644 index 0000000..e140e8a --- /dev/null +++ b/addons/goldsrc/importer/spr_importer.gd.uid @@ -0,0 +1 @@ +uid://n8cr2s2yyhqe diff --git a/addons/goldsrc/importer/wad_importer.gd b/addons/goldsrc/importer/wad_importer.gd new file mode 100644 index 0000000..a460aca --- /dev/null +++ b/addons/goldsrc/importer/wad_importer.gd @@ -0,0 +1,110 @@ +@tool +extends EditorImportPlugin +## Imports a GoldSrc WAD file. +## +## The WAD is saved as a minimal .tres marker so Godot tracks it as a known +## asset. The BSP importer loads WAD files directly via the C++ GoldSrcWAD +## class — it does not use extracted PNGs. +## +## PNG extraction is opt-in (extract_textures = false by default) for cases +## where individual textures are needed outside of BSP rendering. + + +func _get_importer_name() -> String: + return "goldsrc.wad" + + +func _get_visible_name() -> String: + return "GoldSrc WAD" + + +func _get_recognized_extensions() -> PackedStringArray: + return PackedStringArray(["wad"]) + + +func _get_save_extension() -> String: + return "tres" + + +func _get_resource_type() -> String: + return "Resource" + + +func _get_preset_count() -> int: + return 1 + + +func _get_preset_name(preset_index: int) -> String: + return "Default" + + +func _get_priority() -> float: + return 1.0 + + +func _get_import_order() -> int: + return 0 + + +func _can_import_threaded() -> bool: + return false + + +func _get_import_options(_path: String, _preset_index: int) -> Array[Dictionary]: + return [ + { + "name": "extract_textures", + "default_value": false, + }, + { + "name": "output_directory", + "default_value": "", + "property_hint": PROPERTY_HINT_DIR, + "hint_string": "Leave empty to extract next to the WAD file", + }, + ] + + +func _get_option_visibility(_path: String, _option_name: StringName, _options: Dictionary) -> bool: + if _option_name == "output_directory": + return _options.get("extract_textures", true) + return true + + +func _import(source_file: String, save_path: String, options: Dictionary, + _platform_variants: Array[String], _gen_files: Array[String]) -> Error: + var extract: bool = options.get("extract_textures", true) + + var wad := GoldSrcWAD.new() + var err = wad.load_wad(source_file) + if err != OK: + push_error("WAD Importer: Failed to load '%s': %s" % [source_file, error_string(err)]) + return err + + if extract: + var output_dir: String = options.get("output_directory", "") + if output_dir.is_empty(): + # Default: create a folder named after the WAD next to it + output_dir = source_file.get_base_dir().path_join( + source_file.get_file().get_basename()) + + DirAccess.make_dir_recursive_absolute(output_dir) + + var names = wad.get_texture_names() + var count := 0 + for tex_name in names: + var tex = wad.get_texture(tex_name) + if tex == null: + continue + var img = tex.get_image() + if img == null: + continue + var png_path := output_dir.path_join(tex_name.to_lower() + ".png") + img.save_png(png_path) + count += 1 + + print("WAD Importer: Extracted %d textures to %s" % [count, output_dir]) + + # Save a minimal marker resource so Godot considers the import complete + var res := Resource.new() + return ResourceSaver.save(res, save_path + "." + _get_save_extension()) diff --git a/addons/goldsrc/importer/wad_importer.gd.uid b/addons/goldsrc/importer/wad_importer.gd.uid new file mode 100644 index 0000000..837f12e --- /dev/null +++ b/addons/goldsrc/importer/wad_importer.gd.uid @@ -0,0 +1 @@ +uid://dl2ri2g5ai8cf diff --git a/addons/goldsrc/plugin.cfg b/addons/goldsrc/plugin.cfg new file mode 100644 index 0000000..65ff8fe --- /dev/null +++ b/addons/goldsrc/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="GoldSrc" +description="Import GoldSrc engine assets (BSP maps, WAD textures, MDL models, SPR sprites)" +author="goldsrc-godot" +version="0.1.0" +script="plugin.gd" diff --git a/addons/goldsrc/plugin.gd b/addons/goldsrc/plugin.gd new file mode 100644 index 0000000..931b200 --- /dev/null +++ b/addons/goldsrc/plugin.gd @@ -0,0 +1,29 @@ +@tool +extends EditorPlugin + +var bsp_importer: EditorImportPlugin +var mdl_importer: EditorImportPlugin +var spr_importer: EditorImportPlugin +var wad_importer: EditorImportPlugin + + +func _enter_tree() -> void: + bsp_importer = preload("res://addons/goldsrc/importer/bsp_importer.gd").new() + mdl_importer = preload("res://addons/goldsrc/importer/mdl_importer.gd").new() + spr_importer = preload("res://addons/goldsrc/importer/spr_importer.gd").new() + wad_importer = preload("res://addons/goldsrc/importer/wad_importer.gd").new() + add_import_plugin(bsp_importer) + add_import_plugin(mdl_importer) + add_import_plugin(spr_importer) + add_import_plugin(wad_importer) + + +func _exit_tree() -> void: + remove_import_plugin(bsp_importer) + remove_import_plugin(mdl_importer) + remove_import_plugin(spr_importer) + remove_import_plugin(wad_importer) + bsp_importer = null + mdl_importer = null + spr_importer = null + wad_importer = null diff --git a/addons/goldsrc/plugin.gd.uid b/addons/goldsrc/plugin.gd.uid new file mode 100644 index 0000000..10b4c3d --- /dev/null +++ b/addons/goldsrc/plugin.gd.uid @@ -0,0 +1 @@ +uid://eql0skovdole diff --git a/assets/audio/debris/bustglass1.wav b/assets/audio/debris/bustglass1.wav new file mode 100644 index 0000000..f980d0e Binary files /dev/null and b/assets/audio/debris/bustglass1.wav differ diff --git a/assets/audio/debris/bustglass1.wav.import b/assets/audio/debris/bustglass1.wav.import new file mode 100644 index 0000000..5b63999 --- /dev/null +++ b/assets/audio/debris/bustglass1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://7bv5im2gve24" +path="res://.godot/imported/bustglass1.wav-5a321c1622e14fb90448620412692ee8.sample" + +[deps] + +source_file="res://assets/audio/debris/bustglass1.wav" +dest_files=["res://.godot/imported/bustglass1.wav-5a321c1622e14fb90448620412692ee8.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/debris/bustglass2.wav b/assets/audio/debris/bustglass2.wav new file mode 100644 index 0000000..c31b4f7 Binary files /dev/null and b/assets/audio/debris/bustglass2.wav differ diff --git a/assets/audio/debris/bustglass2.wav.import b/assets/audio/debris/bustglass2.wav.import new file mode 100644 index 0000000..e3b72d7 --- /dev/null +++ b/assets/audio/debris/bustglass2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ccdljstwvm5ap" +path="res://.godot/imported/bustglass2.wav-19b3dbab1fcc4a54c87c97a923b7cf7a.sample" + +[deps] + +source_file="res://assets/audio/debris/bustglass2.wav" +dest_files=["res://.godot/imported/bustglass2.wav-19b3dbab1fcc4a54c87c97a923b7cf7a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/debris/bustglass3.wav b/assets/audio/debris/bustglass3.wav new file mode 100644 index 0000000..3b19bdf Binary files /dev/null and b/assets/audio/debris/bustglass3.wav differ diff --git a/assets/audio/debris/bustglass3.wav.import b/assets/audio/debris/bustglass3.wav.import new file mode 100644 index 0000000..4907698 --- /dev/null +++ b/assets/audio/debris/bustglass3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://byafn5vjq1q4i" +path="res://.godot/imported/bustglass3.wav-897f694350026f541b2bd32aa4be9ce2.sample" + +[deps] + +source_file="res://assets/audio/debris/bustglass3.wav" +dest_files=["res://.godot/imported/bustglass3.wav-897f694350026f541b2bd32aa4be9ce2.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/items/clipinsert1.wav b/assets/audio/items/clipinsert1.wav new file mode 100644 index 0000000..ae63106 Binary files /dev/null and b/assets/audio/items/clipinsert1.wav differ diff --git a/assets/audio/items/clipinsert1.wav.import b/assets/audio/items/clipinsert1.wav.import new file mode 100644 index 0000000..b218b83 --- /dev/null +++ b/assets/audio/items/clipinsert1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://s3toeu34hkyu" +path="res://.godot/imported/clipinsert1.wav-6c6bbac54c1a1e384662e873d716b7dc.sample" + +[deps] + +source_file="res://assets/audio/items/clipinsert1.wav" +dest_files=["res://.godot/imported/clipinsert1.wav-6c6bbac54c1a1e384662e873d716b7dc.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/items/cliprelease1.wav b/assets/audio/items/cliprelease1.wav new file mode 100644 index 0000000..de172e4 Binary files /dev/null and b/assets/audio/items/cliprelease1.wav differ diff --git a/assets/audio/items/cliprelease1.wav.import b/assets/audio/items/cliprelease1.wav.import new file mode 100644 index 0000000..da7d019 --- /dev/null +++ b/assets/audio/items/cliprelease1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ce5a4cctk2cxu" +path="res://.godot/imported/cliprelease1.wav-217792f9ae8c4e1df723fdfb39502c53.sample" + +[deps] + +source_file="res://assets/audio/items/cliprelease1.wav" +dest_files=["res://.godot/imported/cliprelease1.wav-217792f9ae8c4e1df723fdfb39502c53.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/items/guncock1.wav b/assets/audio/items/guncock1.wav new file mode 100644 index 0000000..6b3d30b Binary files /dev/null and b/assets/audio/items/guncock1.wav differ diff --git a/assets/audio/items/guncock1.wav.import b/assets/audio/items/guncock1.wav.import new file mode 100644 index 0000000..b57dc96 --- /dev/null +++ b/assets/audio/items/guncock1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://y7a0fdpxr6xu" +path="res://.godot/imported/guncock1.wav-54ef75289da4dbfe0244fb7564377961.sample" + +[deps] + +source_file="res://assets/audio/items/guncock1.wav" +dest_files=["res://.godot/imported/guncock1.wav-54ef75289da4dbfe0244fb7564377961.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/music/zombie_industrial_loop.ogg b/assets/audio/music/zombie_industrial_loop.ogg new file mode 100644 index 0000000..7ec7907 Binary files /dev/null and b/assets/audio/music/zombie_industrial_loop.ogg differ diff --git a/assets/audio/music/zombie_industrial_loop.ogg.import b/assets/audio/music/zombie_industrial_loop.ogg.import new file mode 100644 index 0000000..2197936 --- /dev/null +++ b/assets/audio/music/zombie_industrial_loop.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://cjmh2mhrcnhmb" +path="res://.godot/imported/zombie_industrial_loop.ogg-97e063f96057a019c1cb4b11d3b1adc6.oggvorbisstr" + +[deps] + +source_file="res://assets/audio/music/zombie_industrial_loop.ogg" +dest_files=["res://.godot/imported/zombie_industrial_loop.ogg-97e063f96057a019c1cb4b11d3b1adc6.oggvorbisstr"] + +[params] + +loop=true +loop_offset=0 +bpm=112.5 +beat_count=108 +bar_beats=4 diff --git a/assets/audio/player/pl_metal1.wav b/assets/audio/player/pl_metal1.wav new file mode 100644 index 0000000..4f2d33a Binary files /dev/null and b/assets/audio/player/pl_metal1.wav differ diff --git a/assets/audio/player/pl_metal1.wav.import b/assets/audio/player/pl_metal1.wav.import new file mode 100644 index 0000000..4510f56 --- /dev/null +++ b/assets/audio/player/pl_metal1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://xyc5cwgyhfms" +path="res://.godot/imported/pl_metal1.wav-42fb8eebd82a46db937bb26761e30ef3.sample" + +[deps] + +source_file="res://assets/audio/player/pl_metal1.wav" +dest_files=["res://.godot/imported/pl_metal1.wav-42fb8eebd82a46db937bb26761e30ef3.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/player/pl_metal2.wav b/assets/audio/player/pl_metal2.wav new file mode 100644 index 0000000..292695d Binary files /dev/null and b/assets/audio/player/pl_metal2.wav differ diff --git a/assets/audio/player/pl_metal2.wav.import b/assets/audio/player/pl_metal2.wav.import new file mode 100644 index 0000000..8893a33 --- /dev/null +++ b/assets/audio/player/pl_metal2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ho1s43gk3hh7" +path="res://.godot/imported/pl_metal2.wav-68c295c4e5934b39639453eb4ac7bb86.sample" + +[deps] + +source_file="res://assets/audio/player/pl_metal2.wav" +dest_files=["res://.godot/imported/pl_metal2.wav-68c295c4e5934b39639453eb4ac7bb86.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/player/pl_metal3.wav b/assets/audio/player/pl_metal3.wav new file mode 100644 index 0000000..5295d91 Binary files /dev/null and b/assets/audio/player/pl_metal3.wav differ diff --git a/assets/audio/player/pl_metal3.wav.import b/assets/audio/player/pl_metal3.wav.import new file mode 100644 index 0000000..6a2c1c1 --- /dev/null +++ b/assets/audio/player/pl_metal3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://b4p7jku5hr3m5" +path="res://.godot/imported/pl_metal3.wav-b7957637b584240fd4fbf20022deda4f.sample" + +[deps] + +source_file="res://assets/audio/player/pl_metal3.wav" +dest_files=["res://.godot/imported/pl_metal3.wav-b7957637b584240fd4fbf20022deda4f.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/player/pl_metal4.wav b/assets/audio/player/pl_metal4.wav new file mode 100644 index 0000000..87d02bd Binary files /dev/null and b/assets/audio/player/pl_metal4.wav differ diff --git a/assets/audio/player/pl_metal4.wav.import b/assets/audio/player/pl_metal4.wav.import new file mode 100644 index 0000000..1f668fe --- /dev/null +++ b/assets/audio/player/pl_metal4.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://6rqyer6de744" +path="res://.godot/imported/pl_metal4.wav-75e3af6a3e59541fba589ed0077615b3.sample" + +[deps] + +source_file="res://assets/audio/player/pl_metal4.wav" +dest_files=["res://.godot/imported/pl_metal4.wav-75e3af6a3e59541fba589ed0077615b3.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/player/pl_pain7.wav b/assets/audio/player/pl_pain7.wav new file mode 100644 index 0000000..6d4ea2b Binary files /dev/null and b/assets/audio/player/pl_pain7.wav differ diff --git a/assets/audio/player/pl_pain7.wav.import b/assets/audio/player/pl_pain7.wav.import new file mode 100644 index 0000000..1e026fb --- /dev/null +++ b/assets/audio/player/pl_pain7.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cgj8qrq5b8ek1" +path="res://.godot/imported/pl_pain7.wav-9b92e039c95d7d9ed3bce71138d0d840.sample" + +[deps] + +source_file="res://assets/audio/player/pl_pain7.wav" +dest_files=["res://.godot/imported/pl_pain7.wav-9b92e039c95d7d9ed3bce71138d0d840.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/dryfire1.wav b/assets/audio/weapons/dryfire1.wav new file mode 100644 index 0000000..cb3f41c Binary files /dev/null and b/assets/audio/weapons/dryfire1.wav differ diff --git a/assets/audio/weapons/dryfire1.wav.import b/assets/audio/weapons/dryfire1.wav.import new file mode 100644 index 0000000..3baea32 --- /dev/null +++ b/assets/audio/weapons/dryfire1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://bkuqkytgyiqoc" +path="res://.godot/imported/dryfire1.wav-a0ce0b345f70cf8c349f33366ea96915.sample" + +[deps] + +source_file="res://assets/audio/weapons/dryfire1.wav" +dest_files=["res://.godot/imported/dryfire1.wav-a0ce0b345f70cf8c349f33366ea96915.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/explode3.wav b/assets/audio/weapons/explode3.wav new file mode 100644 index 0000000..21229a8 Binary files /dev/null and b/assets/audio/weapons/explode3.wav differ diff --git a/assets/audio/weapons/explode3.wav.import b/assets/audio/weapons/explode3.wav.import new file mode 100644 index 0000000..59a2340 --- /dev/null +++ b/assets/audio/weapons/explode3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://d2tbx2yh7bsgb" +path="res://.godot/imported/explode3.wav-3dba11140b272f7d09f0a32dae9c68f4.sample" + +[deps] + +source_file="res://assets/audio/weapons/explode3.wav" +dest_files=["res://.godot/imported/explode3.wav-3dba11140b272f7d09f0a32dae9c68f4.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/explode4.wav b/assets/audio/weapons/explode4.wav new file mode 100644 index 0000000..b90ef80 Binary files /dev/null and b/assets/audio/weapons/explode4.wav differ diff --git a/assets/audio/weapons/explode4.wav.import b/assets/audio/weapons/explode4.wav.import new file mode 100644 index 0000000..be2085e --- /dev/null +++ b/assets/audio/weapons/explode4.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://7rp6ypy5qkxo" +path="res://.godot/imported/explode4.wav-2a1fd2def1be7f87e2d4170c7935734e.sample" + +[deps] + +source_file="res://assets/audio/weapons/explode4.wav" +dest_files=["res://.godot/imported/explode4.wav-2a1fd2def1be7f87e2d4170c7935734e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/explode5.wav b/assets/audio/weapons/explode5.wav new file mode 100644 index 0000000..b01430c Binary files /dev/null and b/assets/audio/weapons/explode5.wav differ diff --git a/assets/audio/weapons/explode5.wav.import b/assets/audio/weapons/explode5.wav.import new file mode 100644 index 0000000..def1ca3 --- /dev/null +++ b/assets/audio/weapons/explode5.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cmbo466uy2w44" +path="res://.godot/imported/explode5.wav-f37b7d55a94ca74c637137d08c0efc5a.sample" + +[deps] + +source_file="res://assets/audio/weapons/explode5.wav" +dest_files=["res://.godot/imported/explode5.wav-f37b7d55a94ca74c637137d08c0efc5a.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/hks1.wav b/assets/audio/weapons/hks1.wav new file mode 100644 index 0000000..e0f90bb Binary files /dev/null and b/assets/audio/weapons/hks1.wav differ diff --git a/assets/audio/weapons/hks1.wav.import b/assets/audio/weapons/hks1.wav.import new file mode 100644 index 0000000..5c886a2 --- /dev/null +++ b/assets/audio/weapons/hks1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://eu4mpcdrhujg" +path="res://.godot/imported/hks1.wav-5afc7788d247a6e2ecdee212682213f3.sample" + +[deps] + +source_file="res://assets/audio/weapons/hks1.wav" +dest_files=["res://.godot/imported/hks1.wav-5afc7788d247a6e2ecdee212682213f3.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/hks2.wav b/assets/audio/weapons/hks2.wav new file mode 100644 index 0000000..3621308 Binary files /dev/null and b/assets/audio/weapons/hks2.wav differ diff --git a/assets/audio/weapons/hks2.wav.import b/assets/audio/weapons/hks2.wav.import new file mode 100644 index 0000000..c3ffab0 --- /dev/null +++ b/assets/audio/weapons/hks2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ba2p4vv66pxbo" +path="res://.godot/imported/hks2.wav-a8fa00ea683bacaa73ed3971af49ec46.sample" + +[deps] + +source_file="res://assets/audio/weapons/hks2.wav" +dest_files=["res://.godot/imported/hks2.wav-a8fa00ea683bacaa73ed3971af49ec46.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/weapons/hks3.wav b/assets/audio/weapons/hks3.wav new file mode 100644 index 0000000..ce05eb7 Binary files /dev/null and b/assets/audio/weapons/hks3.wav differ diff --git a/assets/audio/weapons/hks3.wav.import b/assets/audio/weapons/hks3.wav.import new file mode 100644 index 0000000..f2ac581 --- /dev/null +++ b/assets/audio/weapons/hks3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cldvtw12686n6" +path="res://.godot/imported/hks3.wav-6d4c5e98aa75a598bbaeb4d3ae9762fa.sample" + +[deps] + +source_file="res://assets/audio/weapons/hks3.wav" +dest_files=["res://.godot/imported/hks3.wav-6d4c5e98aa75a598bbaeb4d3ae9762fa.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/claw_miss1.wav b/assets/audio/zombie/claw_miss1.wav new file mode 100644 index 0000000..eec4273 Binary files /dev/null and b/assets/audio/zombie/claw_miss1.wav differ diff --git a/assets/audio/zombie/claw_miss1.wav.import b/assets/audio/zombie/claw_miss1.wav.import new file mode 100644 index 0000000..1be4794 --- /dev/null +++ b/assets/audio/zombie/claw_miss1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cyvgev7nhen2v" +path="res://.godot/imported/claw_miss1.wav-14558e068d3bf5903446c1905fd2aea5.sample" + +[deps] + +source_file="res://assets/audio/zombie/claw_miss1.wav" +dest_files=["res://.godot/imported/claw_miss1.wav-14558e068d3bf5903446c1905fd2aea5.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/claw_miss2.wav b/assets/audio/zombie/claw_miss2.wav new file mode 100644 index 0000000..e2a5239 Binary files /dev/null and b/assets/audio/zombie/claw_miss2.wav differ diff --git a/assets/audio/zombie/claw_miss2.wav.import b/assets/audio/zombie/claw_miss2.wav.import new file mode 100644 index 0000000..6ab0553 --- /dev/null +++ b/assets/audio/zombie/claw_miss2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dwfnaltcs0xhu" +path="res://.godot/imported/claw_miss2.wav-f943074afb53eeef25d51da65bf9c83d.sample" + +[deps] + +source_file="res://assets/audio/zombie/claw_miss2.wav" +dest_files=["res://.godot/imported/claw_miss2.wav-f943074afb53eeef25d51da65bf9c83d.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/claw_strike1.wav b/assets/audio/zombie/claw_strike1.wav new file mode 100644 index 0000000..b5aa5a6 Binary files /dev/null and b/assets/audio/zombie/claw_strike1.wav differ diff --git a/assets/audio/zombie/claw_strike1.wav.import b/assets/audio/zombie/claw_strike1.wav.import new file mode 100644 index 0000000..9d1ec67 --- /dev/null +++ b/assets/audio/zombie/claw_strike1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://ciss3tx6orpsq" +path="res://.godot/imported/claw_strike1.wav-a4baca2453611bd2eb4ab6b09abda977.sample" + +[deps] + +source_file="res://assets/audio/zombie/claw_strike1.wav" +dest_files=["res://.godot/imported/claw_strike1.wav-a4baca2453611bd2eb4ab6b09abda977.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/claw_strike2.wav b/assets/audio/zombie/claw_strike2.wav new file mode 100644 index 0000000..46d696e Binary files /dev/null and b/assets/audio/zombie/claw_strike2.wav differ diff --git a/assets/audio/zombie/claw_strike2.wav.import b/assets/audio/zombie/claw_strike2.wav.import new file mode 100644 index 0000000..5719da6 --- /dev/null +++ b/assets/audio/zombie/claw_strike2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://br4cucdm3rlei" +path="res://.godot/imported/claw_strike2.wav-a29c4c7afd58c82a25fcbcad9f2a7e2f.sample" + +[deps] + +source_file="res://assets/audio/zombie/claw_strike2.wav" +dest_files=["res://.godot/imported/claw_strike2.wav-a29c4c7afd58c82a25fcbcad9f2a7e2f.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/claw_strike3.wav b/assets/audio/zombie/claw_strike3.wav new file mode 100644 index 0000000..8883bbb Binary files /dev/null and b/assets/audio/zombie/claw_strike3.wav differ diff --git a/assets/audio/zombie/claw_strike3.wav.import b/assets/audio/zombie/claw_strike3.wav.import new file mode 100644 index 0000000..b58b75a --- /dev/null +++ b/assets/audio/zombie/claw_strike3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://s7wd3grqssry" +path="res://.godot/imported/claw_strike3.wav-10b43573524aa576abb1ab30a0bf923c.sample" + +[deps] + +source_file="res://assets/audio/zombie/claw_strike3.wav" +dest_files=["res://.godot/imported/claw_strike3.wav-10b43573524aa576abb1ab30a0bf923c.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_alert10.wav b/assets/audio/zombie/zo_alert10.wav new file mode 100644 index 0000000..279df0a Binary files /dev/null and b/assets/audio/zombie/zo_alert10.wav differ diff --git a/assets/audio/zombie/zo_alert10.wav.import b/assets/audio/zombie/zo_alert10.wav.import new file mode 100644 index 0000000..29129fc --- /dev/null +++ b/assets/audio/zombie/zo_alert10.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://o4g6in1otyig" +path="res://.godot/imported/zo_alert10.wav-5c62486989fa332299acadd4861c5413.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_alert10.wav" +dest_files=["res://.godot/imported/zo_alert10.wav-5c62486989fa332299acadd4861c5413.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_alert20.wav b/assets/audio/zombie/zo_alert20.wav new file mode 100644 index 0000000..6342e6a Binary files /dev/null and b/assets/audio/zombie/zo_alert20.wav differ diff --git a/assets/audio/zombie/zo_alert20.wav.import b/assets/audio/zombie/zo_alert20.wav.import new file mode 100644 index 0000000..1c9b6da --- /dev/null +++ b/assets/audio/zombie/zo_alert20.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dkd7hfxoj5xjh" +path="res://.godot/imported/zo_alert20.wav-b9de2e7eba81a460536ba692c8430cfd.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_alert20.wav" +dest_files=["res://.godot/imported/zo_alert20.wav-b9de2e7eba81a460536ba692c8430cfd.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_alert30.wav b/assets/audio/zombie/zo_alert30.wav new file mode 100644 index 0000000..372d64e Binary files /dev/null and b/assets/audio/zombie/zo_alert30.wav differ diff --git a/assets/audio/zombie/zo_alert30.wav.import b/assets/audio/zombie/zo_alert30.wav.import new file mode 100644 index 0000000..2bf7209 --- /dev/null +++ b/assets/audio/zombie/zo_alert30.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://sk1ro4urg2cs" +path="res://.godot/imported/zo_alert30.wav-dcc133093c4a2b3696bca82fad3ec896.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_alert30.wav" +dest_files=["res://.godot/imported/zo_alert30.wav-dcc133093c4a2b3696bca82fad3ec896.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_attack1.wav b/assets/audio/zombie/zo_attack1.wav new file mode 100644 index 0000000..86c6232 Binary files /dev/null and b/assets/audio/zombie/zo_attack1.wav differ diff --git a/assets/audio/zombie/zo_attack1.wav.import b/assets/audio/zombie/zo_attack1.wav.import new file mode 100644 index 0000000..4fd5c9d --- /dev/null +++ b/assets/audio/zombie/zo_attack1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://748ao6mr1nel" +path="res://.godot/imported/zo_attack1.wav-3002ebc52375b18c0a28e6674493ddd3.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_attack1.wav" +dest_files=["res://.godot/imported/zo_attack1.wav-3002ebc52375b18c0a28e6674493ddd3.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_attack2.wav b/assets/audio/zombie/zo_attack2.wav new file mode 100644 index 0000000..17d275b Binary files /dev/null and b/assets/audio/zombie/zo_attack2.wav differ diff --git a/assets/audio/zombie/zo_attack2.wav.import b/assets/audio/zombie/zo_attack2.wav.import new file mode 100644 index 0000000..40df314 --- /dev/null +++ b/assets/audio/zombie/zo_attack2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://0l8xv6hx3oj6" +path="res://.godot/imported/zo_attack2.wav-15c5e901615a03e92903dfe51b842da0.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_attack2.wav" +dest_files=["res://.godot/imported/zo_attack2.wav-15c5e901615a03e92903dfe51b842da0.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_idle1.wav b/assets/audio/zombie/zo_idle1.wav new file mode 100644 index 0000000..bcb2272 Binary files /dev/null and b/assets/audio/zombie/zo_idle1.wav differ diff --git a/assets/audio/zombie/zo_idle1.wav.import b/assets/audio/zombie/zo_idle1.wav.import new file mode 100644 index 0000000..5ff74dd --- /dev/null +++ b/assets/audio/zombie/zo_idle1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://crh4qscriwgk7" +path="res://.godot/imported/zo_idle1.wav-a27e0c3b40ad3a01c705cf4f2e3fae40.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_idle1.wav" +dest_files=["res://.godot/imported/zo_idle1.wav-a27e0c3b40ad3a01c705cf4f2e3fae40.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_idle2.wav b/assets/audio/zombie/zo_idle2.wav new file mode 100644 index 0000000..57a7123 Binary files /dev/null and b/assets/audio/zombie/zo_idle2.wav differ diff --git a/assets/audio/zombie/zo_idle2.wav.import b/assets/audio/zombie/zo_idle2.wav.import new file mode 100644 index 0000000..3dfc7e4 --- /dev/null +++ b/assets/audio/zombie/zo_idle2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://d0xflxyxshva2" +path="res://.godot/imported/zo_idle2.wav-f6c47eb9b7f30bacddd7912c1239cd0e.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_idle2.wav" +dest_files=["res://.godot/imported/zo_idle2.wav-f6c47eb9b7f30bacddd7912c1239cd0e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_idle3.wav b/assets/audio/zombie/zo_idle3.wav new file mode 100644 index 0000000..ce5551e Binary files /dev/null and b/assets/audio/zombie/zo_idle3.wav differ diff --git a/assets/audio/zombie/zo_idle3.wav.import b/assets/audio/zombie/zo_idle3.wav.import new file mode 100644 index 0000000..c36f508 --- /dev/null +++ b/assets/audio/zombie/zo_idle3.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://qiewybqnhnd6" +path="res://.godot/imported/zo_idle3.wav-91fec152e336727fd32a741aedba191e.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_idle3.wav" +dest_files=["res://.godot/imported/zo_idle3.wav-91fec152e336727fd32a741aedba191e.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_idle4.wav b/assets/audio/zombie/zo_idle4.wav new file mode 100644 index 0000000..3d72293 Binary files /dev/null and b/assets/audio/zombie/zo_idle4.wav differ diff --git a/assets/audio/zombie/zo_idle4.wav.import b/assets/audio/zombie/zo_idle4.wav.import new file mode 100644 index 0000000..046c6bd --- /dev/null +++ b/assets/audio/zombie/zo_idle4.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dxgc66x1yivao" +path="res://.godot/imported/zo_idle4.wav-aed9aa8acc48de084ce4c55d26056535.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_idle4.wav" +dest_files=["res://.godot/imported/zo_idle4.wav-aed9aa8acc48de084ce4c55d26056535.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_pain1.wav b/assets/audio/zombie/zo_pain1.wav new file mode 100644 index 0000000..c2bf817 Binary files /dev/null and b/assets/audio/zombie/zo_pain1.wav differ diff --git a/assets/audio/zombie/zo_pain1.wav.import b/assets/audio/zombie/zo_pain1.wav.import new file mode 100644 index 0000000..9b16b4b --- /dev/null +++ b/assets/audio/zombie/zo_pain1.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://cugg0jece34w3" +path="res://.godot/imported/zo_pain1.wav-148f9f8c593375ca6dccc65c0b8b94a6.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_pain1.wav" +dest_files=["res://.godot/imported/zo_pain1.wav-148f9f8c593375ca6dccc65c0b8b94a6.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/audio/zombie/zo_pain2.wav b/assets/audio/zombie/zo_pain2.wav new file mode 100644 index 0000000..ebf46ae Binary files /dev/null and b/assets/audio/zombie/zo_pain2.wav differ diff --git a/assets/audio/zombie/zo_pain2.wav.import b/assets/audio/zombie/zo_pain2.wav.import new file mode 100644 index 0000000..2aa0e47 --- /dev/null +++ b/assets/audio/zombie/zo_pain2.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://dd0h5f0tqi2re" +path="res://.godot/imported/zo_pain2.wav-a62544ba386a1a58ae488d14d8222fdf.sample" + +[deps] + +source_file="res://assets/audio/zombie/zo_pain2.wav" +dest_files=["res://.godot/imported/zo_pain2.wav-a62544ba386a1a58ae488d14d8222fdf.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/assets/effects/blood.png b/assets/effects/blood.png new file mode 100644 index 0000000..d9cb630 Binary files /dev/null and b/assets/effects/blood.png differ diff --git a/assets/effects/blood.png.import b/assets/effects/blood.png.import new file mode 100644 index 0000000..40fba72 --- /dev/null +++ b/assets/effects/blood.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ufe78u0lbqg1" +path="res://.godot/imported/blood.png-f969f52c0a73735f42da79aa1f6aefc4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/effects/blood.png" +dest_files=["res://.godot/imported/blood.png-f969f52c0a73735f42da79aa1f6aefc4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/effects/damage_vignette.gdshader b/assets/effects/damage_vignette.gdshader new file mode 100644 index 0000000..d66c41e --- /dev/null +++ b/assets/effects/damage_vignette.gdshader @@ -0,0 +1,8 @@ +shader_type canvas_item; +render_mode unshaded; +uniform float strength : hint_range(0.0, 1.0) = 0.0; +void fragment() { + vec2 p = (UV - vec2(0.5)) * 2.0; + float edge = smoothstep(0.28, 1.12, length(p)); + COLOR = vec4(0.65, 0.015, 0.025, edge * strength * 0.62); +} diff --git a/assets/effects/damage_vignette.gdshader.uid b/assets/effects/damage_vignette.gdshader.uid new file mode 100644 index 0000000..0fe3f42 --- /dev/null +++ b/assets/effects/damage_vignette.gdshader.uid @@ -0,0 +1 @@ +uid://ch5sbbogmvmwc diff --git a/assets/half_life_manifest.json b/assets/half_life_manifest.json new file mode 100644 index 0000000..ba69af8 --- /dev/null +++ b/assets/half_life_manifest.json @@ -0,0 +1,320 @@ +[ + { + "source": "models/v_9mmar.mdl", + "destination": "assets/weapons/v_9mmar.mdl", + "sha256": "3acee98ba922578e017db3e2087e3b6f2583e5d14a277f853bfe5a53de7468a5", + "textures": 13, + "sequence_groups": 1 + }, + { + "source": "models/w_9mmar.mdl", + "destination": "assets/weapons/w_9mmar.mdl", + "sha256": "8c471c4aa523c2a04fe117ff5b69f9a8b38da3fd30d483c6e9f4309f73cbb9ae", + "textures": 0, + "sequence_groups": 1 + }, + { + "source": "models/p_9mmar.mdl", + "destination": "assets/weapons/p_9mmar.mdl", + "sha256": "c40b0ad38d999d68430862a080d19bcea083db92e3d83503f6adc98cfb89a3de", + "textures": 2, + "sequence_groups": 1 + }, + { + "source": "sound/weapons/hks1.wav", + "destination": "assets/audio/weapons/hks1.wav", + "sha256": "f4df259d2c22faba729346172b937ad31ac4ab46f88fb0ecb3fc5ff40863dd63", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 1.2595918367346939 + }, + { + "source": "sound/weapons/hks2.wav", + "destination": "assets/audio/weapons/hks2.wav", + "sha256": "13fd0120e40ac5648e3da9724bbe129c61bb10dff476c1cf7c457042994700f7", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 1.152018140589569 + }, + { + "source": "sound/weapons/hks3.wav", + "destination": "assets/audio/weapons/hks3.wav", + "sha256": "cc1858ebbb266bdde31ccc03985e06f453b8687825c138b4792d1e87840a5437", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 1.2436734693877551 + }, + { + "source": "sound/weapons/dryfire1.wav", + "destination": "assets/audio/weapons/dryfire1.wav", + "sha256": "155e1c99c487607a872696c1c9316bf339bfe317907f804d487d361b234a50f0", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.17505668934240362 + }, + { + "source": "sound/items/clipinsert1.wav", + "destination": "assets/audio/items/clipinsert1.wav", + "sha256": "46edb5285edf1370ef7f52cd88e06d81391037d664a7b76ed991cd0e5929bd09", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.5688888888888889 + }, + { + "source": "sound/items/cliprelease1.wav", + "destination": "assets/audio/items/cliprelease1.wav", + "sha256": "8ca78a16e1aded33bd837abc4369bb91bcc4d31e967285183a5586f7d40539fb", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.2693424036281179 + }, + { + "source": "sound/items/guncock1.wav", + "destination": "assets/audio/items/guncock1.wav", + "sha256": "ed96f81f5b097bf2f28e988fea77a1c2f878c2c1c59682d549f06b418eb70d6b", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.5624036281179138 + }, + { + "source": "sound/zombie/claw_miss1.wav", + "destination": "assets/audio/zombie/claw_miss1.wav", + "sha256": "6bf27d007a2b5798593b8da66489acb0f458ada8535f276cb3fd0272a43caef2", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.2746485260770975 + }, + { + "source": "sound/zombie/claw_miss2.wav", + "destination": "assets/audio/zombie/claw_miss2.wav", + "sha256": "4c94101632fdd7898dc824ca4a583d0a7c937e5d60c8017f8f7c66e5aad0d263", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.31247165532879817 + }, + { + "source": "sound/zombie/claw_strike1.wav", + "destination": "assets/audio/zombie/claw_strike1.wav", + "sha256": "fcee49297f976e21bc5cea114737490a2a004cad9cd426ac3d8a6ac6b304e352", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.8214512471655329 + }, + { + "source": "sound/zombie/claw_strike2.wav", + "destination": "assets/audio/zombie/claw_strike2.wav", + "sha256": "2bf234b696153a36b52c0b50dae60fa02dd1e3cb0348f6e298362e37890f1dd8", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.9551473922902495 + }, + { + "source": "sound/zombie/claw_strike3.wav", + "destination": "assets/audio/zombie/claw_strike3.wav", + "sha256": "2453fa506fe2c3fdbe592c70f4477217f29225fc3a3883d41c4f216bdba57a57", + "channels": 1, + "bits": 8, + "rate": 22050, + "duration": 0.6505215419501134 + }, + { + "source": "sound/zombie/zo_alert10.wav", + "destination": "assets/audio/zombie/zo_alert10.wav", + "sha256": "c7beff9da12ee5f3f0786017d85d38fc17aeeb56f6647b7b8cbf75832a2cb1d0", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 1.0554195011337868 + }, + { + "source": "sound/zombie/zo_alert20.wav", + "destination": "assets/audio/zombie/zo_alert20.wav", + "sha256": "473f1302fdd687882ed8c3908b4fe28f3e3382a3bed6f29148aea70744c562d0", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.6281179138321995 + }, + { + "source": "sound/zombie/zo_alert30.wav", + "destination": "assets/audio/zombie/zo_alert30.wav", + "sha256": "0148ce5933f0b025b3367ffe13555cdc5f89c2872c8ba8eb6a6fccd21ffc6a02", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.6054421768707483 + }, + { + "source": "sound/zombie/zo_attack1.wav", + "destination": "assets/audio/zombie/zo_attack1.wav", + "sha256": "c6facd042775c8ef9c4c4c5a91f8b301a479ed2e7969a69ec7775b8431562805", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 2.5126530612244897 + }, + { + "source": "sound/zombie/zo_attack2.wav", + "destination": "assets/audio/zombie/zo_attack2.wav", + "sha256": "daaa3473f67e418464a4188287fdf50161cade05fcccb175c2605f3176beda17", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 1.0940589569160997 + }, + { + "source": "sound/zombie/zo_idle1.wav", + "destination": "assets/audio/zombie/zo_idle1.wav", + "sha256": "acf1be1005fb623557a7744a7dda6bf7ebd74dca170ad70ff9c8a7d60ab88ed2", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.532063492063492 + }, + { + "source": "sound/zombie/zo_idle2.wav", + "destination": "assets/audio/zombie/zo_idle2.wav", + "sha256": "c7d1a63201f640d79416891057e3ac2164a76010215c0c9837cedac0ac013876", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.2384580498866213 + }, + { + "source": "sound/zombie/zo_idle3.wav", + "destination": "assets/audio/zombie/zo_idle3.wav", + "sha256": "a101d7f3799cfee122f598f0083dfc0d983b4fab49367d5bf7b683390a3931ac", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.4160544217687075 + }, + { + "source": "sound/zombie/zo_idle4.wav", + "destination": "assets/audio/zombie/zo_idle4.wav", + "sha256": "d7d31fa5859f6549021ca727c876d48fa044030aca765be944ebe2b05eab4076", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.6985034013605442 + }, + { + "source": "sound/zombie/zo_pain1.wav", + "destination": "assets/audio/zombie/zo_pain1.wav", + "sha256": "e7ac56b93ce160283001823ef1a85160bb5a878860b37e4ed451bea2baefdd66", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 1.160453514739229 + }, + { + "source": "sound/zombie/zo_pain2.wav", + "destination": "assets/audio/zombie/zo_pain2.wav", + "sha256": "de7e75a9e2a10f7cd459c047a1aee1770dac527dcd96d5cb371a0c3724d5265e", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.7639002267573696 + }, + { + "source": "sound/player/pl_metal1.wav", + "destination": "assets/audio/player/pl_metal1.wav", + "sha256": "4b0e2c21018cc89e25cc68ae5a8a74042cd59f4b0034e12adebe8365592d586b", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.582312925170068 + }, + { + "source": "sound/player/pl_metal2.wav", + "destination": "assets/audio/player/pl_metal2.wav", + "sha256": "db8c0c6cd03196daf2912f0579a45f5023d48c8e1f7fed95b02e31d39030eb4b", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.5522902494331066 + }, + { + "source": "sound/player/pl_metal3.wav", + "destination": "assets/audio/player/pl_metal3.wav", + "sha256": "7cba90d8900b85f53870902438dd668b95ad54abebb76b849d252bf3e58ce20a", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.606077097505669 + }, + { + "source": "sound/player/pl_metal4.wav", + "destination": "assets/audio/player/pl_metal4.wav", + "sha256": "8e91e7e7e5ff18b8891c9d8ab5196ad6f7ff451ca38d4311d0304b2fc6c32626", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.5500226757369614 + }, + { + "source": "sound/debris/bustglass1.wav", + "destination": "assets/audio/debris/bustglass1.wav", + "sha256": "0fc12e9e858ab2ed24729561d93812d654e2623adfd7f93960c843f1f7d1e205", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.76 + }, + { + "source": "sound/debris/bustglass2.wav", + "destination": "assets/audio/debris/bustglass2.wav", + "sha256": "05a831f658b1e24e709b51e36f27bd4db4ae817dddb7b860b06a58d929b6bd92", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 0.9583673469387755 + }, + { + "source": "sound/debris/bustglass3.wav", + "destination": "assets/audio/debris/bustglass3.wav", + "sha256": "08a83da0959cb5f9193f2874d27836559c3ab8684df71bd61ea00a641a3d446f", + "channels": 1, + "bits": 8, + "rate": 11025, + "duration": 2.6071655328798187 + }, + { + "source": "sound/player/pl_pain7.wav", + "destination": "assets/audio/player/pl_pain7.wav", + "sha256": "71f49330df2247cabd87a78a6cddf247900d79b363747e5092c75c54ef9e6b04", + "event": "player death (original pain vocal)" + }, + { + "source": "models/grenade.mdl", + "destination": "assets/weapons/grenade.mdl", + "sha256": "bc6126523037ed6442fdd44c0ed9f69e2b024bdcc8e2a60dbcc39f97bd847ec9", + "textures": 2 + }, + { + "source": "sound/weapons/explode3.wav", + "destination": "assets/audio/weapons/explode3.wav", + "sha256": "e5c88796ffa1bd7157ac29aaccff54d767fe67bcada52d0f5f488a03dd17d9a6" + }, + { + "source": "sound/weapons/explode4.wav", + "destination": "assets/audio/weapons/explode4.wav", + "sha256": "f6e09e5bfe44a6ea0d0c276e8b632f7911dcbece62329851a722a53a48be42c4" + }, + { + "source": "sound/weapons/explode5.wav", + "destination": "assets/audio/weapons/explode5.wav", + "sha256": "a39a4098aedd116a1688196a3338f03ea83f781df726bce184a03845a54f808d" + } +] diff --git a/assets/textures/ceiling.png b/assets/textures/ceiling.png new file mode 100644 index 0000000..c8ec9a3 Binary files /dev/null and b/assets/textures/ceiling.png differ diff --git a/assets/textures/ceiling.png.import b/assets/textures/ceiling.png.import new file mode 100644 index 0000000..b2b7d3c --- /dev/null +++ b/assets/textures/ceiling.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://usfsatrehju" +path="res://.godot/imported/ceiling.png-4df6872daceb2f13c67d2d3b47380825.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/ceiling.png" +dest_files=["res://.godot/imported/ceiling.png-4df6872daceb2f13c67d2d3b47380825.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/floor.png b/assets/textures/floor.png new file mode 100644 index 0000000..fbb8cbc Binary files /dev/null and b/assets/textures/floor.png differ diff --git a/assets/textures/floor.png.import b/assets/textures/floor.png.import new file mode 100644 index 0000000..1930694 --- /dev/null +++ b/assets/textures/floor.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dvsqowqr6mf80" +path="res://.godot/imported/floor.png-bc19f2b688ab458014752317127027f7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/floor.png" +dest_files=["res://.godot/imported/floor.png-bc19f2b688ab458014752317127027f7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/wall.png b/assets/textures/wall.png new file mode 100644 index 0000000..bd3f961 Binary files /dev/null and b/assets/textures/wall.png differ diff --git a/assets/textures/wall.png.import b/assets/textures/wall.png.import new file mode 100644 index 0000000..992e1c4 --- /dev/null +++ b/assets/textures/wall.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cdfhikteb0e40" +path="res://.godot/imported/wall.png-35b80c25b8f7299ed1631f28676b0e6a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/wall.png" +dest_files=["res://.godot/imported/wall.png-35b80c25b8f7299ed1631f28676b0e6a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_00.png b/assets/textures/zombie_00.png new file mode 100644 index 0000000..602f14e Binary files /dev/null and b/assets/textures/zombie_00.png differ diff --git a/assets/textures/zombie_00.png.import b/assets/textures/zombie_00.png.import new file mode 100644 index 0000000..22e4ae7 --- /dev/null +++ b/assets/textures/zombie_00.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2ld42idtd24m" +path="res://.godot/imported/zombie_00.png-6550d19ec966d284cb6b2aea7443ac12.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_00.png" +dest_files=["res://.godot/imported/zombie_00.png-6550d19ec966d284cb6b2aea7443ac12.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_01.png b/assets/textures/zombie_01.png new file mode 100644 index 0000000..0a6581e Binary files /dev/null and b/assets/textures/zombie_01.png differ diff --git a/assets/textures/zombie_01.png.import b/assets/textures/zombie_01.png.import new file mode 100644 index 0000000..33f5f5c --- /dev/null +++ b/assets/textures/zombie_01.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://df66qa0p7ws74" +path="res://.godot/imported/zombie_01.png-9311a01efd9d836ffba26de14654e0aa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_01.png" +dest_files=["res://.godot/imported/zombie_01.png-9311a01efd9d836ffba26de14654e0aa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_02.png b/assets/textures/zombie_02.png new file mode 100644 index 0000000..7096ab1 Binary files /dev/null and b/assets/textures/zombie_02.png differ diff --git a/assets/textures/zombie_02.png.import b/assets/textures/zombie_02.png.import new file mode 100644 index 0000000..cea8851 --- /dev/null +++ b/assets/textures/zombie_02.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dulbidbknfye0" +path="res://.godot/imported/zombie_02.png-7752fe8d35e6d333891214b434c33168.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_02.png" +dest_files=["res://.godot/imported/zombie_02.png-7752fe8d35e6d333891214b434c33168.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_03.png b/assets/textures/zombie_03.png new file mode 100644 index 0000000..3e0cb94 Binary files /dev/null and b/assets/textures/zombie_03.png differ diff --git a/assets/textures/zombie_03.png.import b/assets/textures/zombie_03.png.import new file mode 100644 index 0000000..3487517 --- /dev/null +++ b/assets/textures/zombie_03.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://2a77fvfc7drv" +path="res://.godot/imported/zombie_03.png-f230870cda45dc6f2ecbc5cd54afabcb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_03.png" +dest_files=["res://.godot/imported/zombie_03.png-f230870cda45dc6f2ecbc5cd54afabcb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_04.png b/assets/textures/zombie_04.png new file mode 100644 index 0000000..e0e3f0f Binary files /dev/null and b/assets/textures/zombie_04.png differ diff --git a/assets/textures/zombie_04.png.import b/assets/textures/zombie_04.png.import new file mode 100644 index 0000000..2cbed5e --- /dev/null +++ b/assets/textures/zombie_04.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://psc7ywgb637w" +path="res://.godot/imported/zombie_04.png-e19fd097c32ac2669cc62410a0ecaaa7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_04.png" +dest_files=["res://.godot/imported/zombie_04.png-e19fd097c32ac2669cc62410a0ecaaa7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_05.png b/assets/textures/zombie_05.png new file mode 100644 index 0000000..5bd85b5 Binary files /dev/null and b/assets/textures/zombie_05.png differ diff --git a/assets/textures/zombie_05.png.import b/assets/textures/zombie_05.png.import new file mode 100644 index 0000000..2db3506 --- /dev/null +++ b/assets/textures/zombie_05.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwq3okic2sipw" +path="res://.godot/imported/zombie_05.png-b4080b43c14ddd7ea1d1290b9b6056f4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_05.png" +dest_files=["res://.godot/imported/zombie_05.png-b4080b43c14ddd7ea1d1290b9b6056f4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_06.png b/assets/textures/zombie_06.png new file mode 100644 index 0000000..71aea76 Binary files /dev/null and b/assets/textures/zombie_06.png differ diff --git a/assets/textures/zombie_06.png.import b/assets/textures/zombie_06.png.import new file mode 100644 index 0000000..22721ef --- /dev/null +++ b/assets/textures/zombie_06.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c6okbtkagh2vp" +path="res://.godot/imported/zombie_06.png-439d5ef8cf9b12e0e7aae3c774625ba3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_06.png" +dest_files=["res://.godot/imported/zombie_06.png-439d5ef8cf9b12e0e7aae3c774625ba3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_07.png b/assets/textures/zombie_07.png new file mode 100644 index 0000000..5188bab Binary files /dev/null and b/assets/textures/zombie_07.png differ diff --git a/assets/textures/zombie_07.png.import b/assets/textures/zombie_07.png.import new file mode 100644 index 0000000..5f606c7 --- /dev/null +++ b/assets/textures/zombie_07.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b6812tee7a6ws" +path="res://.godot/imported/zombie_07.png-757df77e585a7c37a6a5c60099fbbe1e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_07.png" +dest_files=["res://.godot/imported/zombie_07.png-757df77e585a7c37a6a5c60099fbbe1e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_08.png b/assets/textures/zombie_08.png new file mode 100644 index 0000000..dbd8fed Binary files /dev/null and b/assets/textures/zombie_08.png differ diff --git a/assets/textures/zombie_08.png.import b/assets/textures/zombie_08.png.import new file mode 100644 index 0000000..ea489a9 --- /dev/null +++ b/assets/textures/zombie_08.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dthxywoi6038u" +path="res://.godot/imported/zombie_08.png-0c1ee8c0e1a05b520a3f034b313bb487.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_08.png" +dest_files=["res://.godot/imported/zombie_08.png-0c1ee8c0e1a05b520a3f034b313bb487.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_09.png b/assets/textures/zombie_09.png new file mode 100644 index 0000000..7364c45 Binary files /dev/null and b/assets/textures/zombie_09.png differ diff --git a/assets/textures/zombie_09.png.import b/assets/textures/zombie_09.png.import new file mode 100644 index 0000000..a0dcedf --- /dev/null +++ b/assets/textures/zombie_09.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bhwsaow6i6gjn" +path="res://.godot/imported/zombie_09.png-09d1728937634536bfe3e08af05f4675.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_09.png" +dest_files=["res://.godot/imported/zombie_09.png-09d1728937634536bfe3e08af05f4675.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_10.png b/assets/textures/zombie_10.png new file mode 100644 index 0000000..9680b76 Binary files /dev/null and b/assets/textures/zombie_10.png differ diff --git a/assets/textures/zombie_10.png.import b/assets/textures/zombie_10.png.import new file mode 100644 index 0000000..48fe09f --- /dev/null +++ b/assets/textures/zombie_10.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dorvqsbnqbudd" +path="res://.godot/imported/zombie_10.png-ef962c7cb52e9482475c71e665627819.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_10.png" +dest_files=["res://.godot/imported/zombie_10.png-ef962c7cb52e9482475c71e665627819.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_11.png b/assets/textures/zombie_11.png new file mode 100644 index 0000000..a941e66 Binary files /dev/null and b/assets/textures/zombie_11.png differ diff --git a/assets/textures/zombie_11.png.import b/assets/textures/zombie_11.png.import new file mode 100644 index 0000000..eae50ea --- /dev/null +++ b/assets/textures/zombie_11.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://65opb4wgvops" +path="res://.godot/imported/zombie_11.png-46e5702faf68dc9d5a47fad80d5396ab.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_11.png" +dest_files=["res://.godot/imported/zombie_11.png-46e5702faf68dc9d5a47fad80d5396ab.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_12.png b/assets/textures/zombie_12.png new file mode 100644 index 0000000..23ddd9b Binary files /dev/null and b/assets/textures/zombie_12.png differ diff --git a/assets/textures/zombie_12.png.import b/assets/textures/zombie_12.png.import new file mode 100644 index 0000000..4af3cf0 --- /dev/null +++ b/assets/textures/zombie_12.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://df1xalcnoddek" +path="res://.godot/imported/zombie_12.png-d023fbb12d5649000e65a3112a067d1d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_12.png" +dest_files=["res://.godot/imported/zombie_12.png-d023fbb12d5649000e65a3112a067d1d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_13.png b/assets/textures/zombie_13.png new file mode 100644 index 0000000..ffdc512 Binary files /dev/null and b/assets/textures/zombie_13.png differ diff --git a/assets/textures/zombie_13.png.import b/assets/textures/zombie_13.png.import new file mode 100644 index 0000000..69c64ca --- /dev/null +++ b/assets/textures/zombie_13.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dsgxtp3lomlql" +path="res://.godot/imported/zombie_13.png-f28e8e0430e0ab99c50ccd173076d140.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_13.png" +dest_files=["res://.godot/imported/zombie_13.png-f28e8e0430e0ab99c50ccd173076d140.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_14.png b/assets/textures/zombie_14.png new file mode 100644 index 0000000..0d5885b Binary files /dev/null and b/assets/textures/zombie_14.png differ diff --git a/assets/textures/zombie_14.png.import b/assets/textures/zombie_14.png.import new file mode 100644 index 0000000..4baa9d5 --- /dev/null +++ b/assets/textures/zombie_14.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://kuwye3umwch8" +path="res://.godot/imported/zombie_14.png-795e94b7eb6a6fac68b9125c976b3dcf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_14.png" +dest_files=["res://.godot/imported/zombie_14.png-795e94b7eb6a6fac68b9125c976b3dcf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_15.png b/assets/textures/zombie_15.png new file mode 100644 index 0000000..436e363 Binary files /dev/null and b/assets/textures/zombie_15.png differ diff --git a/assets/textures/zombie_15.png.import b/assets/textures/zombie_15.png.import new file mode 100644 index 0000000..5c8638d --- /dev/null +++ b/assets/textures/zombie_15.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://brn7bxdsccp4l" +path="res://.godot/imported/zombie_15.png-b1bfceb3fdd00dc1763451d568fcaffc.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_15.png" +dest_files=["res://.godot/imported/zombie_15.png-b1bfceb3fdd00dc1763451d568fcaffc.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_16.png b/assets/textures/zombie_16.png new file mode 100644 index 0000000..8e566d5 Binary files /dev/null and b/assets/textures/zombie_16.png differ diff --git a/assets/textures/zombie_16.png.import b/assets/textures/zombie_16.png.import new file mode 100644 index 0000000..dbc2fbc --- /dev/null +++ b/assets/textures/zombie_16.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2mveedxr877n" +path="res://.godot/imported/zombie_16.png-103576ee0799c5b54d0f275a170f2ca5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_16.png" +dest_files=["res://.godot/imported/zombie_16.png-103576ee0799c5b54d0f275a170f2ca5.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_17.png b/assets/textures/zombie_17.png new file mode 100644 index 0000000..571efa0 Binary files /dev/null and b/assets/textures/zombie_17.png differ diff --git a/assets/textures/zombie_17.png.import b/assets/textures/zombie_17.png.import new file mode 100644 index 0000000..1e0407d --- /dev/null +++ b/assets/textures/zombie_17.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c7o283ypy8al8" +path="res://.godot/imported/zombie_17.png-ba26b09604899cedc3b0fdb678d8d7fb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_17.png" +dest_files=["res://.godot/imported/zombie_17.png-ba26b09604899cedc3b0fdb678d8d7fb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/textures/zombie_18.png b/assets/textures/zombie_18.png new file mode 100644 index 0000000..83a7770 Binary files /dev/null and b/assets/textures/zombie_18.png differ diff --git a/assets/textures/zombie_18.png.import b/assets/textures/zombie_18.png.import new file mode 100644 index 0000000..b4f6c1d --- /dev/null +++ b/assets/textures/zombie_18.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bgvrvopidmox5" +path="res://.godot/imported/zombie_18.png-4e7495d5e6acdf8743c07055aee6e47b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/textures/zombie_18.png" +dest_files=["res://.godot/imported/zombie_18.png-4e7495d5e6acdf8743c07055aee6e47b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/weapons/grenade.mdl b/assets/weapons/grenade.mdl new file mode 100644 index 0000000..274b54f Binary files /dev/null and b/assets/weapons/grenade.mdl differ diff --git a/assets/weapons/grenade.mdl.import b/assets/weapons/grenade.mdl.import new file mode 100644 index 0000000..9708f32 --- /dev/null +++ b/assets/weapons/grenade.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://djsgpk6t1f4ll" +path="res://.godot/imported/grenade.mdl-71bef19510bc532a0bcde74179bce7c9.scn" + +[deps] + +source_file="res://assets/weapons/grenade.mdl" +dest_files=["res://.godot/imported/grenade.mdl-71bef19510bc532a0bcde74179bce7c9.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/weapons/grenade.scn b/assets/weapons/grenade.scn new file mode 100644 index 0000000..d309f61 Binary files /dev/null and b/assets/weapons/grenade.scn differ diff --git a/assets/weapons/p_9mmar.mdl b/assets/weapons/p_9mmar.mdl new file mode 100644 index 0000000..c971ab2 Binary files /dev/null and b/assets/weapons/p_9mmar.mdl differ diff --git a/assets/weapons/p_9mmar.mdl.import b/assets/weapons/p_9mmar.mdl.import new file mode 100644 index 0000000..4465a67 --- /dev/null +++ b/assets/weapons/p_9mmar.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://bitp7ft4fat8l" +path="res://.godot/imported/p_9mmar.mdl-0718ed89cee8d075bb3f70beaabf1182.scn" + +[deps] + +source_file="res://assets/weapons/p_9mmar.mdl" +dest_files=["res://.godot/imported/p_9mmar.mdl-0718ed89cee8d075bb3f70beaabf1182.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/weapons/v_9mmar.mdl b/assets/weapons/v_9mmar.mdl new file mode 100644 index 0000000..6bfa3e0 Binary files /dev/null and b/assets/weapons/v_9mmar.mdl differ diff --git a/assets/weapons/v_9mmar.mdl.import b/assets/weapons/v_9mmar.mdl.import new file mode 100644 index 0000000..3d0c3f9 --- /dev/null +++ b/assets/weapons/v_9mmar.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://bkqbe6o7njgxy" +path="res://.godot/imported/v_9mmar.mdl-a81471d9f072d6dca2ce20874c986302.scn" + +[deps] + +source_file="res://assets/weapons/v_9mmar.mdl" +dest_files=["res://.godot/imported/v_9mmar.mdl-a81471d9f072d6dca2ce20874c986302.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/weapons/v_9mmar.scn b/assets/weapons/v_9mmar.scn new file mode 100644 index 0000000..428e954 Binary files /dev/null and b/assets/weapons/v_9mmar.scn differ diff --git a/assets/weapons/w_9mmar.mdl b/assets/weapons/w_9mmar.mdl new file mode 100644 index 0000000..cb20186 Binary files /dev/null and b/assets/weapons/w_9mmar.mdl differ diff --git a/assets/weapons/w_9mmar.mdl.import b/assets/weapons/w_9mmar.mdl.import new file mode 100644 index 0000000..3affa4c --- /dev/null +++ b/assets/weapons/w_9mmar.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://c3obasxfsmlqq" +path="res://.godot/imported/w_9mmar.mdl-019ec1b0d869c2946e727565995c8e55.scn" + +[deps] + +source_file="res://assets/weapons/w_9mmar.mdl" +dest_files=["res://.godot/imported/w_9mmar.mdl-019ec1b0d869c2946e727565995c8e55.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/weapons/w_9mmar.scn b/assets/weapons/w_9mmar.scn new file mode 100644 index 0000000..fb9e68a Binary files /dev/null and b/assets/weapons/w_9mmar.scn differ diff --git a/assets/zombies/zombie.mdl b/assets/zombies/zombie.mdl new file mode 100755 index 0000000..2b4a6ec Binary files /dev/null and b/assets/zombies/zombie.mdl differ diff --git a/assets/zombies/zombie.mdl.import b/assets/zombies/zombie.mdl.import new file mode 100644 index 0000000..7108f2d --- /dev/null +++ b/assets/zombies/zombie.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://1prmv1p8ow4f" +path="res://.godot/imported/zombie.mdl-d906e37581a38c818d6d42ba6e40c16e.scn" + +[deps] + +source_file="res://assets/zombies/zombie.mdl" +dest_files=["res://.godot/imported/zombie.mdl-d906e37581a38c818d6d42ba6e40c16e.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/zombies/zombie_textured.mdl b/assets/zombies/zombie_textured.mdl new file mode 100644 index 0000000..c2f5aa5 Binary files /dev/null and b/assets/zombies/zombie_textured.mdl differ diff --git a/assets/zombies/zombie_textured.mdl.import b/assets/zombies/zombie_textured.mdl.import new file mode 100644 index 0000000..970df7a --- /dev/null +++ b/assets/zombies/zombie_textured.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://b7y3p1hlurwwt" +path="res://.godot/imported/zombie_textured.mdl-e0bbc3f29995d7f67c65b7463a8e63be.scn" + +[deps] + +source_file="res://assets/zombies/zombie_textured.mdl" +dest_files=["res://.godot/imported/zombie_textured.mdl-e0bbc3f29995d7f67c65b7463a8e63be.scn"] + +[params] + +scale_factor=0.025 diff --git a/assets/zombies/zombie_textured.scn b/assets/zombies/zombie_textured.scn new file mode 100644 index 0000000..dda271c Binary files /dev/null and b/assets/zombies/zombie_textured.scn differ diff --git a/assets/zombies/zombiet.mdl b/assets/zombies/zombiet.mdl new file mode 100755 index 0000000..942bc66 Binary files /dev/null and b/assets/zombies/zombiet.mdl differ diff --git a/assets/zombies/zombiet.mdl.import b/assets/zombies/zombiet.mdl.import new file mode 100644 index 0000000..5aa9d50 --- /dev/null +++ b/assets/zombies/zombiet.mdl.import @@ -0,0 +1,16 @@ +[remap] + +importer="goldsrc.mdl" +importer_version=2 +type="PackedScene" +uid="uid://ddvh548teagix" +path="res://.godot/imported/zombiet.mdl-4a075fb2bbf6efbdad30d722ffef9e6b.scn" + +[deps] + +source_file="res://assets/zombies/zombiet.mdl" +dest_files=["res://.godot/imported/zombiet.mdl-4a075fb2bbf6efbdad30d722ffef9e6b.scn"] + +[params] + +scale_factor=0.025 diff --git a/docs/DEATH-EFFECTS.md b/docs/DEATH-EFFECTS.md new file mode 100644 index 0000000..bfd8655 --- /dev/null +++ b/docs/DEATH-EFFECTS.md @@ -0,0 +1,21 @@ +# Death and blood effects + +- Zombie idle/attack/death use AudioStreamPlayer3D with attenuation (unit_size 4m, max_distance 30m). +- No original `zo_death*.wav` is available. `zombie/zo_pain2.wav` is deliberately used for the death event; living hurt uses `zo_pain1.wav`. +- Player death uses the original Steam `valve/sound/player/pl_pain7.wav`, copied unchanged. This is a pain vocal assigned to death, not a dedicated death sample. +- Corpses remain until scene restart. Collision and AI stop, death animation retains its final pose. Window-entry tweens are cancelled on death. +- Blood: GPUParticles3D burst and original generated RGBA decal mask. Decals project downward onto static world layer 1. Characters/viewmodel are on visual layer 2. At most 160 stains retained; bodies have no automatic deletion. +- Player death is idempotent: gun hidden, shooting/mouse-look blocked, camera falls to 0.22m and rolls; black fade finishes after 2.5s. Restart label is above black overlay. R reloads scene. + +## Verification + +Use Godot 4.7.1: + +``` +godot --headless --path . --script res://tools/test_death_effects.gd +godot --path . --script res://tools/test_death_effects.gd +``` + +Tests assert corpse survival beyond old four-second lifetime, collision off, one decal, death only once, disabled shooting, camera at floor height and fade alpha 1. Graphical test writes `/tmp/zombie-blood.png`, `/tmp/zombie-corpse.png`, `/tmp/zombie-death-fade.png`. + +Valve assets are local extracted copyrighted game data, not licensed for redistribution by this project. diff --git a/docs/GRENADES-WAVES.md b/docs/GRENADES-WAVES.md new file mode 100644 index 0000000..ef34375 --- /dev/null +++ b/docs/GRENADES-WAVES.md @@ -0,0 +1,22 @@ +# Grenades and growing waves + +- Right mouse button: throw while mouse is captured and player alive. +- Cooldown: 15 seconds from launch, displayed in HUD; cannot be bypassed by repeated clicks. +- Physical RigidBody3D: sphere radius 0.12m, mass0.4kg, bounce0.45, friction0.65, gravity, CCD; launch velocity forward12m/s + up3m/s + player velocity. Thrower excluded from projectile collision but not blast damage. +- Fuse: four physics seconds from spawn; one deferred detonation. +- Blast radius5m, maximum damage160 diminishing linearly with distance; world collision ray blocks damage through walls. Includes thrower if alive. CPU particles/flash and original Half-Life explode3–5.wav. +- Original Valve grenade.mdl contains two embedded textures; copied unchanged and exported to native grenade.scn for Web. Provenance in assets/half_life_manifest.json; extraction script tools/extract_grenade_assets.py. +- Wave size: 4 + 2*wave (6,8,10,...), no old24 limit. Next wave three seconds after all enemies die and spawning completes. Scheduling guard prevents duplicate wave timers. Corpses remain. + +## Tests + +Godot4.7.1: + +``` +--headless --path . --script res://tools/test_grenade.gd +--headless --path . --script res://tools/test_wave_growth.gd +``` + +Grenade test PASS: movement/gravity, fuse3.997s, actual15s cooldown, visible/blocked/out-of-radius damage, self-damage and formula26 enemies for wave11. Wave test PASS: six zombies cleared, single next wave spawns eight, six corpses retained; an existing shutdown resource warning remains in the wave test, not a gameplay error. + +Published Web release4e181f0fe5d02cc9 at https://enne2.net/pub/zombie-fps/; gzip+immutable cache retained. Browser right-click test showed HUD countdown10s after throw. Backup prior root HTML: /home/debian/zombie-fps-backups/index-before-grenade.html. Previous releases kept; no nginx changes. diff --git a/docs/MUSIC.md b/docs/MUSIC.md new file mode 100644 index 0000000..608ff30 --- /dev/null +++ b/docs/MUSIC.md @@ -0,0 +1,67 @@ +# Soundtrack — industrial metal loop + +Endless survival loop played from the first frame, at `-11 dB`, independent of gameplay state. + +## Source + +Generated locally (2026-10-10) with **YuE2-3B Q8_0** through the audio.cpp CUDA build for the +Tesla V100 (`~/dev/audio.cpp`, `cmake -DCMAKE_CUDA_ARCHITECTURES=70`), seed 31337, 16 steps, +`cot=melody` + an external ABC score written for this track (K:Em Phrygian, `Q:1/4=168`, +96 bars; the model rendered it at a perceived 112.5 BPM). Style prompt: instrumental +industrial metal, Nine Inch Nails meets the Quake 2 soundtrack, machine drums, palm-muted +downtuned guitars, atonal synth stabs, no vocals. + +`--lyrics "[instrumental]"` does **not** suppress singing (verified: male shouts 0:52-1:17 and +1:37-2:35). The instrumental was therefore obtained by **HTDemucs** vocal removal +(`--task sep --family htdemucs`, input resampled to 44.1 kHz, RTF 0.049) and remixed as +`drums + bass + other`. + +Working files: `~/yue2-out/zombie-industrial/`. + +## Loop construction + +The track has a structural repeat (bars 24-30 ≈ 46-52, spectral similarity 0.95), so the loop +splices at matching material instead of cutting arbitrarily: + +- region 51.37 s → 108.97 s of the instrumental master = 27 bars (bar = 2.1333 s, 112.5 BPM); +- rotation by one tail segment plus a **0.60 s** equal-power crossfade; +- result: **57.60 s**, seam dip ≤ 1.6 dB in 20 ms windows, sample step 0.04 (inaudible); +- perceptual check on a 3 s + 3 s excerpt around the seam: no click, no level hole, natural + continuation. + +A longer variant (132 s) was rejected: its crossfade cancels (a -48 dB hole) because the two +overlapped sections are out of phase. + +## Asset and import + +`assets/audio/music/zombie_industrial_loop.ogg` — Ogg Vorbis q6, 48 kHz stereo, 1.0 MB, +-14 LUFS / -1.0 dBTP. Import: `loop=true`, `bpm=112.5`, `beat_count=108`, `bar_beats=4`. + +The Web staging step (`tools/prepare_web.py`) copies the asset but ignores `*.import`, and the +staging import regenerates it with `loop=false`. `main.gd` therefore sets `stream.loop = true` +at runtime: keep that line, it is what makes the browser build loop. + +## Runtime wiring + +`scripts/main.gd`: `_setup_music()` creates the `AudioStreamPlayer` named `Music` (group +`music`), `_start_music()` starts it once, `_restart_music()` restarts it if the loop flag is +ever absent. On Web the start is deferred to the click that captures the mouse, because +browsers only allow audio after a user gesture. Music keeps playing while the player is dead; +`R` reloads the scene and the track restarts with it. + +## Verification + +``` +godot --headless --path . --script res://tools/test_music.gd +godot --display-driver headless --audio-driver PulseAudio --path . --script res://tools/test_music.gd +godot --headless --path . -- --smoke +``` + +Both runs PASS: node present, `loop=true`, length 57.60 s, volume -11 dB, playback advancing +(confirmed on the real PulseAudio device, not only the Dummy driver), music still playing after +player death. Grenade, wave, death and damage-feedback tests also pass unchanged. + +## License + +YuE2-3B weights are **CC BY-NC 4.0** (non-commercial): this track is for personal/experimental +use. It is an AI-generated render, not a recording of a third-party work. diff --git a/docs/WEB-DEPLOY.md b/docs/WEB-DEPLOY.md new file mode 100644 index 0000000..cc99474 --- /dev/null +++ b/docs/WEB-DEPLOY.md @@ -0,0 +1,61 @@ +# Web build and compressed deployment + +URL: https://enne2.net/pub/zombie-fps/ +Server: debian@enne2.net (bastion). +Web root: /home/debian/public_html/zombie-fps/. + +## Build + +Use installed Godot 4.7.1 executable, not the Godot 4.3 on PATH: + +```bash +G="$HOME/.local/opt/godot-4.7.1/Godot_v4.7.1-stable_linux.x86_64" +"$G" --headless --path /home/enne2/dev/zombie-fps --script res://tools/export_web_assets.gd +python3 /home/enne2/dev/zombie-fps/tools/prepare_web.py +"$G" --headless --path /home/enne2/dev/zombie-fps/build/web-stage --import +mkdir -p /home/enne2/dev/zombie-fps/build/web +"$G" --headless --path /home/enne2/dev/zombie-fps/build/web-stage --export-release Web /home/enne2/dev/zombie-fps/build/web/index.html +python3 /home/enne2/dev/zombie-fps/tools/package_web_release.py +``` + +Staging excludes native addons, source MDL assets, unused world weapon scenes, extraction tools. Runtime uses native SCN models. Web renderer is Compatibility, with CPU particles and floor blood patches instead of Forward+ decals. Browser click starts waves and captures mouse. Single-thread Web export does not require cross-origin isolation server headers. + +## Versioned compression + +`package_web_release.py` computes a content hash, writes build/web-deploy/releases// and gzip level 9 sidecars, verifying decompression. Original and gzip mtimes match. Root index.html uses a base href to the immutable release directory; original fileSizes remain uncompressed sizes as expected by Godot. Originals remain available for clients rejecting gzip. + +Upload new immutable directory first, never overwrite an existing hash. Then atomically replace root index.html. Keep previous releases for open tabs/rollback. Do not delete them during routine deployment. + +Nginx snippet source: tools/nginx-zombie-fps.conf. Installed at /etc/nginx/snippets/zombie-fps.conf and included only by enne2.net's HTTPS server in /etc/nginx/conf.d/enne2.conf. gzip_static on, Vary Accept-Encoding, correct application/wasm MIME. Root HTML no-cache; versioned files public,max-age=31536000,immutable. Other /pub/ sites unchanged. No Brotli module installed. + +Protected configuration backup: /etc/nginx/zombie-fps-backup-9d706e724b0a/. +Original root HTML backup: /home/debian/zombie-fps-backups/index-before-compression.html. +Tools/apply_nginx_compression.py validates nginx -t, reloads, and restores config on failure. Always obtain approval for server changes. + +## Verified release 7d3c1fb9d96da215 + +- Main text/binary payloads: 42,303,791 -> 11,035,976 bytes (gzip, ~74% reduction). +- WASM: 39,513,091 -> 10,074,664 bytes. +- PCK: 2,495,216 -> 887,112 bytes. +- JS: 279,815 -> 68,657 bytes. +- HTTPS downloaded gzip responses decompress exactly to local files; hashes match. +- identity and gzip;q=0 clients receive originals. Vary header present. +- Browser first load: WASM transfer 10,074,964 bytes including resource-timing overhead; PCK 887,412. Resource durations ~513/522ms on frigate's connection (not universal startup time). +- Browser reload: WASM/PCK/JS transferSize 0; cache reads ~16/4/4ms. Godot loading overlay removed, game initialized on both loads. + +This reduces network transfer, not engine compile/initialization cost. Further gains would require a custom stripped Godot Web template; not built here. Valve copyrighted assets remain subject to redistribution rights; user explicitly stated the necessary rights for public deployment. + +## Verified release ab6054111f5919a8 (soundtrack) + +First release that ships the looping industrial metal soundtrack (see docs/MUSIC.md). + +- PCK: 2,568,000 -> 3,632,524 bytes (+1,064,524, i.e. the 1.0 MB Ogg Vorbis was the only new payload). +- All payloads: 43,441,099 -> 12,089,938 bytes gzip. +- Uploaded 14 files as a new immutable directory; remote sha256 identical to local for all 14; previous releases kept (4e181f0fe5d02cc9, 7d3c1fb9d96da215, 7d55da56077f8eee). +- Root index.html replaced by atomic rename; backup at /home/debian/zombie-fps-backups/index-before-music.html. +- HTTPS: root 200 text/html no-cache with the new base href; PCK byte-identical after gzip decode (3,632,524 bytes); versioned files application/wasm, Vary, immutable one year. +- Browser: Godot 4.7.1, WebGL 2.0 Compatibility, single-threaded; the click starts wave 1 (HUD `ONDATA 1`, 250 points) with zombies, blood decals and the damage vignette. + +Staging trap: `prepare_web.py` ignores `*.import`, so the staging project re-imports the OGG with `loop=false`. `main.gd` sets `stream.loop` at runtime; this was verified in exactly that condition by running `test_music` inside `build/web-stage` with the PulseAudio driver (loop false at load -> true after setup, playback advancing). + +Bastion disk: 1.4 GB free on / (97% used). Do not accumulate releases without checking space, and delete old ones only with approval. diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..864c14c --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,29 @@ +[preset.0] +name="Web" +platform="Web" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="addons/*,tools/*,docs/*,build/*,assets/**/*.mdl,assets/half_life_manifest.json" +export_path="build/web/index.html" +patches=PackedStringArray() +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.0.options] +custom_template/debug="" +custom_template/release="" +variant/extensions_support=false +variant/thread_support=false +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=true +html/export_icon=false +html/custom_html_shell="" +html/head_include="" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +progressive_web_app/enabled=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..9b9f848 --- /dev/null +++ b/project.godot @@ -0,0 +1,87 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="Zombie Room FPS" +config/description="FPS survival: zombie di Half-Life in una stanza con finestre e nessuna porta." +run/main_scene="res://scenes/main.tscn" +config/features=PackedStringArray("4.7", "Forward Plus") + +[animation] + +compatibility/default_parent_skeleton_in_mesh_instance_3d=true + +[display] + +window/size/viewport_width=1280 +window/size/viewport_height=720 +window/stretch/mode="canvas_items" + +[editor_plugins] + +enabled=PackedStringArray("res://addons/goldsrc/plugin.cfg") + +[input] + +move_forward={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +, Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_back={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +move_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +move_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +jump={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +shoot={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +] +} +reload={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"location":0,"echo":false,"script":null) +] +} +pause={ +"deadzone": 0.5, +"events": [Object(InputEventKey, "resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + +grenade={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton, "button_index":2)] +} + +[rendering] + +renderer/rendering_method="forward_plus" +renderer/rendering_method.web="gl_compatibility" +textures/vram_compression/import_etc2_astc=true +environment/defaults/default_clear_color=Color(0.05, 0.06, 0.08, 1) diff --git a/scenes/main.tscn b/scenes/main.tscn new file mode 100644 index 0000000..184ac35 --- /dev/null +++ b/scenes/main.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://scripts/main.gd" id="1"] + +[node name="ZombieRoom" type="Node3D"] +script = ExtResource("1") diff --git a/scripts/blood_effects.gd b/scripts/blood_effects.gd new file mode 100644 index 0000000..34625aa --- /dev/null +++ b/scripts/blood_effects.gd @@ -0,0 +1,66 @@ +extends RefCounted +const MAX_STAINS := 160 + +static func impact(world: Node3D, point: Vector3, direction: Vector3) -> void: + # CPU particles work in both Forward+ and WebGL Compatibility. + var particles := CPUParticles3D.new() + particles.name = "BloodSpray" + particles.amount = 28 + particles.lifetime = 0.65 + particles.one_shot = true + particles.explosiveness = 1.0 + particles.direction = direction.normalized() + particles.spread = 65 + particles.initial_velocity_min = 1.3 + particles.initial_velocity_max = 3.5 + particles.gravity = Vector3(0, -9.8, 0) + particles.scale_amount_min = 0.5 + particles.scale_amount_max = 1.5 + var mesh := SphereMesh.new() + mesh.radius = 0.018 + mesh.height = 0.036 + var material := StandardMaterial3D.new() + material.albedo_color = Color(0.45, 0.015, 0.025) + material.roughness = 0.35 + mesh.material = material + particles.mesh = mesh + world.add_child(particles) + particles.global_position = point + particles.emitting = true + world.get_tree().create_timer(1.3).timeout.connect(particles.queue_free) + var query := PhysicsRayQueryParameters3D.create(point + Vector3.UP * 0.05, point + Vector3.DOWN * 8, 1) + var hit := world.get_world_3d().direct_space_state.intersect_ray(query) + if hit.is_empty() or hit.normal.dot(Vector3.UP) < 0.7: + return + var width := randf_range(0.55, 1.15) + var stain: Node3D + if RenderingServer.get_current_rendering_method() == "gl_compatibility": + # WebGL has no projected decals: textured horizontal patch on floor. + var patch := MeshInstance3D.new() + var plane := PlaneMesh.new() + plane.size = Vector2(width, width) + var mat := StandardMaterial3D.new() + mat.albedo_texture = load("res://assets/effects/blood.png") + mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_DISABLED + mat.roughness = 0.9 + plane.material = mat + patch.mesh = plane + patch.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF + stain = patch + else: + var decal := Decal.new() + decal.texture_albedo = load("res://assets/effects/blood.png") + decal.size = Vector3(width, 0.25, width) + decal.cull_mask = 1 + decal.upper_fade = 0 + decal.lower_fade = 0 + stain = decal + stain.name = "BloodStain" + world.add_child(stain) + stain.global_position = hit.position + Vector3.UP * 0.012 + stain.rotation.y = randf_range(0, TAU) + stain.add_to_group("blood_stains") + var stains := world.get_tree().get_nodes_in_group("blood_stains") + if stains.size() > MAX_STAINS: + stains[0].queue_free() diff --git a/scripts/blood_effects.gd.uid b/scripts/blood_effects.gd.uid new file mode 100644 index 0000000..bd2be8d --- /dev/null +++ b/scripts/blood_effects.gd.uid @@ -0,0 +1 @@ +uid://dwi0onjbbv86u diff --git a/scripts/damage_feedback.gd b/scripts/damage_feedback.gd new file mode 100644 index 0000000..184ea4f --- /dev/null +++ b/scripts/damage_feedback.gd @@ -0,0 +1,90 @@ +extends CanvasLayer +## Local-only trauma pivot: never modifies the player's aim angles. +const SHAKE_SECONDS := 0.42 +const BLOOD_SECONDS := 1.15 +const SPLAT_COUNT := 7 +var pivot: Node3D +var shake_left := 0.0 +var blood_left := 0.0 +var intensity := 0.0 +var phase := 0.0 +var active := true +var overlay: Control +var vignette: ColorRect +var shader_material: ShaderMaterial +var splats: Array[TextureRect] = [] + +func _ready() -> void: + layer = 2 + overlay = Control.new() + overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE + add_child(overlay) + overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + vignette = ColorRect.new() + vignette.mouse_filter = Control.MOUSE_FILTER_IGNORE + shader_material = ShaderMaterial.new() + shader_material.shader = load("res://assets/effects/damage_vignette.gdshader") + vignette.material = shader_material + overlay.add_child(vignette) + vignette.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + for i in SPLAT_COUNT: + var splat := TextureRect.new() + splat.texture = load("res://assets/effects/blood.png") + splat.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + splat.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + splat.mouse_filter = Control.MOUSE_FILTER_IGNORE + splat.modulate.a = 0 + overlay.add_child(splat) + splats.append(splat) + +func hit(amount: float) -> void: + if not active or amount <= 0: + return + # Refresh instead of stacking tweens/particles: bounded cost and shake. + intensity = minf(1.0, maxf(intensity, 0.55 + amount / 100.0) + 0.12) + shake_left = SHAKE_SECONDS + blood_left = BLOOD_SECONDS + phase = randf_range(0, TAU) + var viewport_size := get_viewport().get_visible_rect().size + for i in splats.size(): + var splat := splats[i] + var size := minf(viewport_size.x, viewport_size.y) * randf_range(0.22, 0.38) + splat.size = Vector2.ONE * size + splat.pivot_offset = splat.size * 0.5 + # Keep the central reticle and HUD readable; splats lie near the edges. + var center := Vector2.ZERO + match i % 4: + 0: center = viewport_size * Vector2(randf_range(0.06, 0.17), randf_range(0.25, 0.90)) + 1: center = viewport_size * Vector2(randf_range(0.83, 0.95), randf_range(0.25, 0.90)) + 2: center = viewport_size * Vector2(randf_range(0.25, 0.75), randf_range(0.82, 0.98)) + 3: center = viewport_size * Vector2(randf_range(0.3, 0.8), randf_range(0.04, 0.13)) + splat.position = center - splat.size * 0.5 + splat.rotation = randf_range(-PI, PI) + splat.modulate = Color(0.85, 0.1, 0.12, 0.85 * intensity) + shader_material.set_shader_parameter("strength", intensity) + +func _process(delta: float) -> void: + if not active: + return + phase += delta * 42.0 + shake_left = maxf(0, shake_left - delta) + blood_left = maxf(0, blood_left - delta) + var shake := pow(shake_left / SHAKE_SECONDS, 2.0) * intensity + if is_instance_valid(pivot): + pivot.position = Vector3(sin(phase) * 0.025, cos(phase * 1.3) * 0.018, sin(phase * 0.8) * 0.012) * shake + pivot.rotation = Vector3(sin(phase * 0.9) * 0.024, cos(phase) * 0.013, sin(phase * 1.1) * 0.035) * shake + var alpha := blood_left / BLOOD_SECONDS + for splat in splats: + splat.modulate.a = alpha * 0.85 * intensity + shader_material.set_shader_parameter("strength", minf(1, alpha * 1.4) * intensity) + if blood_left <= 0: + intensity = 0 + +func stop() -> void: + active = false + shake_left = 0 + blood_left = 0 + intensity = 0 + if is_instance_valid(pivot): + pivot.transform = Transform3D.IDENTITY + overlay.hide() diff --git a/scripts/damage_feedback.gd.uid b/scripts/damage_feedback.gd.uid new file mode 100644 index 0000000..646478b --- /dev/null +++ b/scripts/damage_feedback.gd.uid @@ -0,0 +1 @@ +uid://75qn70d6fklx diff --git a/scripts/grenade.gd b/scripts/grenade.gd new file mode 100644 index 0000000..9be8b31 --- /dev/null +++ b/scripts/grenade.gd @@ -0,0 +1,113 @@ +extends RigidBody3D +signal detonated +const FUSE_SECONDS := 4.0 +const BLAST_RADIUS := 5.0 +const MAX_DAMAGE := 160.0 +const Audio = preload("res://scripts/hl_audio.gd") +const Blood = preload("res://scripts/blood_effects.gd") +var age := 0.0 +var exploded := false +var thrower: CharacterBody3D + +func _ready() -> void: + name = "Grenade" + add_to_group("grenades") + collision_layer = 16 + collision_mask = 1 | 4 + mass = 0.4 + continuous_cd = true + linear_damp = 0.15 + angular_damp = 0.6 + var physics := PhysicsMaterial.new() + physics.bounce = 0.45 + physics.friction = 0.65 + physics_material_override = physics + var collider := CollisionShape3D.new() + var sphere := SphereShape3D.new() + sphere.radius = 0.12 + collider.shape = sphere + add_child(collider) + var model: Node3D = load("res://assets/weapons/grenade.scn").instantiate() + add_child(model) + for area in model.find_children("*", "Area3D", true, false): + area.collision_layer = 0 + area.collision_mask = 0 + for mesh in model.find_children("*", "MeshInstance3D", true, false): + mesh.layers = 2 + if is_instance_valid(thrower): + add_collision_exception_with(thrower) + +func _physics_process(delta: float) -> void: + age += delta + if age >= FUSE_SECONDS and not exploded: + exploded = true + call_deferred("_explode") + +func _explode() -> void: + freeze = true + collision_layer = 0 + collision_mask = 0 + hide() + var world := get_parent() as Node3D + var point := global_position + var targets := get_tree().get_nodes_in_group("live_zombies") + if is_instance_valid(thrower) and not thrower.death_started: + targets.append(thrower) + for target in targets: + if not is_instance_valid(target): + continue + var center: Vector3 = target.global_position + Vector3.UP * 0.9 + var distance := point.distance_to(center) + if distance > BLAST_RADIUS: + continue + var query := PhysicsRayQueryParameters3D.create(point, center, 1) + if not get_world_3d().direct_space_state.intersect_ray(query).is_empty(): + continue + var damage := MAX_DAMAGE * (1.0 - distance / BLAST_RADIUS) + if target == thrower: + target.take_damage(damage) + else: + Blood.impact(world, center, (center - point).normalized()) + target.take_damage(damage, center.y) + _effect(world, point) + detonated.emit() + queue_free() + +func _effect(world: Node3D, point: Vector3) -> void: + var effect := Node3D.new() + effect.name = "GrenadeExplosion" + world.add_child(effect) + effect.global_position = point + Audio.play(effect, "weapons/explode%d.wav" % randi_range(3, 5), true, -7) + var light := OmniLight3D.new() + light.light_color = Color(1, 0.45, 0.08) + light.light_energy = 8 + light.omni_range = 10 + effect.add_child(light) + var tween := effect.create_tween() + tween.tween_property(light, "light_energy", 0.0, 0.3) + var particles := CPUParticles3D.new() + particles.amount = 60 + particles.lifetime = 0.85 + particles.one_shot = true + particles.explosiveness = 1 + particles.direction = Vector3.UP + particles.spread = 180 + particles.initial_velocity_min = 2 + particles.initial_velocity_max = 6 + particles.gravity = Vector3(0, -3, 0) + var mesh := SphereMesh.new() + mesh.radius = 0.06 + mesh.height = 0.12 + var material := StandardMaterial3D.new() + material.albedo_color = Color(1, 0.3, 0.05) + material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + mesh.material = material + particles.mesh = mesh + var colors := Gradient.new() + colors.set_color(0, Color(1, 0.65, 0.1, 1)) + colors.set_color(1, Color(0.15, 0.12, 0.1, 0)) + particles.color_ramp = colors + effect.add_child(particles) + particles.emitting = true + world.get_tree().create_timer(4).timeout.connect(effect.queue_free) diff --git a/scripts/grenade.gd.uid b/scripts/grenade.gd.uid new file mode 100644 index 0000000..db38533 --- /dev/null +++ b/scripts/grenade.gd.uid @@ -0,0 +1 @@ +uid://6681xr24ymu0 diff --git a/scripts/hl_audio.gd b/scripts/hl_audio.gd new file mode 100644 index 0000000..0920240 --- /dev/null +++ b/scripts/hl_audio.gd @@ -0,0 +1,22 @@ +extends RefCounted +## Original WAVs; overlapping shots allowed, positional creature/environment audio. +static func play(parent: Node, path: String, positional: bool = false, volume: float = -10.0) -> void: + var stream := load("res://assets/audio/" + path) as AudioStream + if stream == null: + return + if positional: + var speaker := AudioStreamPlayer3D.new() + speaker.stream = stream + speaker.volume_db = volume + speaker.max_distance = 30 + speaker.unit_size = 4 + parent.add_child(speaker) + speaker.finished.connect(speaker.queue_free) + speaker.play() + else: + var speaker := AudioStreamPlayer.new() + speaker.stream = stream + speaker.volume_db = volume + parent.add_child(speaker) + speaker.finished.connect(speaker.queue_free) + speaker.play() diff --git a/scripts/hl_audio.gd.uid b/scripts/hl_audio.gd.uid new file mode 100644 index 0000000..939748b --- /dev/null +++ b/scripts/hl_audio.gd.uid @@ -0,0 +1 @@ +uid://clxd1ronsgnm6 diff --git a/scripts/main.gd b/scripts/main.gd new file mode 100644 index 0000000..503f71e --- /dev/null +++ b/scripts/main.gd @@ -0,0 +1,158 @@ +extends Node3D +const PlayerScript = preload("res://scripts/player.gd") +const ZombieScript = preload("res://scripts/zombie.gd") +const RoomScript = preload("res://scripts/room.gd") +const MUSIC_PATH := "res://assets/audio/music/zombie_industrial_loop.ogg" +const MUSIC_VOLUME_DB := -11.0 +var player: CharacterBody3D +var room: Node3D +var music: AudioStreamPlayer +var music_started := false +var hud: Label +var score := 0 +var wave := 0 +var alive := 0 +var spawning := false +var wave_pending := false +var smoke := false +var awaiting_web_start := false +var start_label: Label + +func _ready() -> void: + smoke = "--smoke" in OS.get_cmdline_user_args() + room = Node3D.new() + room.set_script(RoomScript) + add_child(room) + player = CharacterBody3D.new() + player.set_script(PlayerScript) + player.position = Vector3(0, 0.1, 0) + add_child(player) + var envnode := WorldEnvironment.new() + var env := Environment.new() + env.background_mode = Environment.BG_COLOR + env.background_color = Color(0.08, 0.12, 0.19) + env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR + env.ambient_light_color = Color(0.65, 0.73, 0.85) + env.ambient_light_energy = 0.65 + envnode.environment = env + add_child(envnode) + var light := OmniLight3D.new() + light.position = Vector3(0, 4.5, 0) + light.omni_range = 24 + light.light_energy = 3 + light.shadow_enabled = true + add_child(light) + var canvas := CanvasLayer.new() + add_child(canvas) + hud = Label.new() + hud.position = Vector2(24, 20) + hud.add_theme_font_size_override("font_size", 24) + canvas.add_child(hud) + var cross := Label.new() + cross.text = "+" + cross.add_theme_font_size_override("font_size", 30) + canvas.add_child(cross) + cross.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + _setup_music() + if OS.has_feature("web"): + awaiting_web_start = true + start_label = Label.new() + start_label.text = "CLICCA PER INIZIARE\nWASD: movimento · clic sinistro: spara · destro: granata · R: ricarica" + start_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + start_label.add_theme_font_size_override("font_size", 24) + start_label.mouse_filter = Control.MOUSE_FILTER_IGNORE + canvas.add_child(start_label) + start_label.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + else: + _start_music() + _next_wave() + if smoke: + get_tree().create_timer(12).timeout.connect(func(): + print("SMOKE: wave=", wave, " alive=", alive, " player_hp=", player.hp) + get_tree().quit()) + +func _unhandled_input(event: InputEvent) -> void: + if awaiting_web_start and event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + awaiting_web_start = false + start_label.queue_free() + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + # Il browser concede l'audio solo dopo un gesto dell'utente: qui il click. + _start_music() + _next_wave() + +## Colonna sonora: industrial metal strumentale su loop senza giunzione udibile. +func _setup_music() -> void: + music = AudioStreamPlayer.new() + music.name = "Music" + music.stream = load(MUSIC_PATH) + music.volume_db = MUSIC_VOLUME_DB + if music.stream is AudioStreamOggVorbis: + music.stream.loop = true + add_child(music) + music.add_to_group("music") + # Rete di sicurezza: se il flag di loop mancasse, il brano riparte a fine coda. + music.finished.connect(_restart_music) + +func _start_music() -> void: + if music_started or music == null: + return + music_started = true + music.play() + +func _restart_music() -> void: + if music_started and is_instance_valid(music): + music.play() + +func _process(_dt: float) -> void: + if hud: + hud.text = "VITA %d MUNIZIONI %d/30 ONDATA %d PUNTI %d\nWASD · mouse · sinistro: spara · destro: granata · R: ricarica · spazio: salto · Esc: mouse" % [player.hp, player.ammo, wave, score] + hud.text += "\nGRANATA: " + ("PRONTA" if player.grenade_cooldown <= 0 else "%d s" % ceili(player.grenade_cooldown)) + if player.reloading: + hud.text += "\nRICARICA..." + if player.hp <= 0: + hud.text += "\nSEI MORTO — premi R per ricominciare" + +func zombies_for_wave(number: int) -> int: + return 4 + number * 2 + +func _next_wave() -> void: + wave_pending = false + if player.hp <= 0 or spawning: + return + wave += 1 + spawning = true + for i in zombies_for_wave(wave): + if player.hp <= 0: + break + _spawn(i % 4) + await get_tree().create_timer(0.8).timeout + spawning = false + _check_wave() + +func _spawn(index: int) -> void: + var centers := [Vector3(0, 0, -9), Vector3(0, 0, 9), Vector3(-9, 0, 0), Vector3(9, 0, 0)] + var inward: Vector3 = -centers[index].normalized() + var zombie := CharacterBody3D.new() + zombie.set_script(ZombieScript) + zombie.player = player + zombie.position = centers[index] - inward * 1.2 + add_child(zombie) + alive += 1 + zombie.killed.connect(func(headshot: bool): + score += 150 if headshot else 100 + alive -= 1 + _check_wave()) + var tween := zombie.create_tween() + zombie.entry_tween = tween + tween.tween_property(zombie, "position", centers[index] + Vector3(0, 1.55, 0), 1.2) + tween.tween_callback(func(): + for glass in room.glass_panes.duplicate(): + if is_instance_valid(glass) and Vector2(glass.position.x - centers[index].x, glass.position.z - centers[index].z).length() < 2: + room.shatter_glass(glass)) + tween.tween_property(zombie, "position", centers[index] + inward * 1.4 + Vector3(0, 0.1, 0), 0.8) + tween.tween_callback(func(): zombie.entering = false) + +func _check_wave() -> void: + if alive == 0 and not spawning and not wave_pending and player.hp > 0: + wave_pending = true + get_tree().create_timer(3).timeout.connect(_next_wave) diff --git a/scripts/main.gd.uid b/scripts/main.gd.uid new file mode 100644 index 0000000..20f7050 --- /dev/null +++ b/scripts/main.gd.uid @@ -0,0 +1 @@ +uid://bc8p55iv7cox6 diff --git a/scripts/player.gd b/scripts/player.gd new file mode 100644 index 0000000..794b034 --- /dev/null +++ b/scripts/player.gd @@ -0,0 +1,198 @@ +extends CharacterBody3D +var camera: Camera3D +var damage_pivot: Node3D +var damage_feedback: CanvasLayer +const DamageFeedback = preload("res://scripts/damage_feedback.gd") +var hp := 100.0 +var ammo := 30 +var cooldown := 0.0 +var grenade_cooldown := 0.0 +const GRENADE_COOLDOWN := 15.0 +const Grenade = preload("res://scripts/grenade.gd") +var reloading := false +var gun: Node3D +var flash: OmniLight3D +var weapon_anim: AnimationPlayer +var step_timer := 0.0 +var death_started := false +var death_overlay: ColorRect +const Blood = preload("res://scripts/blood_effects.gd") +const Audio = preload("res://scripts/hl_audio.gd") + +func _ready() -> void: + collision_layer = 2 + collision_mask = 1 | 4 + var cs := CollisionShape3D.new() + var cap := CapsuleShape3D.new() + cap.height = 1.8 + cap.radius = 0.35 + cs.shape = cap + cs.position.y = 0.9 + add_child(cs) + camera = Camera3D.new() + camera.position.y = 1.65 + camera.current = true + camera.fov = 80 + damage_pivot = Node3D.new() + damage_pivot.name = "DamagePivot" + add_child(damage_pivot) + damage_pivot.add_child(camera) + damage_feedback = CanvasLayer.new() + damage_feedback.set_script(DamageFeedback) + damage_feedback.pivot = damage_pivot + add_child(damage_feedback) + gun = Node3D.new() + camera.add_child(gun) + var viewmodel: Node3D = load("res://assets/weapons/v_9mmar.scn").instantiate() + gun.add_child(viewmodel) + for area in viewmodel.find_children("*", "Area3D", true, false): + area.collision_layer = 0 + area.collision_mask = 0 + for mesh in viewmodel.find_children("*", "MeshInstance3D", true, false): + mesh.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF + mesh.layers = 2 + weapon_anim = viewmodel.get_node("AnimationPlayer") + weapon_anim.animation_finished.connect(func(_name: StringName): + if not reloading and not death_started: + weapon_anim.play("idle1")) + weapon_anim.play("deploy") + flash = OmniLight3D.new() + flash.position = Vector3(0.28, -0.2, -1.1) + flash.light_color = Color(1, 0.7, 0.25) + flash.omni_range = 7 + flash.light_energy = 0 + camera.add_child(flash) + if DisplayServer.get_name() != "headless" and not OS.has_feature("web"): + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + +func _unhandled_input(event: InputEvent) -> void: + if OS.has_feature("web") and event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT and not death_started: + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and hp > 0: + rotate_y(-event.relative.x * 0.0025) + camera.rotation.x = clampf(camera.rotation.x - event.relative.y * 0.0025, -1.4, 1.4) + if event.is_action_pressed("pause") and not death_started: + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED else Input.MOUSE_MODE_CAPTURED + if event.is_action_pressed("grenade") and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED and not death_started: + throw_grenade() + if event.is_action_pressed("reload"): + if hp <= 0: + get_tree().reload_current_scene() + elif not reloading and ammo < 30: + reloading = true + weapon_anim.play("reload") + Audio.play(self, "items/cliprelease1.wav") + get_tree().create_timer(0.8).timeout.connect(func(): + if not death_started: + Audio.play(self, "items/clipinsert1.wav")) + get_tree().create_timer(1.57).timeout.connect(func(): + if not death_started: + ammo = 30 + reloading = false + weapon_anim.play("idle1")) + +func _physics_process(dt: float) -> void: + cooldown -= dt + grenade_cooldown = maxf(0, grenade_cooldown - dt) + flash.light_energy = maxf(0, flash.light_energy - dt * 60) + gun.position.z = lerpf(gun.position.z, 0, dt * 15) + if hp <= 0: + return + var input := Input.get_vector("move_left", "move_right", "move_forward", "move_back") + var direction := transform.basis * Vector3(input.x, 0, input.y) + velocity.x = direction.x * 5 + velocity.z = direction.z * 5 + velocity.y -= 20 * dt + if is_on_floor() and Input.is_action_just_pressed("jump"): + velocity.y = 7 + move_and_slide() + step_timer -= dt + if is_on_floor() and direction.length() > 0.1 and step_timer <= 0: + step_timer = 0.42 + Audio.play(self, "player/pl_metal%d.wav" % randi_range(1, 4), false, -18) + if Input.is_action_just_pressed("shoot") and ammo <= 0 and not reloading: + Audio.play(self, "weapons/dryfire1.wav") + if Input.is_action_pressed("shoot") and cooldown <= 0 and ammo > 0 and not reloading and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: + shoot() + +func shoot() -> void: + if ammo <= 0 or reloading or hp <= 0: + return + weapon_anim.stop() + weapon_anim.play("shoot") + Audio.play(self, "weapons/hks%d.wav" % randi_range(1, 3), false, -13) + ammo -= 1 + cooldown = 0.13 + flash.light_energy = 3 + gun.position.z = 0.06 + var start := camera.global_position + var query := PhysicsRayQueryParameters3D.create(start, start - camera.global_basis.z * 80, 1 | 4) + var hit := get_world_3d().direct_space_state.intersect_ray(query) + if hit and hit.collider.has_method("take_damage"): + Blood.impact(get_parent(), hit.position, -camera.global_basis.z) + hit.collider.take_damage(35, hit.position.y) + +func throw_grenade() -> RigidBody3D: + if death_started or hp <= 0 or grenade_cooldown > 0: + return null + var world := get_parent() as Node3D + var forward := -camera.global_basis.z + var start := camera.global_position + var destination := start + forward * 0.55 + # Do not spawn through the wall when the camera is close to it. + var query := PhysicsRayQueryParameters3D.create(start, destination, 1) + var hit := get_world_3d().direct_space_state.intersect_ray(query) + if not hit.is_empty(): + destination = start + forward * maxf(0, start.distance_to(hit.position) - 0.16) + var grenade := RigidBody3D.new() + grenade.set_script(Grenade) + grenade.thrower = self + grenade.position = world.to_local(destination) + world.add_child(grenade) + grenade.linear_velocity = forward * 12 + Vector3.UP * 3 + velocity + grenade.angular_velocity = Vector3(5, 3, 2) + grenade_cooldown = GRENADE_COOLDOWN + return grenade + +func take_damage(amount: float) -> void: + if death_started or amount <= 0: + return + hp = maxf(0, hp - amount) + if hp <= 0: + _die() + else: + damage_feedback.hit(amount) + +func _die() -> void: + death_started = true + damage_feedback.stop() + velocity = Vector3.ZERO + reloading = false + gun.hide() + flash.light_energy = 0 + weapon_anim.stop() + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + # Original Half-Life player pain sample used as the death event vocal. + Audio.play(self, "player/pl_pain7.wav", false, -8) + var layer := CanvasLayer.new() + layer.layer = 5 + add_child(layer) + death_overlay = ColorRect.new() + death_overlay.color = Color(0, 0, 0, 0) + death_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE + layer.add_child(death_overlay) + death_overlay.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + var tween := create_tween() + tween.set_parallel(true) + tween.tween_property(camera, "position:y", 0.22, 0.9).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN) + tween.tween_property(camera, "rotation:z", deg_to_rad(-75), 0.9) + tween.tween_property(camera, "rotation:x", 0.08, 0.9) + tween.tween_property(death_overlay, "color:a", 1.0, 1.8).set_delay(0.7) + var message := Label.new() + message.text = "SEI MORTO\nPremi R per ricominciare" + message.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + message.add_theme_font_size_override("font_size", 36) + message.modulate.a = 0 + layer.add_child(message) + message.set_anchors_and_offsets_preset(Control.PRESET_CENTER) + tween.tween_property(message, "modulate:a", 1.0, 0.5).set_delay(2.2) diff --git a/scripts/player.gd.uid b/scripts/player.gd.uid new file mode 100644 index 0000000..3108845 --- /dev/null +++ b/scripts/player.gd.uid @@ -0,0 +1 @@ +uid://dfflxt06enx5 diff --git a/scripts/room.gd b/scripts/room.gd new file mode 100644 index 0000000..66c0028 --- /dev/null +++ b/scripts/room.gd @@ -0,0 +1,198 @@ +## Stanza 18x18x5 con 4 finestre (una per muro) e nessuna porta. +## Geometria costruita in codice per determinismo; vetro = Area3D rompibile. +class_name Room +extends Node3D + +const W: float = 18.0 # larghezza (x) +const D: float = 18.0 # profondita (z) +const H: float = 5.0 # altezza (y) +const WALL_T: float = 0.3 + +# Finestra: 4 m x 2 m, bordo basso a 1.5 m +const WIN_W: float = 4.0 +const WIN_H: float = 2.0 +const WIN_Y0: float = 1.5 + +var glass_panes: Array[Area3D] = [] +## Punti di spawn esterni, uno per finestra (4 finestre x 4 slot) +var window_spawn_points: Array[Vector3] = [] +var window_normals: Array[Vector3] = [] # direzione "dentro" la stanza per ogni finestra + +func _ready() -> void: + var wall_mat := _surface_material("wall", 0.9) + var floor_mat := _surface_material("floor", 0.95) + var ceil_mat := _surface_material("ceiling", 0.85) + + # Pavimento + _box(Vector3(W, 0.2, D), Vector3(0, -0.1, 0), floor_mat, true) + # Soffitto + _box(Vector3(W, 0.2, D), Vector3(0, H + 0.1, 0), ceil_mat, false) + + # 4 muri con apertura finestra al centro + # nord (z = -D/2), sud (z = +D/2), ovest (x = -W/2), est (x = +W/2) + var walls := [ + {"center": Vector3(0, H / 2, -D / 2 - WALL_T / 2), "size": Vector3(W, H, WALL_T), "axis": "x"}, + {"center": Vector3(0, H / 2, D / 2 + WALL_T / 2), "size": Vector3(W, H, WALL_T), "axis": "x"}, + {"center": Vector3(-W / 2 - WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "axis": "z"}, + {"center": Vector3(W / 2 + WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "axis": "z"}, + ] + for w in walls: + _build_wall_with_window(w.center, w.size, w.axis, wall_mat) + + # Vetrata per ogni finestra + _add_glass(Vector3(0, WIN_Y0 + WIN_H / 2, -D / 2), Vector3(WIN_W, WIN_H, 0.05), Vector3(1, 0, 0)) + _add_glass(Vector3(0, WIN_Y0 + WIN_H / 2, D / 2), Vector3(WIN_W, WIN_H, 0.05), Vector3(1, 0, 0)) + _add_glass(Vector3(-W / 2, WIN_Y0 + WIN_H / 2, 0), Vector3(0.05, WIN_H, WIN_W), Vector3(0, 0, 1)) + _add_glass(Vector3(W / 2, WIN_Y0 + WIN_H / 2, 0), Vector3(0.05, WIN_H, WIN_W), Vector3(0, 0, 1)) + + # Punti di spawn: 4 slot per finestra, fuori dal muro + var win_centers := [ + {"c": Vector3(0, 0, -D / 2), "n": Vector3(0, 0, 1)}, + {"c": Vector3(0, 0, D / 2), "n": Vector3(0, 0, -1)}, + {"c": Vector3(-W / 2, 0, 0), "n": Vector3(1, 0, 0)}, + {"c": Vector3(W / 2, 0, 0), "n": Vector3(-1, 0, 0)}, + ] + for wc in win_centers: + var side: Vector3 = wc.n.cross(Vector3(0, 1, 0)) # asse laterale della finestra + for i in 4: + var off := (i - 1.5) * (WIN_W / 3.0) + var p: Vector3 = wc.c + side * off + wc.n * (-1.2) # 1.2 m fuori dalla finestra + window_spawn_points.append(p) + window_normals.append(wc.n) + +func _build_wall_with_window(center: Vector3, size: Vector3, axis: String, mat: StandardMaterial3D) -> void: + # Segments around the window opening (window is centered on the wall face) + var wall := StaticBody3D.new() + wall.name = "Wall" + add_child(wall) + var half_win_x := WIN_W / 2.0 + var y0 := WIN_Y0 + var y1 := WIN_Y0 + WIN_H + + if axis == "x": # muro lungo x + var L := size.x + var segs := [ + {"s": Vector3(L / 2 - half_win_x, size.y, size.z), "c": Vector3(-half_win_x - (L / 2 - half_win_x) / 2.0, 0, 0)}, + {"s": Vector3(L / 2 - half_win_x, size.y, size.z), "c": Vector3(half_win_x + (L / 2 - half_win_x) / 2.0, 0, 0)}, + {"s": Vector3(WIN_W, y0, size.z), "c": Vector3(0, -size.y / 2 + y0 / 2.0, 0)}, + {"s": Vector3(WIN_W, size.y - y1, size.z), "c": Vector3(0, size.y / 2 - (size.y - y1) / 2.0, 0)}, + ] + for seg in segs: + _add_box_to(wall, seg.s, center + seg.c, mat) + else: # muro lungo z + var L := size.z + var segs := [ + {"s": Vector3(size.x, size.y, L / 2 - half_win_x), "c": Vector3(0, 0, -half_win_x - (L / 2 - half_win_x) / 2.0)}, + {"s": Vector3(size.x, size.y, L / 2 - half_win_x), "c": Vector3(0, 0, half_win_x + (L / 2 - half_win_x) / 2.0)}, + {"s": Vector3(size.x, y0, WIN_W), "c": Vector3(0, -size.y / 2 + y0 / 2.0, 0)}, + {"s": Vector3(size.x, size.y - y1, WIN_W), "c": Vector3(0, size.y / 2 - (size.y - y1) / 2.0, 0)}, + ] + for seg in segs: + _add_box_to(wall, seg.s, center + seg.c, mat) + +func _add_glass(center: Vector3, size: Vector3, rot_axis: Vector3) -> void: + var glass := Area3D.new() + glass.name = "Glass" + glass.collision_layer = 8 + glass.collision_mask = 0 + add_child(glass) + var shape := CollisionShape3D.new() + var box := BoxShape3D.new() + box.size = size + shape.shape = box + glass.add_child(shape) + glass.global_position = center + var mesh := MeshInstance3D.new() + var bm := BoxMesh.new() + bm.size = size + var gm := StandardMaterial3D.new() + gm.albedo_color = Color(0.7, 0.85, 0.9, 0.16) + gm.cull_mode = BaseMaterial3D.CULL_DISABLED + gm.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + gm.shading_mode = BaseMaterial3D.SHADING_MODE_PER_PIXEL + gm.roughness = 0.05 + gm.metallic = 0.1 + bm.material = gm + mesh.mesh = bm + glass.add_child(mesh) + glass_panes.append(glass) + +func shatter_glass(glass: Area3D) -> void: + var sound_origin := Node3D.new() + add_child(sound_origin) + sound_origin.global_position = glass.global_position + preload("res://scripts/hl_audio.gd").play(sound_origin, "debris/bustglass%d.wav" % randi_range(1, 3), true, -12) + get_tree().create_timer(4).timeout.connect(sound_origin.queue_free) + for i in glass_panes.size(): + if glass_panes[i] == glass: + glass_panes.remove_at(i) + break + # Schegge di vetro (particelle semplici) + var sh := CPUParticles3D.new() + sh.direction = Vector3(0, -0.3, 0) + sh.spread = 180 + sh.initial_velocity_min = 1.5 + sh.initial_velocity_max = 4 + sh.gravity = Vector3(0, -9.8, 0) + sh.scale_amount_min = 0.4 + sh.scale_amount_max = 1.2 + sh.color = Color(0.7, 0.85, 1.0, 0.8) + sh.emission_shape = CPUParticles3D.EMISSION_SHAPE_SPHERE + sh.emission_sphere_radius = 1.2 + var shard := BoxMesh.new() + shard.size = Vector3(0.05, 0.02, 0.08) + var shard_mat := StandardMaterial3D.new() + shard_mat.albedo_color = Color(0.6, 0.8, 0.9) + shard.material = shard_mat + sh.mesh = shard + sh.amount = 40 + sh.lifetime = 0.9 + sh.one_shot = true + sh.explosiveness = 1.0 + sh.position = glass.global_position + add_child(sh) + sh.emitting = true + get_tree().create_timer(2).timeout.connect(sh.queue_free) + glass.queue_free() + +func _box(s: Vector3, c: Vector3, m: StandardMaterial3D, collide: bool) -> void: + var body := StaticBody3D.new() + add_child(body) + _add_box_to(body, s, c, m) + if not collide: + body.get_node("shape").set_deferred("disabled", true) + +func _add_box_to(parent: Node3D, s: Vector3, c: Vector3, m: StandardMaterial3D) -> void: + var mi := MeshInstance3D.new() + var bm := BoxMesh.new() + bm.size = s + bm.material = m + mi.mesh = bm + parent.add_child(mi) + var cs := CollisionShape3D.new() + var bs := BoxShape3D.new() + bs.size = s + cs.shape = bs + cs.position = c - parent.position if parent is StaticBody3D else c + cs.name = "shape" + parent.add_child(cs) + # il mesh va centrato sullo stesso punto della collision + mi.position = c - parent.position if parent is StaticBody3D else c + +func _surface_material(texture_name: String, rough: float) -> StandardMaterial3D: + var material := StandardMaterial3D.new() + material.albedo_texture = load("res://assets/textures/" + texture_name + ".png") + material.albedo_color = Color.WHITE + material.roughness = rough + material.texture_filter = BaseMaterial3D.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC + material.texture_repeat = true + material.uv1_triplanar = true + material.uv1_world_triplanar = true + material.uv1_scale = Vector3.ONE * 0.5 + return material + +func _mat(color: Color, rough: float) -> StandardMaterial3D: + var m := StandardMaterial3D.new() + m.albedo_color = color + m.roughness = rough + return m diff --git a/scripts/room.gd.uid b/scripts/room.gd.uid new file mode 100644 index 0000000..6cb664e --- /dev/null +++ b/scripts/room.gd.uid @@ -0,0 +1 @@ +uid://biqsa3jp3253l diff --git a/scripts/zombie.gd b/scripts/zombie.gd new file mode 100644 index 0000000..d20b14f --- /dev/null +++ b/scripts/zombie.gd @@ -0,0 +1,93 @@ +extends CharacterBody3D +signal killed(headshot: bool) +var player: CharacterBody3D +var hp := 100.0 +var speed := 2.0 +var cooldown := 0.0 +var dead := false +var entering := true +var anim: AnimationPlayer +var voice_timer := 4.0 +var entry_tween: Tween +const Audio = preload("res://scripts/hl_audio.gd") + +func _ready() -> void: + add_to_group("live_zombies") + collision_layer = 4 + collision_mask = 1 | 4 + var shape := CollisionShape3D.new() + var cap := CapsuleShape3D.new() + cap.radius = 0.35 + cap.height = 1.8 + shape.shape = cap + shape.position.y = 0.9 + add_child(shape) + var model: Node3D = load("res://assets/zombies/zombie_textured.scn").instantiate() + add_child(model) + anim = model.get_node("AnimationPlayer") + for mesh in model.find_children("*", "MeshInstance3D", true, false): + mesh.layers = 2 + for area in model.find_children("*", "Area3D", true, false): + area.collision_layer = 0 + speed = randf_range(1.6, 2.8) + anim.play("walk") + Audio.play(self, "zombie/zo_alert%d0.wav" % randi_range(1, 3), true, -15) + +func _physics_process(dt: float) -> void: + if dead or entering or not is_instance_valid(player) or player.hp <= 0: + return + cooldown -= dt + voice_timer -= dt + if voice_timer <= 0: + voice_timer = randf_range(5, 10) + Audio.play(self, "zombie/zo_idle%d.wav" % randi_range(1, 4), true, -19) + var dir := player.global_position - global_position + dir.y = 0 + var distance := dir.length() + if distance > 0.01: + rotation.y = atan2(-dir.x, -dir.z) + var walking := distance > 1.7 + var motion := dir.normalized() * speed if walking else Vector3.ZERO + velocity.x = motion.x + velocity.z = motion.z + velocity.y = -1.0 if is_on_floor() else velocity.y - 20.0 * dt + move_and_slide() + if walking and cooldown <= 0 and anim.current_animation != "walk": + anim.play("walk", 0.15) + if not walking and cooldown <= 0: + cooldown = 1.5 + anim.play("attack2", 0.1) + Audio.play(self, "zombie/zo_attack%d.wav" % randi_range(1, 2), true, -15) + get_tree().create_timer(0.5).timeout.connect(_attack) + +func _attack() -> void: + if not dead and is_instance_valid(player) and player.hp > 0 and global_position.distance_to(player.global_position) < 2.3: + player.take_damage(12) + Audio.play(self, "zombie/claw_strike%d.wav" % randi_range(1, 3), true, -12) + +func take_damage(amount: float, hit_y: float) -> void: + if dead: + return + var headshot := hit_y - global_position.y > 1.45 + hp -= amount * (2.5 if headshot else 1.0) + if hp <= 0: + dead = true + remove_from_group("live_zombies") + velocity = Vector3.ZERO + if entry_tween and entry_tween.is_running(): + entry_tween.kill() + collision_layer = 0 + collision_mask = 0 + for speaker in get_children(): + if speaker is AudioStreamPlayer3D: + speaker.stop() + speaker.queue_free() + # The original zombie has no dedicated death WAV: use its pain grunt only at death. + Audio.play(self, "zombie/zo_pain2.wav", true, -11) + anim.play("dieheadshot" if headshot else "diesimple", 0.1) + var settle := create_tween() + settle.tween_property(self, "position:y", 0.0, 0.45) + add_to_group("zombie_corpses") + killed.emit(headshot) + else: + Audio.play(self, "zombie/zo_pain1.wav", true, -15) diff --git a/scripts/zombie.gd.uid b/scripts/zombie.gd.uid new file mode 100644 index 0000000..61abbb8 --- /dev/null +++ b/scripts/zombie.gd.uid @@ -0,0 +1 @@ +uid://jriohka6tdgj diff --git a/tools/apply_nginx_compression.py b/tools/apply_nginx_compression.py new file mode 100644 index 0000000..90309dd --- /dev/null +++ b/tools/apply_nginx_compression.py @@ -0,0 +1,30 @@ +"""Run with sudo on bastion after upload. Backup, validate, reload, rollback on failure.""" +from pathlib import Path +import subprocess,sys,uuid,shutil +source=Path(sys.argv[1]) +config=Path('/etc/nginx/conf.d/enne2.conf') +snippet=Path('/etc/nginx/snippets/zombie-fps.conf') +backup=Path('/etc/nginx/zombie-fps-backup-'+uuid.uuid4().hex[:12]) +backup.mkdir(mode=0o700) +shutil.copy2(config,backup/'enne2.conf') +had_snippet=snippet.exists() +if had_snippet: shutil.copy2(snippet,backup/'zombie-fps.conf') +old=config.read_text() +include=' include /etc/nginx/snippets/zombie-fps.conf;\n' +anchor=' location /pub/ {' +assert old.count(anchor)==1 +new=old if include in old else old.replace(anchor,include+'\n'+anchor,1) +shutil.copyfile(source,snippet) +snippet.chmod(0o644) +config.write_text(new) +try: + subprocess.run(['/usr/sbin/nginx','-t'],check=True) + subprocess.run(['systemctl','reload','nginx'],check=True) +except Exception: + shutil.copy2(backup/'enne2.conf',config) + if had_snippet: shutil.copy2(backup/'zombie-fps.conf',snippet) + else: snippet.unlink(missing_ok=True) + subprocess.run(['/usr/sbin/nginx','-t'],check=True) + subprocess.run(['systemctl','reload','nginx'],check=True) + raise +print('Nginx validated and reloaded; protected backup:',backup) diff --git a/tools/export_weapons.gd b/tools/export_weapons.gd new file mode 100644 index 0000000..b9b1804 --- /dev/null +++ b/tools/export_weapons.gd @@ -0,0 +1,23 @@ +extends SceneTree +## Persist imported assets as native Godot scenes, independent of import extension at runtime. +func _initialize() -> void: + for name in ["v_9mmar", "w_9mmar", "p_9mmar"]: + var scene: PackedScene = load("res://assets/weapons/" + name + ".mdl") + assert(scene != null) + assert(ResourceSaver.save(scene, "res://assets/weapons/" + name + ".scn") == OK) + var node := scene.instantiate() + var textures := 0 + for mesh in node.find_children("*", "MeshInstance3D", true, false): + for i in mesh.mesh.get_surface_count(): + var material = mesh.mesh.surface_get_material(i) + assert(material is StandardMaterial3D and material.albedo_texture != null) + textures += 1 + assert(textures > 0) + if name == "v_9mmar": + var animation: AnimationPlayer = node.get_node("AnimationPlayer") + for expected in ["deploy", "reload", "shoot", "idle1"]: + assert(animation.has_animation(expected)) + assert(animation.get_animation(expected).get_track_count() > 0) + print("EXPORT VERIFIED ", name, " textured surfaces=", textures) + node.free() + quit() diff --git a/tools/export_weapons.gd.uid b/tools/export_weapons.gd.uid new file mode 100644 index 0000000..a00b640 --- /dev/null +++ b/tools/export_weapons.gd.uid @@ -0,0 +1 @@ +uid://ox7reh4673n5 diff --git a/tools/export_web_assets.gd b/tools/export_web_assets.gd new file mode 100644 index 0000000..21f38f9 --- /dev/null +++ b/tools/export_web_assets.gd @@ -0,0 +1,17 @@ +extends SceneTree +func _initialize() -> void: + for path in ["res://assets/weapons/v_9mmar", "res://assets/zombies/zombie_textured", "res://assets/weapons/grenade"]: + var scene: PackedScene = load(path + ".mdl") + assert(scene != null) + assert(ResourceSaver.save(scene, path + ".scn") == OK) + var node := scene.instantiate() + var textured := 0 + for mesh in node.find_children("*", "MeshInstance3D", true, false): + for i in mesh.mesh.get_surface_count(): + var material = mesh.mesh.surface_get_material(i) + assert(material is StandardMaterial3D and material.albedo_texture != null) + textured += 1 + assert(textured > 0) + print("NATIVE SCENE VERIFIED ", path, " surfaces=", textured) + node.free() + quit() diff --git a/tools/export_web_assets.gd.uid b/tools/export_web_assets.gd.uid new file mode 100644 index 0000000..938740e --- /dev/null +++ b/tools/export_web_assets.gd.uid @@ -0,0 +1 @@ +uid://divewqhrr6nq5 diff --git a/tools/extract_grenade_assets.py b/tools/extract_grenade_assets.py new file mode 100644 index 0000000..5747126 --- /dev/null +++ b/tools/extract_grenade_assets.py @@ -0,0 +1,21 @@ +"""Copy original grenade assets; verify and append provenance manifest.""" +from pathlib import Path +import shutil,hashlib,json,struct,wave +root=Path(__file__).resolve().parents[1] +source=Path.home()/'.steam/steam/steamapps/common/Half-Life/valve' +manifest=root/'assets/half_life_manifest.json' +rows=json.loads(manifest.read_text()) +for relative in ['models/grenade.mdl']+['sound/weapons/explode%d.wav'%i for i in [3,4,5]]: + p=source/relative + dest=root/'assets'/('weapons/'+p.name if relative.startswith('models/') else 'audio/weapons/'+p.name) + shutil.copyfile(p,dest) + raw=p.read_bytes(); assert raw==dest.read_bytes() + row={'source':relative,'destination':str(dest.relative_to(root)),'sha256':hashlib.sha256(raw).hexdigest()} + if p.suffix=='.mdl': + assert raw[:4]==b'IDST' and struct.unpack_from(' void: + add_to_group("live_zombies") +func take_damage(amount: float, _hit_y: float) -> void: + damage_taken += amount diff --git a/tools/grenade_test_target.gd.uid b/tools/grenade_test_target.gd.uid new file mode 100644 index 0000000..cf7904c --- /dev/null +++ b/tools/grenade_test_target.gd.uid @@ -0,0 +1 @@ +uid://bx3iw0exfo25k diff --git a/tools/nginx-zombie-fps.conf b/tools/nginx-zombie-fps.conf new file mode 100644 index 0000000..07f90ef --- /dev/null +++ b/tools/nginx-zombie-fps.conf @@ -0,0 +1,39 @@ +# Scope is limited to this game. Versioned resources are never overwritten. +location ^~ /pub/zombie-fps/releases/ { + alias /home/debian/public_html/zombie-fps/releases/; + index index.html; + autoindex off; + types { + text/html html; + application/javascript js; + application/wasm wasm; + application/octet-stream pck; + image/png png; + } + default_type application/octet-stream; + gzip_static on; + gzip_vary on; + gzip_proxied any; + add_header Cache-Control "public, max-age=31536000, immutable"; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; +} +location ^~ /pub/zombie-fps/ { + alias /home/debian/public_html/zombie-fps/; + index index.html; + autoindex off; + types { + text/html html; + application/javascript js; + application/wasm wasm; + application/octet-stream pck; + image/png png; + } + default_type application/octet-stream; + gzip_static on; + gzip_vary on; + gzip_proxied any; + add_header Cache-Control "no-cache" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; +} diff --git a/tools/package_web_release.py b/tools/package_web_release.py new file mode 100644 index 0000000..0ff1059 --- /dev/null +++ b/tools/package_web_release.py @@ -0,0 +1,33 @@ +"""Immutable content-addressed Web releases with offline gzip compression.""" +from pathlib import Path +import hashlib, gzip, json, shutil, os +root=Path(__file__).resolve().parents[1] +source=root/'build/web' +output=root/'build/web-deploy' +files=sorted(p for p in source.iterdir() if p.is_file() and not p.name.endswith(('.gz','.br'))) +identity=hashlib.sha256() +for p in files: + identity.update(p.name.encode()+b'\0'+p.read_bytes()) +version=identity.hexdigest()[:16] +if output.exists(): shutil.rmtree(output) +release=output/'releases'/version +release.mkdir(parents=True) +report=[] +for p in files: + destination=release/p.name + shutil.copyfile(p,destination) + raw=p.read_bytes() + if p.suffix in ['.wasm','.pck','.js','.html']: + compressed=gzip.compress(raw,compresslevel=9,mtime=0) + assert gzip.decompress(compressed)==raw + z=destination.with_name(destination.name+'.gz') + z.write_bytes(compressed) + os.utime(z,(destination.stat().st_mtime,destination.stat().st_mtime)) + report.append({'file':p.name,'original':len(raw),'gzip':len(compressed),'sha256':hashlib.sha256(raw).hexdigest()}) +html=(source/'index.html').read_text() +html=html.replace('','\n\t\t',1) +(output/'index.html').write_text(html) +(root/'build/web-compression-report.json').write_text(json.dumps({'version':version,'files':report},indent=2)+'\n') +print('Release:',version) +for r in report: print(r['file'],r['original'],'->',r['gzip']) +print('Total:',sum(r['original'] for r in report),'->',sum(r['gzip'] for r in report)) diff --git a/tools/prepare_web.py b/tools/prepare_web.py new file mode 100644 index 0000000..2b7862b --- /dev/null +++ b/tools/prepare_web.py @@ -0,0 +1,15 @@ +"""Build a GDExtension-free staging project; leaves desktop import plugin intact.""" +from pathlib import Path +import shutil +root = Path(__file__).resolve().parents[1] +stage = root/'build/web-stage' +if stage.exists(): + shutil.rmtree(stage) +stage.mkdir(parents=True) +for directory in ['scripts','scenes','assets']: + shutil.copytree(root/directory,stage/directory,ignore=shutil.ignore_patterns('*.mdl','*.import','*.uid','half_life_manifest.json','p_9mmar.scn','w_9mmar.scn')) +config=(root/'project.godot').read_text().replace('enabled=PackedStringArray("res://addons/goldsrc/plugin.cfg")','enabled=PackedStringArray()') +config=config.replace('renderer/rendering_method="forward_plus"','renderer/rendering_method="gl_compatibility"') +(stage/'project.godot').write_text(config) +shutil.copyfile(root/'export_presets.cfg',stage/'export_presets.cfg') +print(stage) diff --git a/tools/test_damage_feedback.gd b/tools/test_damage_feedback.gd new file mode 100644 index 0000000..b805b81 --- /dev/null +++ b/tools/test_damage_feedback.gd @@ -0,0 +1,52 @@ +extends SceneTree +func _initialize() -> void: + call_deferred("run_test") +func run_test() -> void: + var world := Node3D.new() + root.add_child(world) + var room := Node3D.new() + room.set_script(load("res://scripts/room.gd")) + world.add_child(room) + var light := OmniLight3D.new() + light.position = Vector3(0, 4, 0) + light.omni_range = 24 + light.light_energy = 4 + world.add_child(light) + var player := CharacterBody3D.new() + player.set_script(load("res://scripts/player.gd")) + player.position.y = 0.1 + world.add_child(player) + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + await create_timer(0.2).timeout + player.camera.rotation.x = 0.12 + var aim: Vector3 = player.camera.rotation + player.take_damage(12) + assert(player.hp == 88) + assert(player.damage_feedback.blood_left > 0) + assert(player.damage_feedback.splats.size() == 7) + await create_timer(0.06).timeout + assert(player.damage_pivot.position.length() > 0.0001) + assert(player.camera.rotation.is_equal_approx(aim)) + if DisplayServer.get_name() != "headless": + await RenderingServer.frame_post_draw + root.get_texture().get_image().save_png("/tmp/zombie-player-hit.png") + # Multiple hits reuse a bounded pool; no camera offset accumulates. + player.take_damage(12) + player.take_damage(12) + assert(player.damage_feedback.intensity <= 1) + assert(player.damage_feedback.splats.size() == 7) + await create_timer(1.35).timeout + assert(player.damage_pivot.transform.is_equal_approx(Transform3D.IDENTITY)) + assert(player.camera.rotation.is_equal_approx(aim)) + assert(player.damage_feedback.blood_left == 0) + player.take_damage(12) + player.take_damage(1000) + assert(not player.damage_feedback.active) + assert(player.damage_pivot.transform.is_equal_approx(Transform3D.IDENTITY)) + await create_timer(3).timeout + assert(player.camera.position.y < 0.25) + assert(player.death_overlay.color.a > 0.99) + print("PASS: damage shake, screen blood, aim preserved, repeated hits bounded, camera reset, death fade intact") + world.queue_free() + await process_frame + quit() diff --git a/tools/test_damage_feedback.gd.uid b/tools/test_damage_feedback.gd.uid new file mode 100644 index 0000000..c82ae51 --- /dev/null +++ b/tools/test_damage_feedback.gd.uid @@ -0,0 +1 @@ +uid://duk4s0b075ckw diff --git a/tools/test_death_effects.gd b/tools/test_death_effects.gd new file mode 100644 index 0000000..a742000 --- /dev/null +++ b/tools/test_death_effects.gd @@ -0,0 +1,52 @@ +extends SceneTree +var world: Node3D +func _initialize() -> void: + call_deferred("run_test") +func run_test() -> void: + world = load("res://scenes/main.tscn").instantiate() + root.add_child(world) + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + await create_timer(2.4).timeout + var zombie: Node3D + for child in world.get_children(): + if child.get_script() == world.ZombieScript: + zombie = child + break + assert(zombie != null) + world.player.hp = 10000 + zombie.position = Vector3(0, 0, -3) + if zombie.entry_tween: + zombie.entry_tween.kill() + zombie.entering = false + var point := zombie.global_position + Vector3(0, 1.2, 0) + preload("res://scripts/blood_effects.gd").impact(world, point, Vector3(0, 0, -1)) + zombie.take_damage(1000, point.y) + assert(zombie.dead and zombie.collision_layer == 0) + await create_timer(0.2).timeout + assert(get_nodes_in_group("blood_stains").size() == 1) + if DisplayServer.get_name() != "headless": + await RenderingServer.frame_post_draw + root.get_texture().get_image().save_png("/tmp/zombie-blood.png") + await create_timer(4.3).timeout + assert(is_instance_valid(zombie) and zombie.is_inside_tree()) + assert(absf(zombie.position.y) < 0.01) + assert(get_nodes_in_group("zombie_corpses").has(zombie)) + if DisplayServer.get_name() != "headless": + await RenderingServer.frame_post_draw + root.get_texture().get_image().save_png("/tmp/zombie-corpse.png") + world.player.take_damage(20000) + world.player.take_damage(12) + assert(world.player.death_started and world.player.hp == 0) + var ammo: int = world.player.ammo + world.player.shoot() + assert(world.player.ammo == ammo) + await create_timer(3).timeout + assert(world.player.camera.position.y < 0.25) + assert(world.player.death_overlay.color.a > 0.99) + if DisplayServer.get_name() != "headless": + await RenderingServer.frame_post_draw + root.get_texture().get_image().save_png("/tmp/zombie-death-fade.png") + print("PASS: persistent corpse, blood decal, single death, blocked shooting, camera fall, black fade") + world.queue_free() + await process_frame + quit() diff --git a/tools/test_death_effects.gd.uid b/tools/test_death_effects.gd.uid new file mode 100644 index 0000000..3e2cd74 --- /dev/null +++ b/tools/test_death_effects.gd.uid @@ -0,0 +1 @@ +uid://dboym2kmns672 diff --git a/tools/test_grenade.gd b/tools/test_grenade.gd new file mode 100644 index 0000000..ecb2cbb --- /dev/null +++ b/tools/test_grenade.gd @@ -0,0 +1,70 @@ +extends SceneTree +var detonation_time := -1 +func _initialize() -> void: + call_deferred("run_test") +func run_test() -> void: + var world := Node3D.new() + root.add_child(world) + var room := Node3D.new() + room.set_script(load("res://scripts/room.gd")) + world.add_child(room) + var player := CharacterBody3D.new() + player.set_script(load("res://scripts/player.gd")) + player.position = Vector3(0, 0.1, 4) + world.add_child(player) + player.hp = 10000 + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + await physics_frame + var grenade: RigidBody3D = player.throw_grenade() + assert(grenade != null) + assert(player.throw_grenade() == null) + assert(is_equal_approx(player.grenade_cooldown, 15)) + assert(grenade.physics_material_override.bounce > 0) + grenade.detonated.connect(func(): detonation_time = Time.get_ticks_msec()) + var started := Time.get_ticks_msec() + var start := grenade.position + await create_timer(0.5).timeout + assert(grenade.position.distance_to(start) > 1) + assert(grenade.linear_velocity.y < 3) + var targets: Array[Node3D] = [] + for pos in [Vector3(0, 0, -2), Vector3(2, 0, 0), Vector3(0, 0, -7)]: + var target := Node3D.new() + target.set_script(load("res://tools/grenade_test_target.gd")) + target.position = pos + world.add_child(target) + targets.append(target) + var wall := StaticBody3D.new() + wall.position = Vector3(1, 1.5, 0) + var shape := CollisionShape3D.new() + var box := BoxShape3D.new() + box.size = Vector3(0.2, 3, 2) + shape.shape = box + wall.add_child(shape) + world.add_child(wall) + # Isolate explosion coverage from the projectile's random rotation/bounces. + grenade.freeze = true + grenade.global_position = Vector3(0, 0.3, 0) + await create_timer(3.15).timeout + assert(is_instance_valid(grenade) and not grenade.exploded) + await create_timer(0.6).timeout + assert(detonation_time >= 0) + var seconds := (detonation_time - started) / 1000.0 + assert(seconds >= 3.8 and seconds <= 4.3) + assert(targets[0].damage_taken > 0) + assert(targets[1].damage_taken == 0) # behind a wall + assert(targets[2].damage_taken == 0) # beyond blast radius + assert(player.hp < 10000) + assert(player.throw_grenade() == null) + # Wait until the real cooldown expires, without changing the game timer. + await create_timer(player.grenade_cooldown + 0.1).timeout + assert(player.grenade_cooldown == 0) + assert(player.throw_grenade() != null) + var main = load("res://scripts/main.gd").new() + assert(main.zombies_for_wave(1) == 6) + assert(main.zombies_for_wave(2) == 8) + assert(main.zombies_for_wave(11) == 26) + main.free() + print("PASS: thrown physics/gravity, 4s fuse (", seconds, "), 15s cooldown, blast LOS/range/self-damage, wave growth beyond 24") + world.queue_free() + await process_frame + quit() diff --git a/tools/test_grenade.gd.uid b/tools/test_grenade.gd.uid new file mode 100644 index 0000000..a5bde2c --- /dev/null +++ b/tools/test_grenade.gd.uid @@ -0,0 +1 @@ +uid://h20uv8uhbnfo diff --git a/tools/test_music.gd b/tools/test_music.gd new file mode 100644 index 0000000..eb4a535 --- /dev/null +++ b/tools/test_music.gd @@ -0,0 +1,37 @@ +extends SceneTree +## Verifica la colonna sonora in loop: nodo presente, stream OGG con loop attivo, +## volume da sottofondo, durata attesa e riproduzione avviata. +const EXPECTED_LENGTH := 57.6 +const EXPECTED_VOLUME_DB := -11.0 + +func _initialize() -> void: + call_deferred("run_test") + +func run_test() -> void: + var world = load("res://scenes/main.tscn").instantiate() + root.add_child(world) + await process_frame + var players := get_nodes_in_group("music") + assert(players.size() == 1) + var music: AudioStreamPlayer = players[0] + assert(music.name == "Music") + assert(music.stream is AudioStreamOggVorbis) + assert(music.stream.loop) + assert(absf(music.volume_db - EXPECTED_VOLUME_DB) < 0.001) + var length: float = music.stream.get_length() + assert(absf(length - EXPECTED_LENGTH) < 0.3) + assert(music.playing) + var first := music.get_playback_position() + await create_timer(0.5).timeout + assert(music.playing) + var second := music.get_playback_position() + print("PASS: Music loop=%s durata=%.2fs volume=%.1fdB posizione %.2f -> %.2f | driver audio: %s" + % [music.stream.loop, length, music.volume_db, first, second, AudioServer.get_driver_name()]) + # La musica deve restare in riproduzione anche a giocatore morto. + world.player.take_damage(9999) + await create_timer(0.3).timeout + assert(music.playing) + print("PASS: musica attiva anche dopo la morte del giocatore") + world.queue_free() + await process_frame + quit() diff --git a/tools/test_wave_growth.gd b/tools/test_wave_growth.gd new file mode 100644 index 0000000..9952390 --- /dev/null +++ b/tools/test_wave_growth.gd @@ -0,0 +1,24 @@ +extends SceneTree +func _initialize() -> void: + call_deferred("run_test") +func run_test() -> void: + var world = load("res://scenes/main.tscn").instantiate() + root.add_child(world) + world.player.hp = 100000 + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE + await create_timer(5.2).timeout + assert(world.wave == 1 and world.alive == 6 and not world.spawning) + for zombie in get_nodes_in_group("live_zombies"): + zombie.take_damage(1000, zombie.global_position.y + 0.9) + assert(world.alive == 0 and world.wave_pending) + world._check_wave() + world._check_wave() # no duplicate next-wave scheduling + await create_timer(3.2).timeout + assert(world.wave == 2) + await create_timer(6.4).timeout + assert(world.wave == 2 and world.alive == 8 and not world.spawning) + assert(get_nodes_in_group("zombie_corpses").size() == 6) + print("PASS: wave1=6, clear -> wave2=8, single transition, corpses retained") + world.queue_free() + await process_frame + quit() diff --git a/tools/test_wave_growth.gd.uid b/tools/test_wave_growth.gd.uid new file mode 100644 index 0000000..7d8e2e1 --- /dev/null +++ b/tools/test_wave_growth.gd.uid @@ -0,0 +1 @@ +uid://btk5c8aa0wlwy