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
|
||||
|
||||
## 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/
|
||||
Server: debian@enne2.net (bastion).
|
||||
Web root: /home/debian/public_html/zombie-fps/.
|
||||
|
||||
+7
-2
@@ -31,8 +31,13 @@ func _ready() -> void:
|
||||
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.background_mode = Environment.BG_SKY
|
||||
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_color = Color(0.65, 0.73, 0.85)
|
||||
env.ambient_light_energy = 0.65
|
||||
|
||||
+14
-1
@@ -3,7 +3,19 @@ var camera: Camera3D
|
||||
var damage_pivot: Node3D
|
||||
var damage_feedback: CanvasLayer
|
||||
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 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"):
|
||||
get_tree().reload_current_scene() # R o pulsante Ricarica a partita finita
|
||||
return
|
||||
_regenerate_health(dt)
|
||||
_handle_actions()
|
||||
# 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.
|
||||
|
||||
@@ -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