Files
wails-epic/internal/service/parser_service.go

983 lines
24 KiB
Go
Raw 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 service
import (
"bytes"
"encoding/json"
"equipment-analyzer/internal/config"
"equipment-analyzer/internal/model"
"equipment-analyzer/internal/parser"
"equipment-analyzer/internal/utils"
"fmt"
"io/ioutil"
"math"
"os"
"path/filepath"
"sort"
"net/http"
"strings"
"sync"
"time"
)
type ParserService struct {
config *config.Config
logger *utils.Logger
hexParser *parser.HexParser
heroBase map[string]heroBaseStats
heroOnce sync.Once
heroErr error
fallbackMainStatCount int
}
func NewParserService(cfg *config.Config, logger *utils.Logger) *ParserService {
return &ParserService{
config: cfg,
logger: logger,
hexParser: parser.NewHexParser(),
heroBase: make(map[string]heroBaseStats),
}
}
func (ps *ParserService) writeParseSnapshot(kind string, data []byte) {
if len(data) == 0 {
return
}
homeDir, err := os.UserHomeDir()
if err != nil {
return
}
dir := filepath.Join(homeDir, ".equipment-analyzer", "remote_parse_snapshots")
if err := os.MkdirAll(dir, 0755); err != nil {
return
}
stamp := time.Now().Format("20060102_150405.000")
filename := filepath.Join(dir, stamp+"_"+kind+".json")
_ = os.WriteFile(filename, data, 0644)
}
// ParseHexData 解析十六进制数据
func (ps *ParserService) ParseHexData(hexDataList []string) (*model.ParsedResult, string, error) {
if len(hexDataList) == 0 {
ps.logger.Warn("no data to parse")
return &model.ParsedResult{
Items: make([]interface{}, 0),
Heroes: make([]interface{}, 0),
}, "", nil
}
ps.logger.Info("remote parse start", "count", len(hexDataList))
url := "https://krivpfvxi0.execute-api.us-west-2.amazonaws.com/dev/getItems"
reqBody := map[string]interface{}{
"data": hexDataList,
}
jsonBytes, _ := json.Marshal(reqBody)
ps.writeParseSnapshot("request", jsonBytes)
client := &http.Client{Timeout: 60 * time.Second}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
if err == nil {
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err == nil && resp.StatusCode == 200 {
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
ps.writeParseSnapshot("response", body)
var raw map[string]interface{}
if err := json.Unmarshal(body, &raw); err != nil {
ps.logger.Error("remote json unmarshal failed", "error", err)
return nil, "", fmt.Errorf("remote json unmarshal failed: %v", err)
}
dataArr, dataOk := raw["data"].([]interface{})
if !dataOk || len(dataArr) == 0 {
ps.logger.Error("remote json validate failed: data missing or empty")
return nil, "", fmt.Errorf("remote json validate failed: data missing or empty")
}
ps.logger.Info("remote json validate ok, start parse")
parsedResult, err := ps.ReadRawJsonFile(string(body))
if err != nil {
return nil, "", err
}
return parsedResult, "", nil
} else if err != nil {
ps.logger.Error("remote parse request failed", "error", err)
return nil, "", fmt.Errorf("remote parse request failed: %v", err)
} else {
ps.logger.Error("remote parse http status", "status", resp.StatusCode)
return nil, "", fmt.Errorf("remote parse http status %d", resp.StatusCode)
}
} else {
ps.logger.Error("remote parse request build failed", "error", err)
return nil, "", fmt.Errorf("remote parse request build failed: %v", err)
}
}
// ReadRawJsonFile 读取rawJson内容并进行数据转换返回ParsedResult对象
func (ps *ParserService) ReadRawJsonFile(rawJson string) (*model.ParsedResult, error) {
var rawData map[string]interface{}
if err := json.Unmarshal([]byte(rawJson), &rawData); err != nil {
ps.logger.Error("解析JSON失败", "error", err)
return nil, err
}
// If input is already parsed (gear.txt export), use items/heroes directly.
if itemsRaw, ok := rawData["items"].([]interface{}); ok {
heroesRaw, _ := rawData["heroes"].([]interface{})
parsedItems := ps.normalizeParsedItems(itemsRaw)
result := &model.ParsedResult{
Items: parsedItems,
Heroes: heroesRaw,
GearTxt: rawJson,
}
return result, nil
}
// If input is a mixed "data" array (remote parse response), split items/heroes.
if dataRaw, ok := rawData["data"].([]interface{}); ok && len(dataRaw) > 0 {
dataItems, dataHeroes := ps.splitDataArray(dataRaw)
if len(dataItems) > 0 || len(dataHeroes) > 0 {
ps.logger.Info("raw json format: mixed data array", "items", len(dataItems), "heroes", len(dataHeroes))
// If heroes are not present in data array, fall back to units.
var rawUnits []interface{}
if unitsRaw, ok := rawData["units"].([]interface{}); ok && len(unitsRaw) > 0 {
maxLen := 0
for _, u := range unitsRaw {
if arr, ok := u.([]interface{}); ok && len(arr) > maxLen {
maxLen = len(arr)
rawUnits = arr
}
}
}
parsedHeroes := dataHeroes
if len(rawUnits) > 0 {
parsedHeroes = rawUnits
}
ps.fallbackMainStatCount = 0
convertedItems := ps.convertItemsAllWithLog(dataItems)
ps.logger.Info("main stat fallback count", "count", ps.fallbackMainStatCount)
convertedHeroes := ps.convertUnits(parsedHeroes)
result := &model.ParsedResult{
Items: make([]interface{}, len(convertedItems)),
Heroes: make([]interface{}, len(convertedHeroes)),
}
for i, v := range convertedItems {
result.Items[i] = v
}
for i, v := range convertedHeroes {
result.Heroes[i] = v
}
result.GearTxt = rawJson
return result, nil
}
}
// 提取装备和英雄数?
equips, _ := rawData["data"].([]interface{})
// 修正units 取最大长度的那组
var rawUnits []interface{}
if unitsRaw, ok := rawData["units"].([]interface{}); ok && len(unitsRaw) > 0 {
maxLen := 0
for _, u := range unitsRaw {
if arr, ok := u.([]interface{}); ok && len(arr) > maxLen {
maxLen = len(arr)
rawUnits = arr
}
}
}
// 过滤有效装备 (x => !!x.f)
var validEquips []interface{}
for _, equip := range equips {
if equipMap, ok := equip.(map[string]interface{}); ok {
if f, exists := equipMap["f"]; exists && f != nil && f != "" {
validEquips = append(validEquips, equip)
}
}
}
// 转换装备数据
ps.fallbackMainStatCount = 0
convertedItems := ps.convertItemsAllWithLog(validEquips)
ps.logger.Info("main stat fallback count", "count", ps.fallbackMainStatCount)
// 转换英雄数据(只对最大组?
convertedHeroes := ps.convertUnits(rawUnits)
result := &model.ParsedResult{
Items: make([]interface{}, len(convertedItems)),
Heroes: make([]interface{}, len(convertedHeroes)),
}
for i, v := range convertedItems {
result.Items[i] = v
}
for i, v := range convertedHeroes {
result.Heroes[i] = v
}
result.GearTxt = rawJson
return result, nil
}
func (ps *ParserService) isGearRecord(itemMap map[string]interface{}) bool {
// Require set field if present in this raw format.
if f, ok := itemMap["f"].(string); ok && f != "" {
return true
}
// Allow already-normalized gear fields from other sources.
if _, ok := itemMap["set"]; ok {
return true
}
if _, ok := itemMap["gear"]; ok {
return true
}
if _, ok := itemMap["type"]; ok {
return true
}
if _, ok := itemMap["mainStatType"]; ok {
return true
}
// Otherwise treat as non-gear to avoid including material-like records.
return false
}
func (ps *ParserService) isHeroRecord(itemMap map[string]interface{}) bool {
if name, ok := itemMap["name"].(string); ok && name != "" && name != "Unknown" {
return true
}
if code, ok := itemMap["code"].(string); ok && strings.HasPrefix(code, "c") {
return true
}
// heroes commonly have opt/exp fields
if _, ok := itemMap["opt"]; ok {
return true
}
if _, ok := itemMap["exp"]; ok {
return true
}
return false
}
func (ps *ParserService) splitDataArray(data []interface{}) (items []interface{}, heroes []interface{}) {
for _, entry := range data {
itemMap, ok := entry.(map[string]interface{})
if !ok || itemMap == nil {
continue
}
if ps.isGearRecord(itemMap) {
items = append(items, entry)
continue
}
if ps.isHeroRecord(itemMap) {
heroes = append(heroes, entry)
continue
}
}
return items, heroes
}
func (ps *ParserService) normalizeParsedItems(items []interface{}) []interface{} {
for i, item := range items {
itemMap, ok := item.(map[string]interface{})
if !ok || itemMap == nil {
continue
}
if _, exists := itemMap["main"]; exists {
continue
}
op, opExists := itemMap["op"].([]interface{})
if !opExists || len(op) == 0 {
continue
}
mainOp, ok := op[0].([]interface{})
if !ok || len(mainOp) < 2 {
continue
}
mainOpType, _ := mainOp[0].(string)
mainOpValue, _ := mainOp[1].(float64)
if mainOpType == "" {
continue
}
mainType := statByIngameStat[mainOpType]
if mainType == "" {
continue
}
var mainValue float64
if ps.isFlat(mainOpType) {
mainValue = mainOpValue
} else {
mainValue = ps.round10ths(mainOpValue * 100)
}
if mainValue == 0 || mainValue != mainValue {
mainValue = 0
}
itemMap["main"] = map[string]interface{}{
"type": mainType,
"value": mainValue,
}
items[i] = itemMap
}
return items
}
// convertItems 转换装备数据
func (ps *ParserService) convertItems(rawItems []interface{}) []map[string]interface{} {
var convertedItems []map[string]interface{}
for _, rawItem := range rawItems {
if itemMap, ok := rawItem.(map[string]interface{}); ok {
convertedItem := ps.convertSingleItem(itemMap)
if convertedItem != nil {
convertedItems = append(convertedItems, convertedItem)
}
}
}
var filteredItems []map[string]interface{}
for _, item := range convertedItems {
filteredItems = append(filteredItems, item)
}
return filteredItems
}
// convertSingleItem 转换单个装备
func (ps *ParserService) convertSingleItem(item map[string]interface{}) map[string]interface{} {
converted := make(map[string]interface{})
// 复制基本字段
for key, value := range item {
converted[key] = value
}
// 转换装备类型
ps.convertGear(converted)
// 转换等级
ps.convertRank(converted)
// 转换套装
ps.convertSet(converted)
// 转换名称
ps.convertName(converted)
// 转换等级
ps.convertLevel(converted)
// 转换增强
ps.convertEnhance(converted)
// 转换主属?
ps.convertMainStat(converted)
// 转换副属?
ps.convertSubStats(converted)
// 转换ID
ps.convertId(converted)
// 转换装备ID
ps.convertEquippedId(converted)
return converted
}
// convertUnits 转换英雄数据
func (ps *ParserService) convertUnits(rawUnits []interface{}) []map[string]interface{} {
var convertedUnits []map[string]interface{}
ps.ensureHeroBaseData()
for _, rawUnit := range rawUnits {
if unitMap, ok := rawUnit.(map[string]interface{}); ok {
if name, exists := unitMap["name"]; exists && name != nil && name != "" {
if id, exists := unitMap["id"]; exists && id != nil {
convertedUnit := make(map[string]interface{})
for key, value := range unitMap {
convertedUnit[key] = value
}
// 转换星星和觉?
if g, exists := unitMap["g"]; exists {
convertedUnit["stars"] = g
}
if z, exists := unitMap["z"]; exists {
convertedUnit["awaken"] = z
}
if code, ok := unitMap["code"].(string); ok && code != "" {
if base, ok := ps.heroBase[code]; ok {
ps.applyHeroBase(convertedUnit, base)
}
}
convertedUnits = append(convertedUnits, convertedUnit)
}
}
}
}
return convertedUnits
}
// 转换函数实现
func (ps *ParserService) convertGear(item map[string]interface{}) {
if _, exists := item["type"]; !exists {
if code, exists := item["code"].(string); exists {
baseCode := code
if parts := strings.Split(code, "_"); len(parts) > 0 {
baseCode = parts[0]
}
if idx := len(baseCode) - 1; idx >= 0 {
gearLetter := string(baseCode[idx])
item["gear"] = gearByGearLetter[gearLetter]
}
}
} else {
if itemType, exists := item["type"].(string); exists {
item["gear"] = gearByIngameType[itemType]
}
}
}
func (ps *ParserService) convertRank(item map[string]interface{}) {
if g, exists := item["g"].(float64); exists {
rankIndex := int(g)
if rankIndex >= 0 && rankIndex < len(rankByIngameGrade) {
item["rank"] = rankByIngameGrade[rankIndex]
}
}
}
func (ps *ParserService) convertSet(item map[string]interface{}) {
if f, exists := item["f"].(string); exists {
item["set"] = setsByIngameSet[f]
}
}
func (ps *ParserService) convertName(item map[string]interface{}) {
if _, exists := item["name"]; !exists {
item["name"] = "Unknown"
}
}
func (ps *ParserService) convertLevel(item map[string]interface{}) {
if _, exists := item["level"]; !exists {
item["level"] = 0
}
}
func (ps *ParserService) convertEnhance(item map[string]interface{}) {
rank, rankExists := item["rank"].(string)
op, opExists := item["op"].([]interface{})
if rankExists && opExists {
countByRank := map[string]int{
"Normal": 5,
"Good": 6,
"Rare": 7,
"Heroic": 8,
"Epic": 9,
}
offsetByRank := map[string]int{
"Normal": 0,
"Good": 1,
"Rare": 2,
"Heroic": 3,
"Epic": 4,
}
count := countByRank[rank]
offset := offsetByRank[rank]
subsCount := len(op) - 1
if subsCount > count {
subsCount = count
}
enhance := (subsCount - offset) * 3
if enhance < 0 {
enhance = 0
}
item["enhance"] = enhance
}
}
func (ps *ParserService) convertMainStat(item map[string]interface{}) {
op, opExists := item["op"].([]interface{})
mainStatValue, mainStatExists := item["mainStatValue"].(float64)
fallbackUsed := false
if opExists && len(op) > 0 && mainStatExists {
if mainOp, ok := op[0].([]interface{}); ok && len(mainOp) > 0 {
if mainOpType, ok := mainOp[0].(string); ok {
mainType := statByIngameStat[mainOpType]
var mainValue float64
if ps.isFlat(mainOpType) {
mainValue = mainStatValue
} else {
mainValue = ps.round10ths(mainStatValue * 100)
}
if mainValue == 0 || mainValue != mainValue { // NaN check
mainValue = 0
}
item["main"] = map[string]interface{}{
"type": mainType,
"value": mainValue,
}
}
}
}
// Fallback: if main is missing, derive from op[0] value directly.
if _, exists := item["main"]; !exists {
if opExists && len(op) > 0 {
if mainOp, ok := op[0].([]interface{}); ok && len(mainOp) >= 2 {
mainOpType, _ := mainOp[0].(string)
mainOpValue, _ := mainOp[1].(float64)
if mainOpType != "" {
mainType := statByIngameStat[mainOpType]
if mainType != "" {
var mainValue float64
if ps.isFlat(mainOpType) {
mainValue = mainOpValue
} else {
mainValue = ps.round10ths(mainOpValue * 100)
}
if mainValue == 0 || mainValue != mainValue {
mainValue = 0
}
item["main"] = map[string]interface{}{
"type": mainType,
"value": mainValue,
}
fallbackUsed = true
}
}
}
}
}
if fallbackUsed {
ps.fallbackMainStatCount++
}
}
func (ps *ParserService) convertSubStats(item map[string]interface{}) {
op, opExists := item["op"].([]interface{})
if !opExists || len(op) <= 1 {
item["substats"] = []interface{}{}
return
}
statAcc := make(map[string]map[string]interface{})
// 处理副属?(从索?开?
for i := 1; i < len(op); i++ {
if opItem, ok := op[i].([]interface{}); ok && len(opItem) >= 2 {
opType, _ := opItem[0].(string)
opValue, _ := opItem[1].(float64)
annotation := ""
if len(opItem) > 2 {
annotation, _ = opItem[2].(string)
}
statType := statByIngameStat[opType]
var value float64
if ps.isFlat(opType) {
value = opValue
} else {
value = ps.round10ths(opValue * 100)
}
if existingStat, exists := statAcc[statType]; exists {
existingStat["value"] = existingStat["value"].(float64) + value
if annotation == "c" {
existingStat["modified"] = true
} else if annotation != "u" {
rolls := existingStat["rolls"].(int) + 1
existingStat["rolls"] = rolls
existingStat["ingameRolls"] = rolls
}
} else {
rolls := 1
if annotation == "u" {
rolls = 0
}
statAcc[statType] = map[string]interface{}{
"value": value,
"rolls": rolls,
"ingameRolls": rolls,
"modified": annotation == "c",
}
}
}
}
// 转换为最终格?
var substats []interface{}
for statType, statData := range statAcc {
substat := map[string]interface{}{
"type": statType,
"value": statData["value"],
"rolls": statData["rolls"],
"ingameRolls": statData["ingameRolls"],
"modified": statData["modified"],
}
substats = append(substats, substat)
}
item["substats"] = substats
}
func (ps *ParserService) convertId(item map[string]interface{}) {
if id, exists := item["id"]; exists {
item["ingameId"] = id
}
}
func (ps *ParserService) convertEquippedId(item map[string]interface{}) {
if p, exists := item["p"]; exists {
item["ingameEquippedId"] = fmt.Sprintf("%v", p)
}
}
func (ps *ParserService) isFlat(text string) bool {
return text == "max_hp" || text == "speed" || text == "att" || text == "def"
}
func (ps *ParserService) round10ths(value float64) float64 {
return math.Round(value*10) / 10
}
// 映射常量
var (
rankByIngameGrade = []string{
"Unknown",
"Normal",
"Good",
"Rare",
"Heroic",
"Epic",
}
gearByIngameType = map[string]string{
"weapon": "Weapon",
"helm": "Helmet",
"armor": "Armor",
"neck": "Necklace",
"ring": "Ring",
"boot": "Boots",
}
gearByGearLetter = map[string]string{
"w": "Weapon",
"h": "Helmet",
"a": "Armor",
"n": "Necklace",
"r": "Ring",
"b": "Boots",
}
setsByIngameSet = map[string]string{
"set_acc": "HitSet",
"set_att": "AttackSet",
"set_coop": "UnitySet",
"set_counter": "CounterSet",
"set_cri_dmg": "DestructionSet",
"set_cri": "CriticalSet",
"set_def": "DefenseSet",
"set_immune": "ImmunitySet",
"set_max_hp": "HealthSet",
"set_penetrate": "PenetrationSet",
"set_rage": "RageSet",
"set_res": "ResistSet",
"set_revenge": "RevengeSet",
"set_scar": "InjurySet",
"set_speed": "SpeedSet",
"set_vampire": "LifestealSet",
"set_shield": "ProtectionSet",
"set_torrent": "TorrentSet",
"set_revenant": "ReversalSet",
"set_riposte": "RiposteSet",
"set_chase": "PursuitSet",
"set_opener": "WarfareSet",
}
statByIngameStat = map[string]string{
"att_rate": "AttackPercent",
"max_hp_rate": "HealthPercent",
"def_rate": "DefensePercent",
"att": "Attack",
"max_hp": "Health",
"def": "Defense",
"speed": "Speed",
"res": "EffectResistancePercent",
"cri": "CriticalHitChancePercent",
"cri_dmg": "CriticalHitDamagePercent",
"acc": "EffectivenessPercent",
"coop": "DualAttackChancePercent",
}
)
// 新增不做enhance过滤的convertItems
func (ps *ParserService) convertItemsAllWithLog(rawItems []interface{}) []map[string]interface{} {
var convertedItems []map[string]interface{}
for _, rawItem := range rawItems {
if itemMap, ok := rawItem.(map[string]interface{}); ok {
convertedItem := ps.convertSingleItem(itemMap)
if convertedItem != nil {
convertedItems = append(convertedItems, convertedItem)
}
}
}
return convertedItems
}
type heroBaseStats struct {
Atk float64
Def float64
Hp float64
Spd float64
Cr float64
Cd float64
Eff float64
Res float64
}
type heroDataEntry struct {
Code string `json:"code"`
Name string `json:"name"`
CalculatedStatus struct {
Lv60SixStarFullyAwakened struct {
Atk float64 `json:"atk"`
Def float64 `json:"def"`
Hp float64 `json:"hp"`
Spd float64 `json:"spd"`
Chc float64 `json:"chc"`
Chd float64 `json:"chd"`
Eff float64 `json:"eff"`
Efr float64 `json:"efr"`
} `json:"lv60SixStarFullyAwakened"`
} `json:"calculatedStatus"`
}
func (ps *ParserService) ensureHeroBaseData() {
ps.heroOnce.Do(func() {
homeDir, err := os.UserHomeDir()
if err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
path := filepath.Join(homeDir, ".equipment-analyzer", "herodata.json")
body, err := ioutil.ReadFile(path)
if err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
var raw map[string]heroDataEntry
if err := json.Unmarshal(body, &raw); err != nil {
ps.heroErr = err
ps.logger.Error("load hero data failed", "error", err)
return
}
for _, entry := range raw {
if entry.Code == "" {
continue
}
stats := entry.CalculatedStatus.Lv60SixStarFullyAwakened
ps.heroBase[entry.Code] = heroBaseStats{
Atk: stats.Atk,
Def: stats.Def,
Hp: stats.Hp,
Spd: stats.Spd,
Cr: stats.Chc * 100,
Cd: stats.Chd * 100,
Eff: stats.Eff * 100,
Res: stats.Efr * 100,
}
}
ps.logger.Info("hero base data loaded", "count", len(ps.heroBase))
})
}
func (ps *ParserService) FillHeroBase(heroes []model.OptimizeHero) error {
ps.ensureHeroBaseData()
if len(ps.heroBase) == 0 {
return fmt.Errorf("hero base data not loaded")
}
for i := range heroes {
code := heroes[i].Code
if code == "" {
continue
}
base, ok := ps.heroBase[code]
if !ok {
continue
}
if heroes[i].BaseAtk == 0 {
heroes[i].BaseAtk = base.Atk
}
if heroes[i].BaseDef == 0 {
heroes[i].BaseDef = base.Def
}
if heroes[i].BaseHp == 0 {
heroes[i].BaseHp = base.Hp
}
if heroes[i].BaseSpd == 0 {
heroes[i].BaseSpd = base.Spd
}
if heroes[i].BaseCr == 0 {
heroes[i].BaseCr = base.Cr
}
if heroes[i].BaseCd == 0 {
heroes[i].BaseCd = base.Cd
}
if heroes[i].BaseEff == 0 {
heroes[i].BaseEff = base.Eff
}
if heroes[i].BaseRes == 0 {
heroes[i].BaseRes = base.Res
}
if heroes[i].Atk == 0 {
heroes[i].Atk = base.Atk
}
if heroes[i].Def == 0 {
heroes[i].Def = base.Def
}
if heroes[i].Hp == 0 {
heroes[i].Hp = base.Hp
}
if heroes[i].Spd == 0 {
heroes[i].Spd = base.Spd
}
if heroes[i].Cr == 0 {
heroes[i].Cr = base.Cr
}
if heroes[i].Cd == 0 {
heroes[i].Cd = base.Cd
}
if heroes[i].Eff == 0 {
heroes[i].Eff = base.Eff
}
if heroes[i].Res == 0 {
heroes[i].Res = base.Res
}
}
return nil
}
func (ps *ParserService) GetHeroTemplates() ([]model.HeroTemplate, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, err
}
path := filepath.Join(homeDir, ".equipment-analyzer", "herodata.json")
body, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var raw map[string]heroDataEntry
if err := json.Unmarshal(body, &raw); err != nil {
return nil, err
}
list := make([]model.HeroTemplate, 0, len(raw))
for _, entry := range raw {
if entry.Code == "" {
continue
}
stats := entry.CalculatedStatus.Lv60SixStarFullyAwakened
list = append(list, model.HeroTemplate{
Code: entry.Code,
Name: entry.Name,
BaseAtk: stats.Atk,
BaseDef: stats.Def,
BaseHp: stats.Hp,
BaseSpd: stats.Spd,
BaseCr: stats.Chc * 100,
BaseCd: stats.Chd * 100,
BaseEff: stats.Eff * 100,
BaseRes: stats.Efr * 100,
})
}
sort.Slice(list, func(i, j int) bool {
return list[i].Name < list[j].Name
})
return list, nil
}
func (ps *ParserService) applyHeroBase(unit map[string]interface{}, base heroBaseStats) {
if _, ok := unit["baseAtk"]; !ok {
unit["baseAtk"] = base.Atk
}
if _, ok := unit["baseDef"]; !ok {
unit["baseDef"] = base.Def
}
if _, ok := unit["baseHp"]; !ok {
unit["baseHp"] = base.Hp
}
if _, ok := unit["baseSpd"]; !ok {
unit["baseSpd"] = base.Spd
}
if _, ok := unit["baseCr"]; !ok {
unit["baseCr"] = base.Cr
}
if _, ok := unit["baseCd"]; !ok {
unit["baseCd"] = base.Cd
}
if _, ok := unit["baseEff"]; !ok {
unit["baseEff"] = base.Eff
}
if _, ok := unit["baseRes"]; !ok {
unit["baseRes"] = base.Res
}
if _, ok := unit["atk"]; !ok {
unit["atk"] = base.Atk
}
if _, ok := unit["def"]; !ok {
unit["def"] = base.Def
}
if _, ok := unit["hp"]; !ok {
unit["hp"] = base.Hp
}
if _, ok := unit["spd"]; !ok {
unit["spd"] = base.Spd
}
if _, ok := unit["cr"]; !ok {
unit["cr"] = base.Cr
}
if _, ok := unit["cd"]; !ok {
unit["cd"] = base.Cd
}
if _, ok := unit["eff"]; !ok {
unit["eff"] = base.Eff
}
if _, ok := unit["res"]; !ok {
unit["res"] = base.Res
}
}