All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
feat(xmlres): 使用rawFlexibleString替换字符串类型以支持灵活解析 - 将EffectArg结构体中的SideEffectArg字段类型从string改为rawFlexibleString - 将Move结构体中的Name字段类型从string改为rawFlexibleString,并更新反序列化逻辑 - 统一配置文件解析方式,移除磁盘回退机制并简化readConfigContent函数 - 移除不再使用的导入包和变量 fix(fight): 修复战斗系统中的空技能和无效数据问题 - 在
218 lines
7.9 KiB
Go
218 lines
7.9 KiB
Go
package xmlres
|
||
|
||
import (
|
||
"encoding/json"
|
||
"encoding/xml"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
func ParseSideEffectArgs(argsStr string) []int {
|
||
if argsStr == "" {
|
||
return []int{}
|
||
}
|
||
|
||
parts := strings.Fields(argsStr)
|
||
args := make([]int, 0, len(parts))
|
||
|
||
for _, part := range parts {
|
||
if num, err := strconv.Atoi(part); err == nil {
|
||
args = append(args, num)
|
||
}
|
||
}
|
||
|
||
return args
|
||
}
|
||
|
||
// MovesTbl 定义 XML 根元素
|
||
type MovesTbl struct {
|
||
XMLName xml.Name `xml:"MovesTbl"`
|
||
Moves []Move `xml:"Moves>Move"`
|
||
EFF []SideEffect `xml:"SideEffects>SideEffect"`
|
||
}
|
||
|
||
type MovesJSON struct {
|
||
MovesTbl MovesJSONRoot `json:"MovesTbl"`
|
||
}
|
||
|
||
type MovesJSONRoot struct {
|
||
Moves struct {
|
||
Move []Move `json:"Move"`
|
||
} `json:"Moves"`
|
||
SideEffects struct {
|
||
SideEffect []SideEffect `json:"SideEffect"`
|
||
} `json:"SideEffects"`
|
||
}
|
||
|
||
type MovesMap struct {
|
||
XMLName xml.Name `xml:"MovesTbl"`
|
||
Moves map[int]Move
|
||
EFF []SideEffect `xml:"SideEffects>SideEffect"`
|
||
}
|
||
|
||
type rawFlexibleString string
|
||
|
||
func (s *rawFlexibleString) UnmarshalJSON(data []byte) error {
|
||
text := strings.TrimSpace(string(data))
|
||
if text == "" || text == "null" {
|
||
*s = ""
|
||
return nil
|
||
}
|
||
|
||
if len(text) >= 2 && text[0] == '"' && text[len(text)-1] == '"' {
|
||
var decoded string
|
||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||
return err
|
||
}
|
||
*s = rawFlexibleString(decoded)
|
||
return nil
|
||
}
|
||
|
||
*s = rawFlexibleString(text)
|
||
return nil
|
||
}
|
||
|
||
// Move 定义单个技能的结构
|
||
type Move struct {
|
||
ID int `xml:"ID,attr" json:"ID"`
|
||
Name string `xml:"Name,attr" json:"Name"`
|
||
|
||
Category int `xml:"Category,attr" json:"Category"` //属性
|
||
Type int `xml:"Type,attr" json:"Type"` //类型
|
||
Power int `xml:"Power,attr" json:"Power"` //威力
|
||
MaxPP int `xml:"MaxPP,attr" json:"MaxPP"` //最大PP
|
||
Accuracy int `xml:"Accuracy,attr" json:"Accuracy"` //命中率
|
||
CritRate int `xml:"CritRate,attr,omitempty" json:"CritRate,omitempty"` //暴击率
|
||
Priority int `xml:"Priority,attr,omitempty" json:"Priority,omitempty"` //优先级
|
||
MustHit int `xml:"MustHit,attr,omitempty" json:"MustHit,omitempty"` //是否必中
|
||
SwapElemType int `xml:"SwapElemType,attr,omitempty" json:"SwapElemType,omitempty"` //技能交换属性
|
||
CopyElemType int `xml:"CopyElemType,attr,omitempty" json:"CopyElemType,omitempty"` // 技能复制属性
|
||
CritAtkFirst int `xml:"CritAtkFirst,attr,omitempty" json:"CritAtkFirst,omitempty"` // 先出手时必定致命一击
|
||
CritAtkSecond int `xml:"CritAtkSecond,attr,omitempty" json:"CritAtkSecond,omitempty"` //后出手时必定致命一击
|
||
CritSelfHalfHp int `xml:"CritSelfHalfHp,attr,omitempty" json:"CritSelfHalfHp,omitempty"` //自身体力低于一半时必定致命一击
|
||
CritFoeHalfHp int `xml:"CritFoeHalfHp,attr,omitempty" json:"CritFoeHalfHp,omitempty"` //对方体力低于一半时必定致命一击
|
||
DmgBindLv int `xml:"DmgBindLv,attr,omitempty" json:"DmgBindLv,omitempty"` //使对方受到的伤害值等于自身的等级
|
||
PwrBindDv int `xml:"PwrBindDv,attr,omitempty" json:"PwrBindDv,omitempty"` //威力(power)取决于自身的潜力(个体值)
|
||
PwrDouble int `xml:"PwrDouble,attr,omitempty" json:"PwrDouble,omitempty"` //攻击时,若对方处于异常状态, 则威力翻倍;
|
||
DmgBindHpDv int `xml:"DmgBindHpDv,attr,omitempty" json:"DmgBindHpDv,omitempty"` //使对方受到的伤害值等于自身的体力值
|
||
SideEffect string `xml:"SideEffect,attr,omitempty" json:"SideEffect,omitempty"`
|
||
SideEffectArg string `xml:"SideEffectArg,attr,omitempty" json:"SideEffectArg,omitempty"`
|
||
SideEffectS []int
|
||
SideEffectArgS []int
|
||
AtkNum int `xml:"AtkNum,attr,omitempty" json:"AtkNum,omitempty"`
|
||
AtkType int `xml:"AtkType,attr,omitempty" json:"AtkType,omitempty"` // 0:所有人 1:仅己方 2:仅对方 3:仅自己
|
||
Url string `xml:"Url,attr,omitempty" json:"Url,omitempty"`
|
||
|
||
Info string `xml:"info,attr,omitempty" json:"info,omitempty"`
|
||
|
||
CD *int `xml:"CD,attr" json:"CD"`
|
||
}
|
||
|
||
func (m *Move) UnmarshalJSON(data []byte) error {
|
||
type moveAlias struct {
|
||
ID int `json:"ID"`
|
||
Name rawFlexibleString `json:"Name"`
|
||
Category int `json:"Category"`
|
||
Type int `json:"Type"`
|
||
Power int `json:"Power"`
|
||
MaxPP int `json:"MaxPP"`
|
||
Accuracy int `json:"Accuracy"`
|
||
CritRate int `json:"CritRate,omitempty"`
|
||
Priority int `json:"Priority,omitempty"`
|
||
MustHit int `json:"MustHit,omitempty"`
|
||
SwapElemType int `json:"SwapElemType,omitempty"`
|
||
CopyElemType int `json:"CopyElemType,omitempty"`
|
||
CritAtkFirst int `json:"CritAtkFirst,omitempty"`
|
||
CritAtkSecond int `json:"CritAtkSecond,omitempty"`
|
||
CritSelfHalfHp int `json:"CritSelfHalfHp,omitempty"`
|
||
CritFoeHalfHp int `json:"CritFoeHalfHp,omitempty"`
|
||
DmgBindLv int `json:"DmgBindLv,omitempty"`
|
||
PwrBindDv int `json:"PwrBindDv,omitempty"`
|
||
PwrDouble int `json:"PwrDouble,omitempty"`
|
||
DmgBindHpDv int `json:"DmgBindHpDv,omitempty"`
|
||
SideEffect rawFlexibleString `json:"SideEffect,omitempty"`
|
||
SideEffectArg rawFlexibleString `json:"SideEffectArg,omitempty"`
|
||
AtkNum int `json:"AtkNum,omitempty"`
|
||
AtkType int `json:"AtkType,omitempty"`
|
||
Url string `json:"Url,omitempty"`
|
||
Info string `json:"info,omitempty"`
|
||
CD *int `json:"CD"`
|
||
}
|
||
|
||
var aux moveAlias
|
||
if err := json.Unmarshal(data, &aux); err != nil {
|
||
return err
|
||
}
|
||
|
||
*m = Move{
|
||
ID: aux.ID,
|
||
Name: string(aux.Name),
|
||
Category: aux.Category,
|
||
Type: aux.Type,
|
||
Power: aux.Power,
|
||
MaxPP: aux.MaxPP,
|
||
Accuracy: aux.Accuracy,
|
||
CritRate: aux.CritRate,
|
||
Priority: aux.Priority,
|
||
MustHit: aux.MustHit,
|
||
SwapElemType: aux.SwapElemType,
|
||
CopyElemType: aux.CopyElemType,
|
||
CritAtkFirst: aux.CritAtkFirst,
|
||
CritAtkSecond: aux.CritAtkSecond,
|
||
CritSelfHalfHp: aux.CritSelfHalfHp,
|
||
CritFoeHalfHp: aux.CritFoeHalfHp,
|
||
DmgBindLv: aux.DmgBindLv,
|
||
PwrBindDv: aux.PwrBindDv,
|
||
PwrDouble: aux.PwrDouble,
|
||
DmgBindHpDv: aux.DmgBindHpDv,
|
||
SideEffect: string(aux.SideEffect),
|
||
SideEffectArg: string(aux.SideEffectArg),
|
||
AtkNum: aux.AtkNum,
|
||
AtkType: aux.AtkType,
|
||
Url: aux.Url,
|
||
Info: aux.Info,
|
||
CD: aux.CD,
|
||
}
|
||
return nil
|
||
}
|
||
|
||
type SideEffect struct {
|
||
ID int `xml:"ID,attr" json:"ID"`
|
||
Help string `xml:"help,attr" json:"help"`
|
||
Des string `xml:"des,attr" json:"des"`
|
||
}
|
||
|
||
// ReadHTTPFile 通过HTTP GET请求获取远程文件内容
|
||
// url: 远程文件的URL地址
|
||
// 返回文件内容字节流和可能的错误
|
||
func ReadHTTPFile(url string) ([]byte, error) {
|
||
// 创建HTTP客户端并设置超时时间(避免无限等待)
|
||
client := &http.Client{
|
||
Timeout: 30 * time.Second, // 30秒超时
|
||
}
|
||
|
||
// 发送GET请求
|
||
resp, err := client.Get(url)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("请求失败: %w", err)
|
||
}
|
||
defer resp.Body.Close() // 确保响应体被关闭
|
||
|
||
// 检查响应状态码
|
||
if resp.StatusCode != http.StatusOK {
|
||
return nil, fmt.Errorf("请求返回非成功状态码: %d", resp.StatusCode)
|
||
}
|
||
|
||
// 读取响应体内容
|
||
content, err := io.ReadAll(resp.Body)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取内容失败: %w", err)
|
||
}
|
||
|
||
return content, nil
|
||
}
|