# mob_drop_scatter.gd —— 40250 怪物死亡爆装与辐射散射 1:1 规则引擎 # 严格对照: # metin2/src/server/game/src/char_battle.cpp:840-930 (pos.x = number(-7, 7) * 20) # metin2/src/server/game/src/item.cpp:SetOwnership (ITEM_OWNERSHIP_TIME = 30) extends RefCounted const ITEM_OWNERSHIP_DURATION := 30.0 # 40250 官方 30 秒拾取归属保护期 const CM_TO_METERS := 0.01 # 单个掉落物基础散射偏移区间:[-140cm, +140cm],即 [-1.4m, +1.4m] const MIN_SCATTER_CM := -140 const MAX_SCATTER_CM := 140 const SOUND_MONEY := "sound/ui/money.wav" const SOUND_DROP := "sound/ui/drop.wav" # 计算第 index 个掉落物相对于怪物中心的世界偏移量 # 1:1 对齐 char_battle.cpp: number(-7, 7) * 20cm static func calculate_scatter_offset(index: int, total_count: int, seed_val := -1) -> Vector3: if total_count <= 1: # 单个物品:在 [-1.4m, +1.4m] 范围内轻微偏移 var rng := RandomNumberGenerator.new() if seed_val >= 0: rng.seed = seed_val else: rng.randomize() var dx: float = (rng.randi_range(-7, 7) * 20) * CM_TO_METERS var dz: float = (rng.randi_range(-7, 7) * 20) * CM_TO_METERS return Vector3(dx, 0.0, dz) # 多个物品(首领爆装):360 度环状辐射发散,半径 1.0m ~ 1.8m var angle_step := TAU / float(maxi(1, total_count)) var base_angle := angle_step * float(index) var rng := RandomNumberGenerator.new() if seed_val >= 0: rng.seed = seed_val + index * 37 else: rng.randomize() # 半径在 1.0m ~ 1.6m 之间带微量抖动 var radius := 1.0 + rng.randf_range(0.0, 0.6) var jitter_angle := rng.randf_range(-0.2, 0.2) var angle := base_angle + jitter_angle var dx: float = cos(angle) * radius var dz: float = sin(angle) * radius return Vector3(dx, 0.0, dz) # 计算最终落地世界坐标 static func calculate_drop_position(center_pos: Vector3, index: int, total_count: int, seed_val := -1) -> Vector3: var offset := calculate_scatter_offset(index, total_count, seed_val) return center_pos + offset # 归属保护期判定 static func is_ownership_protected(drop_time: float, current_time: float) -> bool: return (current_time - drop_time) < ITEM_OWNERSHIP_DURATION # 剩余归属秒数 static func remaining_ownership_time(drop_time: float, current_time: float) -> float: return maxf(0.0, ITEM_OWNERSHIP_DURATION - (current_time - drop_time)) # 匹配掉落音效 static func get_drop_sound(is_gold: bool) -> String: return SOUND_MONEY if is_gold else SOUND_DROP