This repository has been archived on 2026-10-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

88 lines
4.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routers
import (
"context"
"sync"
)
var _ = sync.Mutex{}
var _ = context.Background
//
// Party-camp furniture the server places itself — 露营的烤炉 / 帐篷 / 篝火 / 野餐垫 / 灯 / 野餐车.
// WHY (2026-09-26, round 44). 用户站在露营点 2.8 m 处实测「家具全无」:烤炉、帐篷一件都没有,
// 而我们**确实**把营地那一格(32,59)通过 grid-AOI 下发过了。原因和健身房器材一模一样:
// 这批 `Gadgets_Party_*` 只活在客户端的关卡 blob(LX6.Item.GadgetBlob)里,而
// SyncGadgetGridAOIDecrease(68956797) 这条下发**不驱动**
// GadgetBlob.OnAoiAddBlobCfgByGridIndex ⇒ 烘焙里的道具一件都不会自己出现。
// 用户那句「是不是和健身器材一样」是对的。见 TickGymEquipmentAsync。
// ★ 判据(回答「是不是开派对才刷」):**不是**。家具烘焙在 raid 23300888 = 主城 free-roam,
// 而露营/舞会的 PartyConfig.RaidId 也是 23300888 ⇒ 派对就地举行、不切副本 ⇒ 场地家具只能是
// 预先常驻的世界装点。旁证:同一批 prefab 还被当作天台夜市(-378.28,130.50,-497.87)的
// 常驻装点摆着;营地 400 m 圈内全是改车店/公厕/时装店/象棋摊等主城常驻机关。
// ⇒ 所以这里是**常驻摆放**(玩家走进场地半径就摆),而不是「开派对时才摆」。
// ⛔ PackedGadgetInfo.iScale 必须是 0(100 ⇒ transform scale (0,0,1) 零尺寸空壳)。
// Rollback: world.content.partyCamp.enabled = false.
//
//
// ★★★ 2026-09-30:**就地开局** —— 把指定 partyId 的整片场地搬到玩家脚下。
//
//
// 官方把舞会 13030003 摆在 y ≈ 80 的悬空层(那层没有地板,实机够不到),
// 所以开派对时由 GameRouter 的 StartSingleParty 分支调本方法,
// 把家具整片平移到玩家坐标;下一次 grid tick 就会按新坐标摆出来。
// ⛑⛑ 必须同时把场地从 Placed 里摘掉:
// Placed 是「每会话每场地只摆一次」的去重 HashSet,而一次误判
// (玩家临时站在矩形外 ⇒ INDOOR-GATE 全挡)就会把场地永久毒死
// (2026-09-30 踩过,见 2026-09-30.md §45.2)。
// 同理,同一会话重开派对时也必须解掉,否则第二场一件家具都没有。
// 传入的 必须是地面基准(玩家上报 y − 眼高 ≈ 1.07 m),
// 不是玩家上报值 —— 角色上报的是胶囊中心,直接用会整体浮空 1 m。
// 是开派对那一刻玩家的朝向(度)。partyVenuePropFacePlayer
// 打开时,家具整片会按它旋转,让最远那件(舞会 = 灯光控制台)落在玩家正前方;
// 传 0(或开关关闭)则保持官方的世界方位。
//
func GameRouter_PartyCamp_SetPartyVenueAtPlayer(ctx context.Context, session *GameSession, partyId uint32, x float32, y float32, z float32, facingDeg float32) any {
// Router implementation for GameRouter_PartyCamp_SetPartyVenueAtPlayer
var zero any; return zero
}
func GameRouter_PartyCamp_PartyCampStateOf(ctx context.Context, session *GameSession) any {
// Router implementation for GameRouter_PartyCamp_PartyCampStateOf
var zero any; return zero
}
// Reconnect / scene switch: forget what was stocked so the next visit re-places it.
func GameRouter_PartyCamp_ResetPartyCamp(ctx context.Context, session *GameSession) any {
// Router implementation for GameRouter_PartyCamp_ResetPartyCamp
var zero any; return zero
}
//
// Called from the grid-AOI tick (which already knows the player position). Places every camp
// the player has walked into, once per camp per session.
//
func GameRouter_PartyCamp_TickPartyCampAsync(ctx context.Context, session *GameSession, state any, px float32, pz float32) any {
// Router implementation for GameRouter_PartyCamp_TickPartyCampAsync
var zero any; return zero
}
type PartyCampSessionState struct {
SyncRoot any `json:"syncRoot"`
// Camp sites already stocked this session. One shot per site, like the gym.
Placed []string `json:"placed"`
PlacedCount int32 `json:"placedCount"`
WarnedEmpty bool `json:"warnedEmpty"`
// ★ 2026-09-30 就地开局:把整片场地临时搬到玩家脚下(0 = 未启用,走配表锚点)。
VenueAtPlayerPartyId uint32 `json:"venueAtPlayerPartyId"`
VenueAtPlayerX float32 `json:"venueAtPlayerX"`
VenueAtPlayerY float32 `json:"venueAtPlayerY"`
VenueAtPlayerZ float32 `json:"venueAtPlayerZ"`
//
// 就地开局那一刻玩家的朝向(度)。家具整片靠它旋转到玩家正前方
// (见 SetPartyVenueAtPlayer)。0 = 保持官方世界方位。
//
VenueAtPlayerFacingDeg float32 `json:"venueAtPlayerFacingDeg"`
}