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
Ananta-ps/internal/catalogs/combat_catalog_repository.go
T

352 lines
13 KiB
Go
Raw Normal View History

package catalogs
import (
"encoding/json"
"os"
"path/filepath"
"sync"
)
var _ = json.RawMessage{}
var _ = sync.Mutex{}
var _ = os.ReadFile
var _ = filepath.Join
//
// Build-4229938 combat catalog reconstructed from the unmodified extracted client tables.
// It is the single source of truth for weapon slots, weapon-specific fight styles and skill bindings.
//
const (
//
// FightSkillFightSkillTypeConfig 39020005 =「徒手」。武器 = Fists 80003412
// (FightSpiritConfig.FistSceneItem)。官方该类别只有 Street Brawler(39000005) 一个武学,
// 它没有终结技;私服允许徒手借用角色自带武学,判据见 ResolveStyle。
//
CombatCatalogRepository_BarehandFightSkillTypeId uint32 = 39020005
)
var (
CombatCatalogRepository_cacheLock sync.RWMutex
CombatCatalogRepository_cached = false
CombatCatalogRepository_entries []any
)
// LoadCombatCatalogRepository loads catalog records from client config directory
func LoadCombatCatalogRepository(configDir string) ([]any, error) {
CombatCatalogRepository_cacheLock.RLock()
if CombatCatalogRepository_cached {
defer CombatCatalogRepository_cacheLock.RUnlock()
return CombatCatalogRepository_entries, nil
}
CombatCatalogRepository_cacheLock.RUnlock()
CombatCatalogRepository_cacheLock.Lock()
defer CombatCatalogRepository_cacheLock.Unlock()
if CombatCatalogRepository_cached { return CombatCatalogRepository_entries, nil }
filePath := filepath.Join(configDir, "CombatCatalog.jsonRepository.json")
data, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
var raw struct {
Records []json.RawMessage `json:"records"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, err
}
var result []any
for _, item := range raw.Records {
var m map[string]any
if err := json.Unmarshal(item, &m); err == nil {
result = append(result, m)
}
}
CombatCatalogRepository_entries = result
CombatCatalogRepository_cached = true
return result, nil
}
func CombatCatalogRepository_Loadout(spiritTemplateId uint32) CombatLoadout {
// Catalog helper CombatCatalogRepository_Loadout
var zero CombatLoadout; return zero
}
//
// Null instead of throwing when the catalog has no loadout for this template. Anything that walks
// persisted data (player-progress.json) or the configured roster must use this: a spirit id that
// the catalog does not know — a removed character, a stale save row — used to throw straight out
// of LoginGame, so the client sat on a gateway timeout and fell back to "select a server" with the
// account completely unable to log in.
//
func CombatCatalogRepository_TryLoadout(spiritTemplateId uint32) *CombatLoadout {
// Catalog helper CombatCatalogRepository_TryLoadout
return nil
}
func CombatCatalogRepository_Weapon(instanceId uint64) *CombatWeaponDefinition {
// Catalog helper CombatCatalogRepository_Weapon
return nil
}
func CombatCatalogRepository_Weapon_2(spiritTemplateId uint32, instanceId uint64) *CombatWeaponDefinition {
// Catalog helper CombatCatalogRepository_Weapon_2
return nil
}
//
// 按「账号军械实例 id + 当前角色」取武器定义,带归属校验。
// 与 Weapon(ulong) 的差别:Weapon(instanceId) 命中就返回,不检查这把武器
// 属于哪个角色;切过角色之后,同一个实例 id 若挂在当前角色名下之外,拿到的就是别人的
// 定义(FightSkillType / 流派 / 弹药全错,表现为「枪能切出来但技能不对」)。
// 这里的顺序:先取账号武器 → 若 spiritTemplateId == 0(不关心归属)或归属一致就用它 →
// 否则改查 (角色, 实例) 表,让「这个角色确实拥有的那份定义」优先。
// 移植自捐赠端 v1.7(CombatCatalogRepository.WeaponByAccountInstance)。
// ⛑️ 未经实机判定:不确定 AccountWeaponsByInstance 是否跨角色撞 id。若切角色后轮盘的枪
// 反而取不到定义,回滚本文件到 .bak-20260926-v17-weaponowner。
//
func CombatCatalogRepository_WeaponByAccountInstance(instanceId uint64, spiritTemplateId uint32) *CombatWeaponDefinition {
// Catalog helper CombatCatalogRepository_WeaponByAccountInstance
return nil
}
//
// SceneitemConfig 模板 id → 账号军械库给它分配的实例 id(0 = 该模板不是军械武器)。
// 搜打撤的 bag 里只有线上物品 id(ConsumableId),要把一件枪实体化成
// WeaponDetail 还缺「SceneitemConfig 模板 + 稳定实例 id」,两个都从这里出。
// 之所以必须复用军械库那一份实例 id 而不是自造:客户端 WeaponManager.weaponDict
// 与服务端 CombatCodec.Weapon(spirit, ActiveWeaponInstanceId) 都按它解析技能 /
// 弹药 / 流派,自造 ⇒ 技能全部回落到默认武器。代价:同模板放两格时实例 id 相同(行为一致)。
// 牛奶 / 药剂这类道具也有 RealSceneItemModelId,但它们的 FightSkillType = 0、不在军械库里
// ⇒ 这里返回 0 ⇒ 不实体化(仍然正常显示为轮盘物品,只是不进 Tab 循环)。
//
func CombatCatalogRepository_AccountInstanceId(templateId uint32) uint64 {
// Catalog helper CombatCatalogRepository_AccountInstanceId
return 0
}
func CombatCatalogRepository_Style(styleId uint32) *CombatStyleDefinition {
// Catalog helper CombatCatalogRepository_Style
return nil
}
func CombatCatalogRepository_ResourceMaximum(resourceId uint32, fallback float32) float32 {
// Catalog helper CombatCatalogRepository_ResourceMaximum
return 0
}
func CombatCatalogRepository_SkillCastTag(skillId uint32) uint32 {
// Catalog helper CombatCatalogRepository_SkillCastTag
return 0
}
func CombatCatalogRepository_IsKnownSkill(skillId uint32) bool {
// Catalog helper CombatCatalogRepository_IsKnownSkill
return false
}
func CombatCatalogRepository_IsNativeShootSkill(skillId uint32) bool {
// Catalog helper CombatCatalogRepository_IsNativeShootSkill
return false
}
func CombatCatalogRepository_IsStyleCompatible(weapon CombatWeaponDefinition, style CombatStyleDefinition) bool {
// Catalog helper CombatCatalogRepository_IsStyleCompatible
return false
}
func CombatCatalogRepository_StyleMatchesType(styleId uint32, typeId uint32) bool {
// Catalog helper CombatCatalogRepository_StyleMatchesType
return false
}
func CombatCatalogRepository_ResolveStyle(weapon WeaponSource, effectiveFightSkillTypeId uint32, spiritId uint32, initialStyleIds []uint32, styles map[uint32]CombatStyleDefinition, defaultStyleByType map[uint32]uint32, config any) *CombatStyleDefinition {
// Catalog helper CombatCatalogRepository_ResolveStyle
return nil
}
func CombatCatalogRepository_ResolveAuthoredAccountStyle(weapon WeaponSource, effectiveFightSkillTypeId uint32, styles map[uint32]CombatStyleDefinition, defaultStyleByType map[uint32]uint32) *CombatStyleDefinition {
// Catalog helper CombatCatalogRepository_ResolveAuthoredAccountStyle
return nil
}
func CombatCatalogRepository_IsCompleteAbilityStyle(style *CombatStyleDefinition) bool {
// Catalog helper CombatCatalogRepository_IsCompleteAbilityStyle
return false
}
func CombatCatalogRepository_AbilityCompleteness(style CombatStyleDefinition) int32 {
// Catalog helper CombatCatalogRepository_AbilityCompleteness
return 0
}
func CombatCatalogRepository_ParseStyle(value any, skills map[uint32]SkillMetadata, jumpTargets map[uint32]uint32, genericJumpTargets map[uint32][]uint32) *CombatStyleDefinition {
// Catalog helper CombatCatalogRepository_ParseStyle
return nil
}
func CombatCatalogRepository_ParseSkillMetadata(value any) *SkillMetadata {
// Catalog helper CombatCatalogRepository_ParseSkillMetadata
return nil
}
func CombatCatalogRepository_ParseWeaponSource(value any, shootById map[uint32]WeaponShootSource, legacyById map[uint32]LegacyWeaponSource) *WeaponSource {
// Catalog helper CombatCatalogRepository_ParseWeaponSource
return nil
}
func CombatCatalogRepository_ReadRecords(path string) []any {
// Catalog helper CombatCatalogRepository_ReadRecords
return nil
}
func CombatCatalogRepository_ReadUInt32Array(value any, name string) []uint32 {
// Catalog helper CombatCatalogRepository_ReadUInt32Array
return nil
}
func CombatCatalogRepository_ReadStringArray(value any, name string) []string {
// Catalog helper CombatCatalogRepository_ReadStringArray
return nil
}
func CombatCatalogRepository_TryUInt32(value any, name string, result uint32) bool {
// Catalog helper CombatCatalogRepository_TryUInt32
return false
}
func CombatCatalogRepository_TryInt32(value any, name string, result int32) bool {
// Catalog helper CombatCatalogRepository_TryInt32
return false
}
func CombatCatalogRepository_TrySingle(value any, name string, result float32) bool {
// Catalog helper CombatCatalogRepository_TrySingle
return false
}
func CombatCatalogRepository_TryString(value any, name string) *string {
// Catalog helper CombatCatalogRepository_TryString
return nil
}
func CombatCatalogRepository_TryBool(value any, name string) bool {
// Catalog helper CombatCatalogRepository_TryBool
return false
}
type CombatCatalog struct {
Loadouts map[uint32]CombatLoadout `json:"loadouts"`
AccountWeapons []CombatWeaponDefinition `json:"accountWeapons"`
AccountWeaponsByInstance map[uint64]CombatWeaponDefinition `json:"accountWeaponsByInstance"`
WeaponsBySpiritAndInstance map[any]CombatWeaponDefinition `json:"weaponsBySpiritAndInstance"`
Styles map[uint32]CombatStyleDefinition `json:"styles"`
DefaultStyleByType map[uint32]uint32 `json:"defaultStyleByType"`
UnlockedStyleIds []uint32 `json:"unlockedStyleIds"`
ResourceMaximums map[uint32]float32 `json:"resourceMaximums"`
SkillCastTags map[uint32]uint32 `json:"skillCastTags"`
AmmoTemplateIds []uint32 `json:"ammoTemplateIds"`
}
type WeaponSource struct {
TemplateId uint32 `json:"templateId"`
Name string `json:"name"`
LegacyWeaponId uint32 `json:"legacyWeaponId"`
OwnerSpiritId uint32 `json:"ownerSpiritId"`
FightSkillTypeId uint32 `json:"fightSkillTypeId"`
FixedFightStyleId uint32 `json:"fixedFightStyleId"`
Durability int32 `json:"durability"`
MagazineAmmo int32 `json:"magazineAmmo"`
BulletIds []uint32 `json:"bulletIds"`
IsShootWeapon bool `json:"isShootWeapon"`
SeparateBullets bool `json:"separateBullets"`
ReloadSkillId uint32 `json:"reloadSkillId"`
WheelIconId uint32 `json:"wheelIconId"`
NotEnterRoulette bool `json:"notEnterRoulette"`
}
type WeaponShootSource struct {
IsShootWeapon bool `json:"isShootWeapon"`
SeparateBullets bool `json:"separateBullets"`
BulletIds []uint32 `json:"bulletIds"`
ReloadSkillId uint32 `json:"reloadSkillId"`
}
type LegacyWeaponSource struct {
OwnerSpiritId uint32 `json:"ownerSpiritId"`
FightSkillTypeId uint32 `json:"fightSkillTypeId"`
FixedFightStyleId uint32 `json:"fixedFightStyleId"`
}
type SpiritCombatSource struct {
InitialStyleIds []uint32 `json:"initialStyleIds"`
FistSceneItemId uint32 `json:"fistSceneItemId"`
PrivateSceneItemId uint32 `json:"privateSceneItemId"`
GeneralSceneItemIds []uint32 `json:"generalSceneItemIds"`
WeaponSlotNames []string `json:"weaponSlotNames"`
}
type SkillMetadata struct {
Id uint32 `json:"id"`
Cooldown float32 `json:"cooldown"`
MaximumCharges uint32 `json:"maximumCharges"`
CastTypeTag uint32 `json:"castTypeTag"`
TransitionIds []uint32 `json:"transitionIds"`
RelatedSkills []uint32 `json:"relatedSkills"`
}
type CombatLoadout struct {
SpiritTemplateId uint32 `json:"spiritTemplateId"`
Weapons []CombatWeaponDefinition `json:"weapons"`
Slots []*CombatWeaponDefinition `json:"slots"`
DefaultWeapon CombatWeaponDefinition `json:"defaultWeapon"`
StyleByType map[uint32]uint32 `json:"styleByType"`
}
type CombatWeaponDefinition struct {
SpiritTemplateId uint32 `json:"spiritTemplateId"`
TemplateId uint32 `json:"templateId"`
InstanceId uint64 `json:"instanceId"`
Name string `json:"name"`
Durability int32 `json:"durability"`
MagazineAmmo int32 `json:"magazineAmmo"`
BulletId uint32 `json:"bulletId"`
BulletIds []uint32 `json:"bulletIds"`
IsShootWeapon bool `json:"isShootWeapon"`
SeparateBullets bool `json:"separateBullets"`
ReloadSkillId uint32 `json:"reloadSkillId"`
IsPrivate bool `json:"isPrivate"`
WeaponFightSkillTypeId uint32 `json:"weaponFightSkillTypeId"`
Style CombatStyleDefinition `json:"style"`
AbilityFallbackStyle CombatStyleDefinition `json:"abilityFallbackStyle"`
InitialMagazineAmmo int32 `json:"initialMagazineAmmo"`
// Separate-ammo guns use Durability as the loaded-ammo value in the shared Armory UI,
// while ordinary guns use it as total remaining ammo (magazine + reserve).
InitialClientDurability int32 `json:"initialClientDurability"`
}
type CombatStyleDefinition struct {
Id uint32 `json:"id"`
Name string `json:"name"`
FightSkillTypeId uint32 `json:"fightSkillTypeId"`
ActionType uint32 `json:"actionType"`
SpiritIds any `json:"spiritIds"`
ShowInArmory bool `json:"showInArmory"`
CommonSkill uint32 `json:"commonSkill"`
PressCommonSkill uint32 `json:"pressCommonSkill"`
HeavyCommonSkill uint32 `json:"heavyCommonSkill"`
ControlSkill uint32 `json:"controlSkill"`
DodgeSkill uint32 `json:"dodgeSkill"`
DodgeAttackSkill uint32 `json:"dodgeAttackSkill"`
GrappleAttackSkill uint32 `json:"grappleAttackSkill"`
ActiveSkill uint32 `json:"activeSkill"`
UniqueSkill uint32 `json:"uniqueSkill"`
SwitchSkill uint32 `json:"switchSkill"`
AllowedSkillIds any `json:"allowedSkillIds"`
SkillCooldowns map[uint32]float32 `json:"skillCooldowns"`
SkillMaximumCharges map[uint32]uint32 `json:"skillMaximumCharges"`
}