Add one HP per second regeneration and Half-Life starfield sky
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"spacert.tga": "1b5726f5c2f5e27ddfd8b583b6f31b77a7b85c8ae31f11a765e43dd41c1ba141",
|
||||||
|
"spacelf.tga": "18b1408a35244736380a2e98d39fbb02ff76bdb33262bd87f2de6b4c1895d4f3",
|
||||||
|
"spaceup.tga": "c3a548c1bf08859179a2922366c793c29ed322ace6ce5ae75f094fcc39457505",
|
||||||
|
"spacedn.tga": "1f746a42da3bc66c3fc5ab468ecf29a22587cd6413afad9bedf92518067c9b87",
|
||||||
|
"spaceft.tga": "73606c44eaa85615068b2a42c41f2cddef4ff49a27c872052ceff20543374ec2",
|
||||||
|
"spacebk.tga": "09dcf639be7050b87ee172b359c4cd499af66f5978bbb23cf61a3976e2fb4a24"
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 476 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bf7v2lohgsvfa"
|
||||||
|
path="res://.godot/imported/space_panorama.png-9d641b39004f6127eea8a85f76e8ffe9.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/textures/sky/space_panorama.png"
|
||||||
|
dest_files=["res://.godot/imported/space_panorama.png-9d641b39004f6127eea8a85f76e8ffe9.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
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Health regeneration and starry sky
|
||||||
|
|
||||||
|
- Living players recover exactly one HP per elapsed gameplay second, including combat.
|
||||||
|
Maximum is 100 HP; no healing or resurrection after death. Physics time accumulator
|
||||||
|
handles frame-rate variation; time at full HP does not bank healing.
|
||||||
|
- `scripts/main.gd` uses a `PanoramaSkyMaterial` as the environment background.
|
||||||
|
Ambient light remains the existing constant color; sky reflections are disabled.
|
||||||
|
- Compared local Half-Life skies `night`, `space`, and several `neb*` sets. `space`
|
||||||
|
was selected for a plain star field (`night` is cloudy; nebulae are colorful).
|
||||||
|
- `assets/textures/sky/space_panorama.png`: 2048x1024 equirectangular conversion of
|
||||||
|
the six Valve `space*.tga` faces, not a newly generated texture. The original faces
|
||||||
|
are low resolution, so stars retain their original soft appearance. Source hashes
|
||||||
|
are in `source_manifest.json`; original Steam files are never modified.
|
||||||
|
|
||||||
|
Reproduce (Pillow and FFmpeg with v360 required):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 tools/prepare_sky.py /path/to/Half-Life/valve/gfx/env
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify with installed Godot 4.7.1:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
"$G" --headless --path . --script res://tools/test_health_sky.gd
|
||||||
|
"$G" --display-driver x11 --rendering-method gl_compatibility --path . \
|
||||||
|
--script res://tools/test_health_sky.gd -- --screenshot
|
||||||
|
```
|
||||||
|
|
||||||
|
Screenshot: `/tmp/zombie-star-window.png`. Test checks discrete timing, maximum,
|
||||||
|
no healing after death, panorama dimensions, and unchanged ambient light. Existing
|
||||||
|
music, grenade, wave, death, damage, and touch tests also pass. The existing resource
|
||||||
|
leak/in-use warnings at test shutdown remain; they are not fixed by this change.
|
||||||
|
|
||||||
|
Sky format reference: https://docs.godotengine.org/en/stable/classes/class_panoramaskymaterial.html
|
||||||
@@ -1,5 +1,17 @@
|
|||||||
# Web build and compressed deployment
|
# Web build and compressed deployment
|
||||||
|
|
||||||
|
## Health + starry sky release: 31564f79b9f5e299
|
||||||
|
|
||||||
|
See `docs/HEALTH-SKY.md`: +1 HP/second while alive, capped at 100; Half-Life
|
||||||
|
`space` panorama visible outside windows. PCK 3,938,672 bytes; gzip payload total
|
||||||
|
12,395,646 bytes. Previous releases retained; root backup `index-before-health-sky.html`.
|
||||||
|
|
||||||
|
Build encountered an import-thread crash (`propagate_notification` off caller thread,
|
||||||
|
signal 11). Workaround applied only to generated staging `project.godot`:
|
||||||
|
`[editor] import/use_multiple_threads=false`; serial re-import and export succeeded.
|
||||||
|
Upstream similar issue: https://github.com/godotengine/godot/issues/96242
|
||||||
|
|
||||||
|
|
||||||
URL: https://enne2.net/pub/zombie-fps/
|
URL: https://enne2.net/pub/zombie-fps/
|
||||||
Server: debian@enne2.net (bastion).
|
Server: debian@enne2.net (bastion).
|
||||||
Web root: /home/debian/public_html/zombie-fps/.
|
Web root: /home/debian/public_html/zombie-fps/.
|
||||||
|
|||||||
+7
-2
@@ -31,8 +31,13 @@ func _ready() -> void:
|
|||||||
add_child(player)
|
add_child(player)
|
||||||
var envnode := WorldEnvironment.new()
|
var envnode := WorldEnvironment.new()
|
||||||
var env := Environment.new()
|
var env := Environment.new()
|
||||||
env.background_mode = Environment.BG_COLOR
|
env.background_mode = Environment.BG_SKY
|
||||||
env.background_color = Color(0.08, 0.12, 0.19)
|
var sky_material := PanoramaSkyMaterial.new()
|
||||||
|
sky_material.panorama = load("res://assets/textures/sky/space_panorama.png")
|
||||||
|
env.sky = Sky.new()
|
||||||
|
env.sky.sky_material = sky_material
|
||||||
|
# Solo sfondo: illuminazione e riflessi non dipendono dal cielo scuro.
|
||||||
|
env.reflected_light_source = Environment.REFLECTION_SOURCE_DISABLED
|
||||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
env.ambient_light_source = Environment.AMBIENT_SOURCE_COLOR
|
||||||
env.ambient_light_color = Color(0.65, 0.73, 0.85)
|
env.ambient_light_color = Color(0.65, 0.73, 0.85)
|
||||||
env.ambient_light_energy = 0.65
|
env.ambient_light_energy = 0.65
|
||||||
|
|||||||
+14
-1
@@ -3,7 +3,19 @@ var camera: Camera3D
|
|||||||
var damage_pivot: Node3D
|
var damage_pivot: Node3D
|
||||||
var damage_feedback: CanvasLayer
|
var damage_feedback: CanvasLayer
|
||||||
const DamageFeedback = preload("res://scripts/damage_feedback.gd")
|
const DamageFeedback = preload("res://scripts/damage_feedback.gd")
|
||||||
var hp := 100.0
|
const MAX_HP := 100.0
|
||||||
|
var hp := MAX_HP
|
||||||
|
var _regen_elapsed := 0.0
|
||||||
|
|
||||||
|
## Discreto: +1 HP per secondo di gioco, mai oltre il massimo o dopo morte.
|
||||||
|
func _regenerate_health(dt: float) -> void:
|
||||||
|
if hp <= 0 or death_started or hp >= MAX_HP:
|
||||||
|
_regen_elapsed = 0.0
|
||||||
|
return
|
||||||
|
_regen_elapsed += dt
|
||||||
|
while _regen_elapsed >= 1.0:
|
||||||
|
_regen_elapsed -= 1.0
|
||||||
|
hp = minf(MAX_HP, hp + 1.0)
|
||||||
var ammo := 30
|
var ammo := 30
|
||||||
var cooldown := 0.0
|
var cooldown := 0.0
|
||||||
var grenade_cooldown := 0.0
|
var grenade_cooldown := 0.0
|
||||||
@@ -127,6 +139,7 @@ func _physics_process(dt: float) -> void:
|
|||||||
if Input.is_action_just_pressed("reload"):
|
if Input.is_action_just_pressed("reload"):
|
||||||
get_tree().reload_current_scene() # R o pulsante Ricarica a partita finita
|
get_tree().reload_current_scene() # R o pulsante Ricarica a partita finita
|
||||||
return
|
return
|
||||||
|
_regenerate_health(dt)
|
||||||
_handle_actions()
|
_handle_actions()
|
||||||
# Deadzone esplicita piccola (0.2): con la 0.5 delle azioni la corsa utile del
|
# Deadzone esplicita piccola (0.2): con la 0.5 delle azioni la corsa utile del
|
||||||
# joystick touch si dimezzerebbe, mentre per tastiera/joypad nulla cambia.
|
# joystick touch si dimezzerebbe, mentre per tastiera/joypad nulla cambia.
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
"""Convert local Half-Life space sky faces to a Godot panorama (Pillow + ffmpeg)."""
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import tempfile
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('source', type=pathlib.Path, help='Half-Life valve/gfx/env directory')
|
||||||
|
args = parser.parse_args()
|
||||||
|
out = pathlib.Path(__file__).resolve().parents[1] / 'assets/textures/sky'
|
||||||
|
out.mkdir(parents=True, exist_ok=True)
|
||||||
|
faces = ['rt', 'lf', 'up', 'dn', 'ft', 'bk']
|
||||||
|
images = [Image.open(args.source / f'space{s}.tga').convert('RGB') for s in faces]
|
||||||
|
w, h = images[0].size
|
||||||
|
assert w == h and all(im.size == (w, h) for im in images)
|
||||||
|
strip = Image.new('RGB', (6*w, h))
|
||||||
|
for i, im in enumerate(images):
|
||||||
|
strip.paste(im, (i*w, 0))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
src = pathlib.Path(tmp) / 'cube.png'
|
||||||
|
strip.save(src)
|
||||||
|
subprocess.run(['ffmpeg', '-y', '-loglevel', 'error', '-i', str(src),
|
||||||
|
'-vf', 'v360=input=c6x1:output=e:in_forder=rludfb:w=2048:h=1024',
|
||||||
|
'-frames:v', '1', str(out / 'space_panorama.png')], check=True)
|
||||||
|
manifest = {f'space{s}.tga': hashlib.sha256((args.source / f'space{s}.tga').read_bytes()).hexdigest() for s in faces}
|
||||||
|
(out / 'source_manifest.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
||||||
|
print(out / 'space_panorama.png')
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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.set_process(false)
|
||||||
|
var player = world.player
|
||||||
|
player.set_physics_process(false)
|
||||||
|
player.hp = 90.0
|
||||||
|
player._regenerate_health(0.5)
|
||||||
|
assert(player.hp == 90.0)
|
||||||
|
player._regenerate_health(0.5)
|
||||||
|
assert(player.hp == 91.0)
|
||||||
|
player._regenerate_health(3.0)
|
||||||
|
assert(player.hp == 94.0)
|
||||||
|
print("PASS: +1 HP ogni secondo, recupero frame lunghi")
|
||||||
|
player.hp = 99.5
|
||||||
|
player._regenerate_health(5.0)
|
||||||
|
assert(player.hp == 100.0)
|
||||||
|
player._regenerate_health(0.0)
|
||||||
|
player.hp = 90.0
|
||||||
|
player._regenerate_health(0.5)
|
||||||
|
assert(player.hp == 90.0)
|
||||||
|
print("PASS: limite 100 HP, nessun accumulo a vita piena")
|
||||||
|
if "--screenshot" in OS.get_cmdline_user_args():
|
||||||
|
player.position = Vector3(0, 0, -6)
|
||||||
|
player.camera.rotation = Vector3.ZERO
|
||||||
|
await create_timer(0.5).timeout
|
||||||
|
await RenderingServer.frame_post_draw
|
||||||
|
root.get_texture().get_image().save_png("/tmp/zombie-star-window.png")
|
||||||
|
player.hp = 0
|
||||||
|
player._regenerate_health(10.0)
|
||||||
|
assert(player.hp == 0)
|
||||||
|
player.hp = 30
|
||||||
|
player.death_started = true
|
||||||
|
player._regenerate_health(10.0)
|
||||||
|
assert(player.hp == 30)
|
||||||
|
print("PASS: nessuna rigenerazione dopo morte")
|
||||||
|
var environment: Environment
|
||||||
|
for node in world.get_children():
|
||||||
|
if node is WorldEnvironment:
|
||||||
|
environment = node.environment
|
||||||
|
assert(environment.background_mode == Environment.BG_SKY)
|
||||||
|
assert(environment.sky.sky_material is PanoramaSkyMaterial)
|
||||||
|
var texture = environment.sky.sky_material.panorama
|
||||||
|
assert(texture.get_width() == 2048 and texture.get_height() == 1024)
|
||||||
|
assert(environment.ambient_light_source == Environment.AMBIENT_SOURCE_COLOR)
|
||||||
|
print("PASS: panorama stellato caricato, illuminazione stanza preservata")
|
||||||
|
world.queue_free()
|
||||||
|
await process_frame
|
||||||
|
quit()
|
||||||
Reference in New Issue
Block a user