Files
bd2/go/internal/server/world/field_monster_events.go
T

199 lines
5.9 KiB
Go

package world
import (
"bd2server/internal/server/gamedata"
"bd2server/internal/server/player"
"bd2server/internal/server/wire"
"bytes"
"fmt"
)
func (s *Service) grantFieldMonster(pack int, m gamedata.FieldMonsterDesign, identity string) ([]byte, error) {
if s.researchEconomy == nil {
return nil, fmt.Errorf("world: monster economy unavailable")
}
var rewards []gamedata.Reward
if m.Reward.Type != 0 && m.Reward.Count > 0 {
rewards = append(rewards, m.Reward)
} else if m.BattleDeck != 0 {
if s.monsterRewards == nil {
return nil, fmt.Errorf("world: monster reward loader unavailable")
}
rows, e := s.monsterRewards(pack, m.BattleDeck)
if e != nil {
return nil, e
}
for _, r := range rows {
rewards = append(rewards, gamedata.Reward{Type: r.Type, ID: r.ID, Count: r.Count})
}
}
return s.researchEconomy.Apply(identity, nil, rewards)
}
func monsterBundleFields(bundle []byte, itemField, equipField int) ([]byte, error) {
var b []byte
err := wire.Walk(bundle, func(f wire.Field) error {
if f.Type == 2 && f.Number == 1 {
b = wire.AppendBytes(b, itemField, f.Value)
} else if f.Type == 2 && f.Number == 4 {
b = wire.AppendBytes(b, equipField, f.Value)
}
return nil
})
return b, err
}
func (s *Service) handleFieldMonsterEvent(path string, request []byte) (int, []byte, bool, error) {
seqCheck, present, seqErr := wire.Varint(request, 1)
if seqErr != nil || !present || seqCheck == 0 || seqCheck > 0x7fffffff {
return 0, nil, true, ErrInvalidRequest
}
code := 83
if path == "/FieldMonsterDamage" {
code = 85
}
id, e := requestPack(request)
if e != nil {
return code, nil, true, e
}
pack, e := s.CurrentPackID()
if e != nil || !s.packUnlocked(pack) {
return code, nil, true, ErrInvalidRequest
}
m, found, e := s.findFieldMonster(pack, id)
if e != nil || !found || !s.monsterEligible(pack, m) {
return code, nil, true, ErrInvalidRequest
}
if path == "/FieldMonsterEvent" && m.Type != 2 && m.Type != 3 {
return code, nil, true, ErrInvalidRequest
}
if e = s.authorizeMonsterMap(pack, id); e != nil {
return code, nil, true, e
}
v, e := s.loadMonsterState()
if e != nil {
return code, nil, true, e
}
seq, _, _ := wire.Varint(request, 1)
if s.monsterSession == "" {
return code, nil, true, fmt.Errorf("world: missing monster session")
}
key := fmt.Sprintf("%s:%s:%d", path, s.monsterSession, seq)
if r, ok := v.Requests[key]; ok {
if !bytes.Equal(r.Request, request) {
return code, nil, true, ErrInvalidRequest
}
return code, r.Response, true, nil
}
state, e := s.monsterState(&v, pack, m)
if e != nil {
return code, nil, true, e
}
var b []byte
active := !state.Defeated && state.Respawn <= s.monsterTime().UnixMilli()
instance := fmt.Sprintf("fieldmonster:%s:%d", monsterKey(pack, s.questDifficulty(pack), id), state.Generation)
if active {
if m.Type == 2 && path == "/FieldMonsterEvent" && !v.Claims[instance] {
bundle, e := s.grantFieldMonster(pack, m, instance)
if e != nil {
return code, nil, true, e
}
b, e = monsterBundleFields(bundle, 3, 4)
if e != nil {
return code, nil, true, e
}
v.Claims[instance] = true
}
dash, _, _ := wire.Varint(request, 3)
if m.FieldBuff > 0 && (path == "/FieldMonsterDamage" || dash == 0) {
if s.monsterDamage == nil {
return code, nil, true, fmt.Errorf("world: monster damage runtime unavailable")
}
rows, e := s.monsterDamage(pack, m.FieldBuff, key)
if e != nil {
return code, nil, true, e
}
for _, r := range rows {
b = wire.AppendBytes(b, 1, r)
}
}
if path == "/FieldMonsterEvent" {
state.Defeated = true
state.Respawn = s.nextMonsterSpawn(m)
v.Monsters[monsterKey(pack, s.questDifficulty(pack), id)] = state
}
}
if path == "/FieldMonsterEvent" {
b = wire.AppendBytes(b, 2, monsterWire(m, state, true))
}
v.Requests[key] = fieldMonsterReply{Request: append([]byte(nil), request...), Response: b}
if e = s.saveMonsterState(v); e != nil {
return code, nil, true, e
}
return code, b, true, nil
}
// Current clients receive collision rewards in FieldMonsterEventResponse;
// FieldMonsterReward has no sender in this version and no pending reward queue.
func (s *Service) handleFieldMonsterReward(request []byte) (int, []byte, bool, error) {
seq, present, e := wire.Varint(request, 1)
if e != nil || !present || seq == 0 {
return 84, nil, true, ErrInvalidRequest
}
return 84, []byte{}, true, nil
}
func (s *Service) ApplyTalentMonsterSummon(identity string, c player.Character, r gamedata.TalentUseRule, ids []uint64) ([]byte, error) {
if identity == "" || len(ids) == 0 || r.Class != 20 {
return nil, ErrInvalidRequest
}
pack, e := s.CurrentPackID()
if e != nil {
return nil, e
}
v, e := s.loadMonsterState()
if e != nil {
return nil, e
}
seen := map[uint64]bool{}
var b []byte
for _, id := range ids {
if seen[id] {
return nil, ErrInvalidRequest
}
seen[id] = true
m, found, e := s.findFieldMonster(pack, int(id))
if e != nil || !found || m.Type != 2 || !s.monsterEligible(pack, m) {
return nil, ErrInvalidRequest
}
if e = s.authorizeMonsterMap(pack, int(id)); e != nil {
return nil, e
}
state, e := s.monsterState(&v, pack, m)
if e != nil {
return nil, e
}
if state.Defeated || state.Respawn > s.monsterTime().UnixMilli() {
return nil, fmt.Errorf("world: summon target is not spawned")
}
instance := fmt.Sprintf("fieldmonster:%s:%d", monsterKey(pack, s.questDifficulty(pack), m.ID), state.Generation)
if !v.Claims[instance] {
bundle, e := s.grantFieldMonster(pack, m, instance)
if e != nil {
return nil, e
}
extra, e := monsterBundleFields(bundle, 3, 4)
if e != nil {
return nil, e
}
b = append(b, extra...)
v.Claims[instance] = true
}
state.Defeated = true
state.Respawn = s.nextMonsterSpawn(m)
v.Monsters[monsterKey(pack, s.questDifficulty(pack), m.ID)] = state
b = wire.AppendBytes(b, 9, monsterWire(m, state, true))
}
if e = s.saveMonsterState(v); e != nil {
return nil, e
}
return b, nil
}