package gameserver import ( "encoding/json" "fmt" "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" "sync" ) type destructibleRule struct { Path string `json:"json_path"` RecoverMP uint32 `json:"recover_mp"` } var propRulesOnce sync.Once var propRules map[uint32]destructibleRule var propRulesError error func loadPropRules() (map[uint32]destructibleRule, error) { propRulesOnce.Do(func() { raw, err := common.ReadResourceFile("resources/PropInteractionConfig.json") if err != nil { propRulesError = err return } var data struct { ClientVersion string `json:"client_version"` Props map[uint32]destructibleRule `json:"destructibles"` } if err = json.Unmarshal(raw, &data); err != nil { propRulesError = err return } cfg, err := common.LoadRuntimeConfig() if err != nil { propRulesError = err return } if cfg.ClientVersion != data.ClientVersion || len(data.Props) == 0 { propRulesError = fmt.Errorf("destructible resource missing or Client revision mismatch") return } propRules = data.Props }) return propRules, propRulesError } func (s *Session) hitSceneProps(req *goproto.SceneCastSkillCsReq) []uint32 { if s.techniqueAvatar(req.CasterId) == 0 { return nil } if _, active := CURRENT_BATTLE.Load(s.Token); active { return nil } rules, err := loadPropRules() if err != nil { Log("[PROP] " + err.Error()) return nil } var ids []uint32 seen := map[uint32]bool{} add := func(id uint32) { if p, ok := s.PropEntities[id]; ok { if _, destructible := rules[p.PropID]; destructible && !seen[id] { seen[id] = true ids = append(ids, id) } } } for _, id := range req.HitTargetEntityIdList { add(id) } for _, id := range req.AssistMonsterEntityIdList { add(id) } for _, group := range req.AssistMonsterEntityInfo { for _, id := range group.GetEntityIdList() { add(id) } } return ids } func (s *Session) breakSceneProps(ids []uint32) error { rules, err := loadPropRules() if err != nil { return err } lineup := s.techniqueLineup() if lineup == nil { return nil } notify := &goproto.SceneGroupRefreshScNotify{FloorId: s.CurrentSceneFloor} groups := map[uint32]*goproto.SceneGroupRefreshInfo{} var recover uint32 for _, id := range ids { prop, exists := s.PropEntities[id] if !exists { continue } rule, ok := rules[prop.PropID] if !ok { continue } group := groups[prop.GroupID] if group == nil { group = &goproto.SceneGroupRefreshInfo{GroupId: prop.GroupID, State: 1, GroupRefreshType: goproto.SceneGroupRefreshType_SCENE_GROUP_REFRESH_TYPE_LOADED} groups[prop.GroupID] = group } group.RefreshEntity = append(group.RefreshEntity, &goproto.SceneEntityRefreshInfo{RefreshType: &goproto.SceneEntityRefreshInfo_DeleteEntity{DeleteEntity: id}}) recover += rule.RecoverMP delete(s.PropEntities, id) SceneDebugLog("[PROP] destroyed entity=%d prop=%d recover_mp=%d", id, prop.PropID, rule.RecoverMP) } for _, id := range sortedU32Keys2(groups) { notify.GroupRefreshInfo = append(notify.GroupRefreshInfo, groups[id]) } if len(notify.GroupRefreshInfo) > 0 { if err = s.Send(notify); err != nil { return err } } if recover > 0 { remaining := lineup.MaxMp - *s.techniqueMP if recover > remaining { recover = remaining } *s.techniqueMP += recover return s.syncTechnique(goproto.SyncLineupReason_SyncLineupReason_SyncReasonMpAdd) } return nil }