Files
Ananta-ps/internal/catalogs/combat_catalog_repository.go

353 lines
13 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 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"`
}