diff --git a/internal/handler/announce/index.go b/internal/handler/announce/index.go
index 45eb76e..55b52af 100644
--- a/internal/handler/announce/index.go
+++ b/internal/handler/announce/index.go
@@ -14,7 +14,7 @@ const announceContent = `目前开发完毕的主要功能包括:
-
func index(ctx *gin.Context) {
ctx.HTML(http.StatusOK, "common/announce.html", gin.H{
- "title": "Love Live! 学园偶像祭",
+ "title": "Love Live! 学园偶像祭",
"content": template.HTML(announceContent),
})
}
diff --git a/internal/handler/live/live.go b/internal/handler/live/live.go
new file mode 100644
index 0000000..ee6abf6
--- /dev/null
+++ b/internal/handler/live/live.go
@@ -0,0 +1,285 @@
+package live
+
+import (
+ "encoding/json"
+ "fmt"
+ unitmodel "honoka-chan/internal/model/unit"
+ usermodel "honoka-chan/internal/model/user"
+ liveschema "honoka-chan/internal/schema/live"
+ "honoka-chan/internal/session"
+ "honoka-chan/pkg/utils"
+ "math"
+)
+
+type liveSettingData struct {
+ NotesSettingAsset string `xorm:"notes_setting_asset"`
+ ARankScore int `xorm:"a_rank_score"`
+ BRankScore int `xorm:"b_rank_score"`
+ CRankScore int `xorm:"c_rank_score"`
+ SRankScore int `xorm:"s_rank_score"`
+ AcFlag int `xorm:"ac_flag"`
+ SwingFlag int `xorm:"swing_flag"`
+}
+
+type leaderSkillData struct {
+ AttributeID int `xorm:"attribute_id"`
+ MainEffectValue float64 `xorm:"main_effect_value"`
+ ExtraEffectValue float64 `xorm:"extra_effect_value"`
+ MemberTagID int `xorm:"member_tag_id"`
+}
+
+type museumBuff struct {
+ Smile float64 `xorm:"smile_buff"`
+ Pure float64 `xorm:"pure_buff"`
+ Cool float64 `xorm:"cool_buff"`
+}
+
+type accessoryBonus struct {
+ Smile float64 `xorm:"smile_max"`
+ Pure float64 `xorm:"pure_max"`
+ Cool float64 `xorm:"cool_max"`
+}
+
+type memberTagMatchKey struct {
+ UnitTypeID int
+ MemberTagID int
+}
+
+type accessoryBonusRow struct {
+ AccessoryOwningUserID int `xorm:"accessory_owning_user_id"`
+ Smile float64 `xorm:"smile_max"`
+ Pure float64 `xorm:"pure_max"`
+ Cool float64 `xorm:"cool_max"`
+}
+
+type removableSkillMeta struct {
+ UnitRemovableSkillID int `xorm:"unit_removable_skill_id"`
+ EffectRange int `xorm:"effect_range"`
+ EffectType int `xorm:"effect_type"`
+ EffectValue float64 `xorm:"effect_value"`
+ FixedValueFlag int `xorm:"fixed_value_flag"`
+ TargetReferenceType int `xorm:"target_reference_type"`
+}
+
+func loadLiveSetting(ss *session.Session, difficultyID int) (liveSettingData, error) {
+ setting := liveSettingData{}
+ sql := `
+SELECT notes_setting_asset,
+ a_rank_score,
+ b_rank_score,
+ c_rank_score,
+ s_rank_score,
+ ac_flag,
+ swing_flag
+FROM live_setting_m
+WHERE live_setting_id IN (
+ SELECT live_setting_id
+ FROM normal_live_m
+ WHERE live_difficulty_id = ?
+ UNION ALL
+ SELECT live_setting_id
+ FROM special_live_m
+ WHERE live_difficulty_id = ?
+)
+ `
+ has, err := ss.MainEng.SQL(sql, difficultyID, difficultyID).Get(&setting)
+ if err != nil {
+ return liveSettingData{}, err
+ }
+ if !has {
+ return liveSettingData{}, fmt.Errorf("live setting not found: %d", difficultyID)
+ }
+ return setting, nil
+}
+
+func loadLiveNotes(notesSettingAsset string) ([]liveschema.NotesList, error) {
+ notes := []liveschema.NotesList{}
+ noteData := utils.ReadAllText("./assets/serverdata/beatmaps/" + notesSettingAsset)
+ err := json.Unmarshal([]byte(noteData), ¬es)
+ return notes, err
+}
+
+func buildRankInfo(setting liveSettingData) []liveschema.RankInfo {
+ return []liveschema.RankInfo{
+ {Rank: 5, RankMin: 0, RankMax: setting.CRankScore},
+ {Rank: 4, RankMin: setting.CRankScore + 1, RankMax: setting.BRankScore},
+ {Rank: 3, RankMin: setting.BRankScore + 1, RankMax: setting.ARankScore},
+ {Rank: 2, RankMin: setting.ARankScore + 1, RankMax: setting.SRankScore},
+ {Rank: 1, RankMin: setting.SRankScore + 1, RankMax: 0},
+ }
+}
+
+func getMuseumBuff(ss *session.Session) (museumBuff, error) {
+ buff := museumBuff{}
+ _, err := ss.MainEng.Table("museum_contents_m").
+ Select("SUM(smile_buff) AS smile_buff,SUM(pure_buff) AS pure_buff,SUM(cool_buff) AS cool_buff").
+ Get(&buff)
+ return buff, err
+}
+
+func getDeckCenterUnitID(ss *session.Session, deckID int) (int, error) {
+ var centerUnitID int
+ _, err := ss.UserEng.Table("user_deck_unit").
+ Join("LEFT", "user_deck", "user_deck_unit.user_deck_id = user_deck.id").
+ Where("user_deck.deck_id = ? AND user_deck.user_id = ? AND user_deck_unit.position = 5", deckID, ss.UserID).
+ Cols("user_deck_unit.unit_id").
+ Get(¢erUnitID)
+ return centerUnitID, err
+}
+
+func getLeaderSkillData(ss *session.Session, unitID int) (leaderSkillData, error) {
+ data := leaderSkillData{}
+ _, err := ss.MainEng.Table("unit_m").
+ Join("LEFT", "unit_leader_skill_m", "unit_m.default_leader_skill_id = unit_leader_skill_m.unit_leader_skill_id").
+ Join("LEFT", "unit_leader_skill_extra_m", "unit_m.default_leader_skill_id = unit_leader_skill_extra_m.unit_leader_skill_id").
+ Where("unit_m.unit_id = ?", unitID).
+ Cols(`
+ unit_m.attribute_id,
+ unit_leader_skill_m.effect_value AS main_effect_value,
+ unit_leader_skill_extra_m.effect_value AS extra_effect_value,
+ unit_leader_skill_extra_m.member_tag_id
+ `).
+ Get(&data)
+ return data, err
+}
+
+func applyAttributeBonus(attributeID int, effectValue, smile, pure, cool float64) (float64, float64, float64) {
+ switch attributeID {
+ case 1:
+ return math.Ceil(smile * (effectValue / 100)), 0, 0
+ case 2:
+ return 0, math.Ceil(pure * (effectValue / 100)), 0
+ case 3:
+ return 0, 0, math.Ceil(cool * (effectValue / 100))
+ default:
+ return 0, 0, 0
+ }
+}
+
+func matchMemberTag(ss *session.Session, cache map[memberTagMatchKey]bool, unitTypeID, memberTagID int) (bool, error) {
+ if memberTagID <= 0 {
+ return false, nil
+ }
+
+ key := memberTagMatchKey{UnitTypeID: unitTypeID, MemberTagID: memberTagID}
+ if matched, ok := cache[key]; ok {
+ return matched, nil
+ }
+
+ matched, err := ss.MainEng.Table("unit_type_member_tag_m").
+ Where("unit_type_id = ? AND member_tag_id = ?", unitTypeID, memberTagID).
+ Exist()
+ if err != nil {
+ return false, err
+ }
+ cache[key] = matched
+ return matched, nil
+}
+
+func loadDeckUnitDataMap(ss *session.Session, unitOwningUserIDs []int) (map[int]unitmodel.UnitDataMap, error) {
+ unitRows := []unitmodel.UnitDataMap{}
+ err := ss.GetBasicUnitInfo().
+ In("a.unit_owning_user_id", unitOwningUserIDs).
+ Find(&unitRows)
+ if err != nil {
+ return nil, err
+ }
+
+ unitMap := make(map[int]unitmodel.UnitDataMap, len(unitRows))
+ for _, row := range unitRows {
+ unitMap[row.UnitOwningUserID] = row
+ }
+ return unitMap, nil
+}
+
+func loadAccessoryBonusMap(ss *session.Session, userID int, unitOwningUserIDs []int) (map[int]accessoryBonus, error) {
+ wears := []usermodel.UserAccessoryWear{}
+ err := ss.UserEng.Table(new(usermodel.UserAccessoryWear)).
+ Where("user_id = ?", userID).
+ In("unit_owning_user_id", unitOwningUserIDs).
+ Find(&wears)
+ if err != nil {
+ return nil, err
+ }
+ if len(wears) == 0 {
+ return map[int]accessoryBonus{}, nil
+ }
+
+ accessoryIDs := make([]int, 0, len(wears))
+ unitToAccessoryID := make(map[int]int, len(wears))
+ for _, wear := range wears {
+ if wear.AccessoryOwningUserID <= 0 {
+ continue
+ }
+ accessoryIDs = append(accessoryIDs, wear.AccessoryOwningUserID)
+ unitToAccessoryID[wear.UnitOwningUserID] = wear.AccessoryOwningUserID
+ }
+ if len(accessoryIDs) == 0 {
+ return map[int]accessoryBonus{}, nil
+ }
+
+ rows := []accessoryBonusRow{}
+ err = ss.MainEng.Table("common_accessory_m").
+ Join("LEFT", "accessory_m", "common_accessory_m.accessory_id = accessory_m.accessory_id").
+ In("common_accessory_m.accessory_owning_user_id", accessoryIDs).
+ Cols("common_accessory_m.accessory_owning_user_id,smile_max,pure_max,cool_max").
+ Find(&rows)
+ if err != nil {
+ return nil, err
+ }
+
+ accessoryMap := make(map[int]accessoryBonus, len(rows))
+ for _, row := range rows {
+ accessoryMap[row.AccessoryOwningUserID] = accessoryBonus{
+ Smile: row.Smile,
+ Pure: row.Pure,
+ Cool: row.Cool,
+ }
+ }
+
+ result := make(map[int]accessoryBonus, len(unitToAccessoryID))
+ for unitOwningUserID, accessoryOwningUserID := range unitToAccessoryID {
+ if bonus, ok := accessoryMap[accessoryOwningUserID]; ok {
+ result[unitOwningUserID] = bonus
+ }
+ }
+ return result, nil
+}
+
+func loadDeckRemovableSkillMap(ss *session.Session, userID int, unitOwningUserIDs []int) (map[int][]int, error) {
+ rows := []usermodel.UserUnitSkillEquip{}
+ err := ss.UserEng.Table(new(usermodel.UserUnitSkillEquip)).
+ Where("user_id = ?", userID).
+ In("unit_owning_user_id", unitOwningUserIDs).
+ Find(&rows)
+ if err != nil {
+ return nil, err
+ }
+
+ skillMap := make(map[int][]int, len(unitOwningUserIDs))
+ for _, row := range rows {
+ skillMap[row.UnitOwningUserID] = append(skillMap[row.UnitOwningUserID], row.UnitRemovableSkillID)
+ }
+ return skillMap, nil
+}
+
+func loadRemovableSkillMetaMap(ss *session.Session, skillIDs []int) (map[int]removableSkillMeta, error) {
+ if len(skillIDs) == 0 {
+ return map[int]removableSkillMeta{}, nil
+ }
+
+ rows := []removableSkillMeta{}
+ err := ss.MainEng.Table("unit_removable_skill_m").
+ In("unit_removable_skill_id", skillIDs).
+ Cols("unit_removable_skill_id,effect_range,effect_type,effect_value,fixed_value_flag,target_reference_type").
+ Find(&rows)
+ if err != nil {
+ return nil, err
+ }
+
+ metaMap := make(map[int]removableSkillMeta, len(rows))
+ for _, row := range rows {
+ metaMap[row.UnitRemovableSkillID] = row
+ }
+ return metaMap, nil
+}
diff --git a/internal/handler/live/play.go b/internal/handler/live/play.go
index de9cc8d..ff93a3d 100644
--- a/internal/handler/live/play.go
+++ b/internal/handler/live/play.go
@@ -1,14 +1,12 @@
package live
import (
- "encoding/json"
+ "errors"
"honoka-chan/internal/middleware"
- unitmodel "honoka-chan/internal/model/unit"
"honoka-chan/internal/router"
liveschema "honoka-chan/internal/schema/live"
"honoka-chan/internal/session"
honokautils "honoka-chan/internal/utils"
- "honoka-chan/pkg/utils"
"math"
"strconv"
"time"
@@ -29,73 +27,22 @@ func play(ctx *gin.Context) {
ss.RegisterLiveInProgress(playReq.UnitDeckID)
- difficultyID, _ := strconv.Atoi(playReq.LiveDifficultyID)
-
- // 歌曲类型: normal / special
- // sqlite3 不支持 FULL OUTER JOIN 所以这里使用 UNION ALL
- var liveSetting struct {
- NotesSettingAsset string `xorm:"notes_setting_asset"`
- ARankScore int `xorm:"a_rank_score"`
- BRankScore int `xorm:"b_rank_score"`
- CRankScore int `xorm:"c_rank_score"`
- SRankScore int `xorm:"s_rank_score"`
- AcFlag int `xorm:"ac_flag"`
- SwingFlag int `xorm:"swing_flag"`
- }
- sql := `
- SELECT notes_setting_asset,
- a_rank_score,
- b_rank_score,
- c_rank_score,
- s_rank_score,
- ac_flag,
- swing_flag
- FROM live_setting_m
- WHERE live_setting_id IN (
- SELECT live_setting_id
- FROM normal_live_m
- WHERE live_difficulty_id = ?
- UNION ALL
- SELECT live_setting_id
- FROM special_live_m
- WHERE live_difficulty_id = ?
- )
- `
- _, err = ss.MainEng.SQL(sql, difficultyID, difficultyID).Get(&liveSetting)
- if ss.CheckErr(err) {
- return
- }
- // fmt.Println("liveSetting", liveSetting)
-
- notes := []liveschema.NotesList{}
- notes_list := utils.ReadAllText("./assets/serverdata/beatmaps/" + liveSetting.NotesSettingAsset)
- err = json.Unmarshal([]byte(notes_list), ¬es)
+ difficultyID, err := strconv.Atoi(playReq.LiveDifficultyID)
if ss.CheckErr(err) {
return
}
- ranks := []liveschema.RankInfo{}
- ranks = append(ranks, liveschema.RankInfo{
- Rank: 5,
- RankMin: 0,
- RankMax: liveSetting.CRankScore,
- }, liveschema.RankInfo{
- Rank: 4,
- RankMin: liveSetting.CRankScore + 1,
- RankMax: liveSetting.BRankScore,
- }, liveschema.RankInfo{
- Rank: 3,
- RankMin: liveSetting.BRankScore + 1,
- RankMax: liveSetting.ARankScore,
- }, liveschema.RankInfo{
- Rank: 2,
- RankMin: liveSetting.ARankScore + 1,
- RankMax: liveSetting.SRankScore,
- }, liveschema.RankInfo{
- Rank: 1,
- RankMin: liveSetting.SRankScore + 1,
- RankMax: 0,
- })
+ liveSetting, err := loadLiveSetting(ss, difficultyID)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ notes, err := loadLiveNotes(liveSetting.NotesSettingAsset)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ ranks := buildRankInfo(liveSetting)
owningIdList := []int{}
err = ss.UserEng.Table("user_deck_unit").
@@ -105,16 +52,65 @@ func play(ctx *gin.Context) {
if ss.CheckErr(err) {
return
}
+ if len(owningIdList) == 0 {
+ ss.CheckErr(errors.New("deck has no units"))
+ return
+ }
- unitList := []liveschema.UnitList{}
+ museumBuff, err := getMuseumBuff(ss)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ myCenterUnitID, err := getDeckCenterUnitID(ss, playReq.UnitDeckID)
+ if ss.CheckErr(err) {
+ return
+ }
+ myLeaderSkill, err := getLeaderSkillData(ss, myCenterUnitID)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ tomoUnitID, err := getSupportCenterUnitID(ss, int(playReq.PartyUserID), myCenterUnitID)
+ if ss.CheckErr(err) {
+ return
+ }
+ tomoLeaderSkill, err := getLeaderSkillData(ss, tomoUnitID)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ memberTagCache := map[memberTagMatchKey]bool{}
+ unitDataMap, err := loadDeckUnitDataMap(ss, owningIdList)
+ if ss.CheckErr(err) {
+ return
+ }
+ accessoryBonusMap, err := loadAccessoryBonusMap(ss, ss.UserID, owningIdList)
+ if ss.CheckErr(err) {
+ return
+ }
+ removableSkillMap, err := loadDeckRemovableSkillMap(ss, ss.UserID, owningIdList)
+ if ss.CheckErr(err) {
+ return
+ }
+ allRemovableSkillIDs := make([]int, 0)
+ for _, skillIDs := range removableSkillMap {
+ allRemovableSkillIDs = append(allRemovableSkillIDs, skillIDs...)
+ }
+ removableSkillMetaMap, err := loadRemovableSkillMetaMap(ss, allRemovableSkillIDs)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ unitList := make([]liveschema.UnitList, 0, len(owningIdList))
var totalSmile, totalPure, totalCool float64
var totalHp int
for _, owningId := range owningIdList {
// 卡片基础属性
var baseSmile, basePure, baseCool, smileMax, pureMax, coolMax float64
- var unitData unitmodel.UnitDataMap
- _, err = ss.GetBasicUnitInfo().Where("unit_owning_user_id = ?", owningId).Get(&unitData)
- if ss.CheckErr(err) {
+ unitData, ok := unitDataMap[owningId]
+ if !ok {
+ ss.CheckErr(errors.New("unit data not found in deck"))
return
}
baseSmile = float64(unitData.Smile)
@@ -124,42 +120,19 @@ func play(ctx *gin.Context) {
// fmt.Println("基础属性:", baseSmile, basePure, baseCool)
// 饰品属性加成(满级)
- var accessoryOwningId int
- exists, err := ss.UserEng.Table("user_accessory_wear").
- Where("unit_owning_user_id = ?", owningId).
- Cols("accessory_owning_user_id").Get(&accessoryOwningId)
- if ss.CheckErr(err) {
- return
- }
- if exists {
- var smileAccessory, pureAccessory, coolAccessory float64
- _, err = ss.MainEng.Table("common_accessory_m").
- Join("LEFT", "accessory_m", "common_accessory_m.accessory_id = accessory_m.accessory_id").
- Where("accessory_owning_user_id = ?", accessoryOwningId).Cols("smile_max,pure_max,cool_max").
- Get(&smileAccessory, &pureAccessory, &coolAccessory)
- if ss.CheckErr(err) {
- return
- }
-
+ if bonus, ok := accessoryBonusMap[owningId]; ok {
// 饰品属性加成(该加成会影响个宝等百分比宝石属性加成的计算,故先计算。)
- baseSmile += smileAccessory
- basePure += pureAccessory
- baseCool += coolAccessory
+ baseSmile += bonus.Smile
+ basePure += bonus.Pure
+ baseCool += bonus.Cool
// fmt.Println("饰品属性加成:", smileAccessory, pureAccessory, coolAccessory)
// fmt.Println("饰品属性加成后的基础属性:", baseSmile, basePure, baseCool)
}
// 回忆画廊属性加成(该加成会影响个宝等百分比宝石属性加成的计算,故先计算。)
- var smileBuff, pureBuff, coolBuff float64
- _, err = ss.MainEng.Table("museum_contents_m").
- Select("SUM(smile_buff),SUM(pure_buff),SUM(cool_buff)").
- Get(&smileBuff, &pureBuff, &coolBuff)
- if ss.CheckErr(err) {
- return
- }
- baseSmile += smileBuff
- basePure += pureBuff
- baseCool += coolBuff
+ baseSmile += museumBuff.Smile
+ basePure += museumBuff.Pure
+ baseCool += museumBuff.Cool
// fmt.Println("回忆画廊属性加成:", smileBuff, pureBuff, coolBuff)
// fmt.Println("回忆画廊属性加成后的基础属性:", baseSmile, basePure, baseCool)
@@ -180,62 +153,53 @@ func play(ctx *gin.Context) {
var skillSmile, skillPure, skillCool float64
// 宝石加成(满级)
- removableSkillIds := []int{}
- err = ss.UserEng.Table("user_unit_skill_equip").
- Where("unit_owning_user_id = ?", owningId).
- Cols("unit_removable_skill_id").Find(&removableSkillIds)
- if ss.CheckErr(err) {
- return
- }
+ removableSkillIds := removableSkillMap[owningId]
for _, sk := range removableSkillIds {
// 判断宝石效果类型(效果范围、效果类型、效果值、是否固定数值)
- var effectRange, effectType, fixedValueFlag, refType int
- var effectValue float64
- _, err = ss.MainEng.Table("unit_removable_skill_m").
- Where("unit_removable_skill_id = ?", sk).
- Cols("effect_range,effect_type,effect_value,fixed_value_flag,target_reference_type").
- Get(&effectRange, &effectType, &effectValue, &fixedValueFlag, &refType)
- if ss.CheckErr(err) {
+ meta, ok := removableSkillMetaMap[sk]
+ if !ok {
+ ss.CheckErr(errors.New("removable skill meta not found"))
return
}
- if fixedValueFlag == 1 {
+ if meta.FixedValueFlag == 1 {
// 吻、眼神属性加成(固定数值)
- switch effectType {
+ switch meta.EffectType {
case 1:
- kissSmile += effectValue
+ kissSmile += meta.EffectValue
case 2:
- kissPure += effectValue
+ kissPure += meta.EffectValue
case 3:
- kissCool += effectValue
+ kissCool += meta.EffectValue
}
// fmt.Println("吻、眼神属性加成:", kissSmile, kissPure, kissCool)
} else {
// 仅效果类型为1、2、3的有属性加成
- if effectType == 1 || effectType == 2 || effectType == 3 {
+ if meta.EffectType == 1 || meta.EffectType == 2 || meta.EffectType == 3 {
// 加成范围:2:全员 1:非全员
- if effectRange == 2 {
- switch effectType {
+ if meta.EffectRange == 2 {
+ switch meta.EffectType {
case 1:
- skillSmile += math.Ceil(baseSmile * (effectValue / 100))
+ skillSmile += math.Ceil(baseSmile * (meta.EffectValue / 100))
case 2:
- skillPure += math.Ceil(basePure * (effectValue / 100))
+ skillPure += math.Ceil(basePure * (meta.EffectValue / 100))
case 3:
- skillCool += math.Ceil(baseCool * (effectValue / 100))
+ skillCool += math.Ceil(baseCool * (meta.EffectValue / 100))
}
// fmt.Println("全员类宝石属性加成:", skillSmile, skillPure, skillCool)
} else {
// refType: 1 -> 年级类加成, target_type -> 指定年级(这里不需要使用,因为能装上宝石肯定是符合的)
// refType: 2 -> 个宝
// refType: 3 -> 爆分、奶、判宝石, 0 -> 竞技场宝石
- if refType == 1 || refType == 2 { // 年级类和个宝都是百分比加成
- if effectType == 1 {
- skillSmile += math.Ceil(baseSmile * (effectValue / 100))
- } else if effectType == 2 {
- skillPure += math.Ceil(basePure * (effectValue / 100))
- } else if effectValue == 3 {
- skillCool += math.Ceil(baseCool * (effectValue / 100))
+ if meta.TargetReferenceType == 1 || meta.TargetReferenceType == 2 { // 年级类和个宝都是百分比加成
+ switch meta.EffectType {
+ case 1:
+ skillSmile += math.Ceil(baseSmile * (meta.EffectValue / 100))
+ case 2:
+ skillPure += math.Ceil(basePure * (meta.EffectValue / 100))
+ case 3:
+ skillCool += math.Ceil(baseCool * (meta.EffectValue / 100))
}
// fmt.Println("年级类宝石、个宝属性加成:", skillSmile, skillPure, skillCool)
}
@@ -249,123 +213,50 @@ func play(ctx *gin.Context) {
pureMax = basePure + kissPure + skillPure
coolMax = baseCool + kissCool + skillCool
- // 主唱技能加成
- var myCenterUnitId int
- _, err = ss.UserEng.Table("user_deck_unit").
- Join("LEFT", "user_deck", "user_deck_unit.user_deck_id = user_deck.id").
- Where("user_deck.deck_id = ? AND user_deck.user_id = ? AND user_deck_unit.position = 5", playReq.UnitDeckID, ss.UserID).
- Cols("user_deck_unit.unit_id").Get(&myCenterUnitId)
- if ss.CheckErr(err) {
- return
- }
-
// 主唱技能加成:主C技能(这里不使用新C技能,即以某属性的百分比提升另一属性)
- var myAttrId int
- var myEffectValue float64
- _, err = ss.MainEng.Table("unit_m").
- Join("LEFT", "unit_leader_skill_m", "unit_m.default_leader_skill_id = unit_leader_skill_m.unit_leader_skill_id").
- Where("unit_m.unit_id = ?", myCenterUnitId).
- Cols("unit_m.attribute_id,unit_leader_skill_m.effect_value").Get(&myAttrId, &myEffectValue)
- if ss.CheckErr(err) {
- return
- }
- var myCenterSmile, myCenterPure, myCenterCool float64
- switch myAttrId {
- case 1:
- myCenterSmile = math.Ceil(smileMax * (myEffectValue / 100))
- case 2:
- myCenterPure = math.Ceil(pureMax * (myEffectValue / 100))
- case 3:
- myCenterCool = math.Ceil(coolMax * (myEffectValue / 100))
- }
+ myCenterSmile, myCenterPure, myCenterCool := applyAttributeBonus(
+ myLeaderSkill.AttributeID,
+ myLeaderSkill.MainEffectValue,
+ smileMax, pureMax, coolMax,
+ )
// fmt.Println("主C技能属性加成:", myCenterSmile, myCenterPure, myCenterCool)
// 主唱技能加成:副C技能
- var mySubEffectValue float64
- var myMemberTagId int
- _, err = ss.MainEng.Table("unit_m").
- Join("LEFT", "unit_leader_skill_extra_m", "unit_m.default_leader_skill_id = unit_leader_skill_extra_m.unit_leader_skill_id").
- Where("unit_m.unit_id = ?", myCenterUnitId).
- Cols("unit_leader_skill_extra_m.effect_value,unit_leader_skill_extra_m.member_tag_id").
- Get(&mySubEffectValue, &myMemberTagId)
- if ss.CheckErr(err) {
- return
- }
-
- exists, err = ss.MainEng.Table("unit_type_member_tag_m").
- Where("unit_type_id = ? AND member_tag_id = ?", unitData.UnitTypeID, myMemberTagId).Exist()
+ matched, err := matchMemberTag(ss, memberTagCache, unitData.UnitTypeID, myLeaderSkill.MemberTagID)
if ss.CheckErr(err) {
return
}
var mySubSmile, mySubPure, mySubCool float64
- if exists {
- switch myAttrId {
- case 1:
- mySubSmile = math.Ceil(smileMax * (mySubEffectValue / 100))
- case 2:
- mySubPure = math.Ceil(pureMax * (mySubEffectValue / 100))
- case 3:
- mySubCool = math.Ceil(coolMax * (mySubEffectValue / 100))
- }
+ if matched {
+ mySubSmile, mySubPure, mySubCool = applyAttributeBonus(
+ myLeaderSkill.AttributeID,
+ myLeaderSkill.ExtraEffectValue,
+ smileMax, pureMax, coolMax,
+ )
// fmt.Println("副C技能属性加成:", mySubSmile, mySubPure, mySubCool)
}
// 好友主唱技能加成
- tomoUnitId, err := getSupportCenterUnitID(ss, int(playReq.PartyUserID), myCenterUnitId)
- if ss.CheckErr(err) {
- return
- }
- // fmt.Println("好友UnitID:", tomoUnitId)
-
- // 好友主唱技能加成:主C技能(这里不使用新C技能,即以某属性的百分比提升另一属性)
- var tomoEffectValue float64
- _, err = ss.MainEng.Table("unit_m").
- Join("LEFT", "unit_leader_skill_m", "unit_m.default_leader_skill_id = unit_leader_skill_m.unit_leader_skill_id").
- Where("unit_m.unit_id = ?", tomoUnitId).
- Cols("unit_leader_skill_m.effect_value").
- Get(&tomoEffectValue)
- if ss.CheckErr(err) {
- return
- }
- var tomoCenterSmile, tomoCenterPure, tomoCenterCool float64
- switch myAttrId {
- case 1:
- tomoCenterSmile = math.Ceil(smileMax * (tomoEffectValue / 100))
- case 2:
- tomoCenterPure = math.Ceil(pureMax * (tomoEffectValue / 100))
- case 3:
- tomoCenterCool = math.Ceil(coolMax * (tomoEffectValue / 100))
- }
+ tomoCenterSmile, tomoCenterPure, tomoCenterCool := applyAttributeBonus(
+ myLeaderSkill.AttributeID,
+ tomoLeaderSkill.MainEffectValue,
+ smileMax, pureMax, coolMax,
+ )
// fmt.Println("好友主C技能属性加成:", tomoCenterSmile, tomoCenterPure, tomoCenterCool)
// 好友主唱技能加成:副C技能
- var tomoSubEffectValue float64
- var tomoMemberTagId int
- _, err = ss.MainEng.Table("unit_m").
- Join("LEFT", "unit_leader_skill_extra_m", "unit_m.default_leader_skill_id = unit_leader_skill_extra_m.unit_leader_skill_id").
- Where("unit_m.unit_id = ?", tomoUnitId).
- Cols("unit_leader_skill_extra_m.effect_value,unit_leader_skill_extra_m.member_tag_id").
- Get(&tomoSubEffectValue, &tomoMemberTagId)
- if ss.CheckErr(err) {
- return
- }
-
- exists, err = ss.MainEng.Table("unit_type_member_tag_m").
- Where("unit_type_id = ? AND member_tag_id = ?", unitData.UnitTypeID, tomoMemberTagId).Exist()
+ matched, err = matchMemberTag(ss, memberTagCache, unitData.UnitTypeID, tomoLeaderSkill.MemberTagID)
if ss.CheckErr(err) {
return
}
var tomoSubSmile, tomoSubPure, tomoSubCool float64
- if exists {
- switch myAttrId {
- case 1:
- tomoSubSmile = math.Ceil(smileMax * (tomoSubEffectValue / 100))
- case 2:
- tomoSubPure = math.Ceil(pureMax * (tomoSubEffectValue / 100))
- case 3:
- tomoSubCool = math.Ceil(coolMax * (tomoSubEffectValue / 100))
- }
+ if matched {
+ tomoSubSmile, tomoSubPure, tomoSubCool = applyAttributeBonus(
+ myLeaderSkill.AttributeID,
+ tomoLeaderSkill.ExtraEffectValue,
+ smileMax, pureMax, coolMax,
+ )
// fmt.Println("好友副C技能属性加成:", tomoSubSmile, tomoSubPure, tomoSubCool)
}
diff --git a/internal/handler/live/precisescore.go b/internal/handler/live/precisescore.go
index de40c6c..8254ed6 100644
--- a/internal/handler/live/precisescore.go
+++ b/internal/handler/live/precisescore.go
@@ -10,7 +10,6 @@ import (
liverecordschema "honoka-chan/internal/schema/liverecord"
"honoka-chan/internal/session"
honokautils "honoka-chan/internal/utils"
- "honoka-chan/pkg/utils"
"strconv"
"time"
@@ -30,71 +29,17 @@ func preciseScore(ctx *gin.Context) {
difficultyID, _ := strconv.Atoi(playScoreReq.LiveDifficultyID)
- // 歌曲类型: normal / special
- // sqlite3 不支持 FULL OUTER JOIN 所以这里使用 UNION ALL
- var liveSetting struct {
- NotesSettingAsset string `xorm:"notes_setting_asset"`
- ARankScore int `xorm:"a_rank_score"`
- BRankScore int `xorm:"b_rank_score"`
- CRankScore int `xorm:"c_rank_score"`
- SRankScore int `xorm:"s_rank_score"`
- AcFlag int `xorm:"ac_flag"`
- SwingFlag int `xorm:"swing_flag"`
- }
- sql := `
- SELECT notes_setting_asset,
- a_rank_score,
- b_rank_score,
- c_rank_score,
- s_rank_score,
- ac_flag,
- swing_flag
- FROM live_setting_m
- WHERE live_setting_id IN (
- SELECT live_setting_id
- FROM normal_live_m
- WHERE live_difficulty_id = ?
- UNION ALL
- SELECT live_setting_id
- FROM special_live_m
- WHERE live_difficulty_id = ?
- )
- `
- _, err = ss.MainEng.SQL(sql, difficultyID, difficultyID).Get(&liveSetting)
- if ss.CheckErr(err) {
- return
- }
- // fmt.Println("liveSetting", liveSetting)
-
- notesList := []liveschema.NotesList{}
- noteData := utils.ReadAllText("./assets/serverdata/beatmaps/" + liveSetting.NotesSettingAsset)
- err = json.Unmarshal([]byte(noteData), ¬esList)
+ liveSetting, err := loadLiveSetting(ss, difficultyID)
if ss.CheckErr(err) {
return
}
- ranks := []liveschema.RankInfo{}
- ranks = append(ranks, liveschema.RankInfo{
- Rank: 5,
- RankMin: 0,
- RankMax: liveSetting.CRankScore,
- }, liveschema.RankInfo{
- Rank: 4,
- RankMin: liveSetting.CRankScore + 1,
- RankMax: liveSetting.BRankScore,
- }, liveschema.RankInfo{
- Rank: 3,
- RankMin: liveSetting.BRankScore + 1,
- RankMax: liveSetting.ARankScore,
- }, liveschema.RankInfo{
- Rank: 2,
- RankMin: liveSetting.ARankScore + 1,
- RankMax: liveSetting.SRankScore,
- }, liveschema.RankInfo{
- Rank: 1,
- RankMin: liveSetting.SRankScore + 1,
- RankMax: 0,
- })
+ notesList, err := loadLiveNotes(liveSetting.NotesSettingAsset)
+ if ss.CheckErr(err) {
+ return
+ }
+
+ ranks := buildRankInfo(liveSetting)
// 检查是否正在进行 Live
progress, _ := ss.GetLiveInProgress()