Files
shenleiandClaude Sonnet 5 f32064c74a 40250 classic: parallel-dev checkpoint through increment 49 (W0 interface freeze)
Captures the uncommitted parallel-development work (increments 46-49) on the
40250 classic client port, folded into docs/CLIENT-GAP.md + CLIENT-GAP-FIX.md.

Increment 49 (W0 interface-freeze batch, Phase 1 prerequisite) lands the
cross-workflow shared-file scaffolding so the 4 Phase 1 workflows can run in
isolated worktrees without contention:

- entity_store.{h,cpp}: Entity gains empire/affect_flags/owner_vid/state_flags;
  new mut_spawn_full() (single-shot two-packet merge, §2.3), mut_ownership()
  (§2.5), mut_map_bgm() + take_bgm_dirty()/bgm_name()/bgm_volume() (§9.1),
  drain_dirty() (§2.1/§2.5 bare-field-update queue); mut_char_info() now stores
  empire; reset_for_map_change() clears m_dirty.
- m2_client.cpp: new bgm_changed(name, volume) signal; classic + m2dev pump
  loops drain drain_dirty() -> entity_info and take_bgm_dirty() -> bgm_changed;
  entity_dict() exposes the 4 new keys.
- classic/classic_parser.{h,cpp}: GC_MAIN_CHARACTER3_BGM /
  GC_MAIN_CHARACTER4_BGM_VOL route bgm_name/bgm_volume to mut_map_bgm() (was
  discarded); new m_pending_actor staging map (W1 fills §2.2 merge logic).
- project/bgm_director.gd (new) + game_scene.gd: BGM consumption split out of
  net_world.gd so W2/W4 don't collide; §8.6/§4.8 keybind convergence (digits
  1-4 -> quickslots 0-3, F1-F4 -> quickslots 4-7, Ctrl+1..9 -> _emote()).

Tests: cmake --build build clean; ctest 16/16; 15 GDScript regressions green
(netbridge, gamescene, netplay, p9, p10, p2b, p8, system_menu_ui, skill,
combat_fx, skill_fx, player_motion, char_status_ui, chat, inventory), no skips.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yvAPqPivoY7vmBbzgqK4W
2026-09-02 15:13:01 +09:00

78 lines
2.9 KiB
GDScript

# WorldTime (P9) —— 昼夜循环:GC_TIME 的服务器时钟 → 太阳角度 + 光色 + 环境色。
#
# var wt := preload("res://world/world_time.gd").new()
# add_child(wt)
# wt.setup(m2client, sun_light, environment) # environment 可空
#
# GC_TIME 给的是 unix 秒。本地按 delta 累加,得到「日内秒」(0..86400)。
# 06:00 日出、12:00 正午、18:00 日落、0:00 午夜。太阳 pitch 随之走。
extends Node
const DAY := 86400.0
const CYCLE_SCALE := 1.0 # >1 加速昼夜(调试用)
var client: Node
var sun: DirectionalLight3D
var env: Environment
var _epoch := 0.0 # 最近一次 GC_TIME 的服务器 unix 秒
var _since := 0.0 # 收到后本地经过的秒
var _have_time := false
func setup(m2client: Node, sun_light: DirectionalLight3D, environment: Environment = null) -> void:
client = m2client
sun = sun_light
env = environment
if client and client.has_signal("time_changed"):
client.time_changed.connect(_on_time)
if client and client.has_method("get_server_time"):
var t: int = client.get_server_time()
if t > 0:
_on_time(t)
func set_lighting(sun_light: DirectionalLight3D, environment: Environment = null) -> void:
sun = sun_light
env = environment
func _on_time(server_epoch: int) -> void:
_epoch = float(server_epoch)
_since = 0.0
_have_time = true
# 当前日内秒(0..86400)。没有 GC_TIME 时固定用「正午」——不要跟着玩家的系统钟
# 走到半夜把整个场景变黑(那不是 m2dev-client-main 的行为)。
const DEFAULT_NOON := 43200.0
func seconds_of_day() -> float:
if not _have_time:
return DEFAULT_NOON
return fmod((_epoch + _since) * CYCLE_SCALE, DAY)
# 0 = 午夜, 0.25 = 日出, 0.5 = 正午, 0.75 = 日落
func day_fraction() -> float:
return seconds_of_day() / DAY
func _process(delta: float) -> void:
_since += delta
if sun == null:
return
var f := day_fraction()
# 太阳高度角:正午(0.5)最高 ~ -85°(朝下),午夜最低 ~ +85°(朝上,被地平线挡)
var elevation := sin((f - 0.25) * TAU) * 80.0 # -80..80
var azimuth := lerpf(40.0, 220.0, f)
sun.rotation = Vector3(deg_to_rad(-elevation), deg_to_rad(azimuth), 0.0)
# 光照强度 & 颜色:白天暖白,黄昏偏橙,夜里近黑冷蓝
var daylight := clampf(sin((f - 0.25) * TAU) * 1.4 + 0.3, 0.0, 1.0)
sun.light_energy = lerpf(0.03, 1.15, daylight)
var warm := Color(1.0, 0.55, 0.30)
var noon := Color(1.0, 0.97, 0.90)
var night := Color(0.35, 0.42, 0.65)
if daylight < 0.35:
sun.light_color = night.lerp(warm, daylight / 0.35)
else:
sun.light_color = warm.lerp(noon, (daylight - 0.35) / 0.65)
if env:
env.ambient_light_energy = lerpf(0.12, 0.6, daylight)
env.ambient_light_color = Color(0.16, 0.19, 0.30).lerp(Color(0.5, 0.55, 0.62), daylight)
if env.background_mode == Environment.BG_COLOR:
env.background_color = Color(0.03, 0.04, 0.09).lerp(Color(0.55, 0.62, 0.72), daylight)