110 lines
2.9 KiB
Go
110 lines
2.9 KiB
Go
package world
|
|
|
|
import (
|
|
"bd2server/internal/server/gamedata"
|
|
"bd2server/internal/server/player"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
func (s *Service) AttachFieldBuffRuntime(root, version string) error {
|
|
d, e := gamedata.LoadFieldBuffDesign(root, version)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
s.fieldBuffs = d
|
|
s.AttachFieldMonsterDamage(s.applyMonsterFieldDamage)
|
|
return nil
|
|
}
|
|
func (s *Service) applyMonsterFieldDamage(pack int, id uint64, identity string) ([][]byte, error) {
|
|
r, ok := s.fieldBuffs[id]
|
|
if !ok || (r.Type != 4 && r.Type != 5) || r.TargetType > 2 || math.IsNaN(r.Value) || math.IsInf(r.Value, 0) || r.Value < 0 {
|
|
return nil, fmt.Errorf("world: invalid damaging field buff")
|
|
}
|
|
if s.characters == nil || s.decks == nil {
|
|
return nil, fmt.Errorf("world: field health runtime unavailable")
|
|
}
|
|
indices, e := s.fieldBuffTargets(pack, r.TargetType)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
var response [][]byte
|
|
for _, index := range indices {
|
|
c, found := s.characters.Find(index)
|
|
if !found {
|
|
return nil, fmt.Errorf("world: field character missing")
|
|
}
|
|
hp, e := s.characters.CurrentHealth(c.InvenIndex)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
if hp == 0 && r.TargetType != 0 {
|
|
continue
|
|
}
|
|
damage := r.Value
|
|
if r.Type == 5 {
|
|
max, e := s.characters.MaxHealth(c.InvenIndex)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
damage = float64(max) * r.Value
|
|
}
|
|
remaining := uint64(0)
|
|
if damage < float64(hp) {
|
|
remaining = hp - uint64(damage)
|
|
}
|
|
if e = s.characters.SetCurrentHealth(c.InvenIndex, remaining); e != nil {
|
|
return nil, e
|
|
}
|
|
c.HP = remaining
|
|
response = append(response, player.CharacterWire(c))
|
|
if r.TargetType == 0 {
|
|
break
|
|
}
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
func (s *Service) fieldBuffTargets(pack int, target uint64) ([]uint64, error) {
|
|
if target > 2 || s.decks == nil {
|
|
return nil, fmt.Errorf("world: invalid field buff target")
|
|
}
|
|
var indices []uint64
|
|
if s.questDifficulty(pack) == 0 && s.storyRoster != nil {
|
|
if quest := s.firstUnclearedQuestFor(pack); quest != 0 {
|
|
party, err := s.ResolveStoryParty(pack, quest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, c := range party {
|
|
indices = append(indices, c.InvenIndex)
|
|
}
|
|
}
|
|
}
|
|
if len(indices) == 0 {
|
|
if s.decks.FieldControlType() == 0 || len(s.decks.CurrentFieldDeck()) == 0 {
|
|
for _, entry := range s.decks.CurrentDeck() {
|
|
indices = append(indices, entry.CharacterInvenIndex)
|
|
}
|
|
} else {
|
|
for _, entry := range s.decks.CurrentFieldDeck() {
|
|
indices = append(indices, entry.CharacterInvenIndex)
|
|
}
|
|
}
|
|
}
|
|
// The client animates either party target through the active field train;
|
|
// LEADER applies only to its first member, never all owned characters.
|
|
if target == 0 && len(indices) > 1 {
|
|
indices = indices[:1]
|
|
}
|
|
seen := map[uint64]bool{}
|
|
var unique []uint64
|
|
for _, id := range indices {
|
|
if !seen[id] {
|
|
unique = append(unique, id)
|
|
seen[id] = true
|
|
}
|
|
}
|
|
return unique, nil
|
|
}
|