Files
GaCy-GO/internal/gameserver/technique.go
T

174 lines
5.0 KiB
Go

package gameserver
import (
"fmt"
"gac-hkrpg-go/internal/common"
goproto "gac-hkrpg-go/protocol/proto"
)
func (s *Session) techniqueLineup() *goproto.LineupInfo {
var lineup *goproto.LineupInfo
if run := s.ChallengeRun; run != nil {
lineup = run.activeLineup(0)
} else if p := s.JSON(); p != nil {
lineup = presetLineup(p, p.ActiveTeamPreset)
} else {
return nil
}
rules, err := common.LoadTechniqueRules()
if err != nil {
return lineup
}
if s.techniqueMP == nil {
v := rules.InitialMP
s.techniqueMP = &v
}
if *s.techniqueMP > lineup.MaxMp {
*s.techniqueMP = lineup.MaxMp
}
lineup.Mp = *s.techniqueMP
if int(s.leaderSlot) < len(lineup.AvatarList) {
lineup.LeaderSlot = s.leaderSlot
}
return lineup
}
func (s *Session) techniqueAvatar(entity uint32) uint32 {
lineup := s.techniqueLineup()
if lineup == nil {
return 0
}
if entity == 0 {
entity = lineup.LeaderSlot + 1
}
for _, a := range lineup.AvatarList {
if a.Slot+1 == entity {
return a.Id
}
}
return 0
}
func (s *Session) resetTechniqueRoom() {
s.techniqueMP = nil
s.leaderSlot = 0
s.paidTechniqueCasts = nil
}
func (s *Session) syncTechnique(reason goproto.SyncLineupReason) error {
return s.Send(&goproto.SyncLineupNotify{Lineup: s.techniqueLineup(), ReasonList: []goproto.SyncLineupReason{reason}})
}
func techniqueMaterials(p *common.FreesrData) ([]*goproto.Material, error) {
if err := p.EnsureTechniqueFoods(); err != nil {
return nil, err
}
var out []*goproto.Material
for _, id := range sortedU32Keys2(p.TechniqueFoodStock) {
out = append(out, &goproto.Material{Tid: id, Num: p.TechniqueFoodStock[id]})
}
return out, nil
}
func (s *Session) consumeTechniqueFood(id, count uint32) error {
p := s.JSON()
if p == nil {
return fmt.Errorf("no player")
}
if _, active := CURRENT_BATTLE.Load(s.Token); active {
return fmt.Errorf("cannot eat during battle")
}
rules, err := common.LoadTechniqueRules()
if err != nil {
return err
}
food, ok := rules.Foods[id]
if !ok || count == 0 || count > food.MaxUse {
return fmt.Errorf("unsupported food or count")
}
if err := p.EnsureTechniqueFoods(); err != nil {
return err
}
if p.TechniqueFoodStock[id] < count {
return fmt.Errorf("food %d is exhausted", id)
}
lineup := s.techniqueLineup()
if lineup == nil || lineup.Mp >= lineup.MaxMp {
return fmt.Errorf("technique points are full")
}
oldStock, oldMP := p.TechniqueFoodStock[id], *s.techniqueMP
p.TechniqueFoodStock[id] -= count
gain := uint64(food.RecoverMP)*uint64(count) + uint64(oldMP)
if gain > uint64(lineup.MaxMp) {
gain = uint64(lineup.MaxMp)
}
*s.techniqueMP = uint32(gain)
if err := p.SavePersistentChecked(); err != nil {
p.TechniqueFoodStock[id] = oldStock
*s.techniqueMP = oldMP
return err
}
if err := s.Send(&goproto.PlayerSyncScNotify{MaterialList: []*goproto.Material{{Tid: id, Num: p.TechniqueFoodStock[id]}}}); err != nil {
return err
}
SceneDebugLog("[TECHNIQUE] food=%d count=%d mp=%d/%d remaining=%d", id, count, *s.techniqueMP, lineup.MaxMp, p.TechniqueFoodStock[id])
return s.syncTechnique(goproto.SyncLineupReason_SyncLineupReason_SyncReasonMpAdd)
}
func (s *Session) payTechnique(castID, skillIndex uint32) (uint32, error) {
if s.paidTechniqueCasts[castID] {
return 0, nil
}
rules, err := common.LoadTechniqueRules()
if err != nil {
return 0, err
}
avatar := s.techniqueAvatar(0)
skills := rules.Avatars[avatar]
if int(skillIndex) >= len(skills) {
return uint32(goproto.Retcode_RET_MAZE_NO_ABILITY), nil
}
cost := skills[skillIndex].MPCost
lineup := s.techniqueLineup()
if lineup == nil {
return uint32(goproto.Retcode_RET_LINEUP_IS_EMPTY), nil
}
p := s.JSON()
if err := p.EnsureTechniqueFoods(); err != nil {
return 0, err
}
if cost > lineup.MaxMp {
return uint32(goproto.Retcode_RET_MAZE_MP_NOT_ENOUGH), nil
}
for *s.techniqueMP < cost && p.AutoTechniqueFood != nil && *p.AutoTechniqueFood {
var selected uint32
for _, id := range sortedU32Keys2(rules.Foods) {
if p.TechniqueFoodStock[id] > 0 {
selected = id
break
}
}
if selected == 0 {
break
}
if err := s.consumeTechniqueFood(selected, 1); err != nil {
return 0, err
}
}
if *s.techniqueMP < cost {
return uint32(goproto.Retcode_RET_MAZE_MP_NOT_ENOUGH), nil
}
*s.techniqueMP -= cost
if s.paidTechniqueCasts == nil {
s.paidTechniqueCasts = map[uint32]bool{}
}
s.paidTechniqueCasts[castID] = true
SceneDebugLog("[TECHNIQUE] cast=%d avatar=%d skill=%d cost=%d mp=%d/%d", castID, avatar, skillIndex, cost, *s.techniqueMP, lineup.MaxMp)
return 0, s.Send(&goproto.SceneCastSkillMpUpdateScNotify{CastEntityId: castID, Mp: *s.techniqueMP})
}
func init() {
handle("UseItem", func(s *Session, req *goproto.UseItemCsReq, rsp *goproto.UseItemScRsp) error {
rsp.UseItemId = req.UseItemId
rsp.UseItemCount = req.UseItemCount
if err := s.consumeTechniqueFood(req.UseItemId, req.UseItemCount); err != nil {
SceneDebugLog("[TECHNIQUE] use item rejected: %v", err)
rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_USE_TARGET_TYPE_INVALID)
}
return nil
})
}