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 }