package customcmds
import (
"errors"
// pattack は先程生成した puffer のパッケージです。
pattack "handson/puffer/go/custom"
"github.com/Diarkis/diarkis/derror"
"github.com/Diarkis/diarkis/room"
"github.com/Diarkis/diarkis/server"
"github.com/Diarkis/diarkis/user"
"github.com/Diarkis/diarkis/util"
)
// attack は Room 内の敵に対して攻撃します。
//
// Diarkis の全てのコマンドは決まった引数を取ります。
// - ver: コマンドのバージョン
// - cmd: コマンド ID
// - payload: コマンドにわたすデータ
// - userData: ユーザーデータ
func attack(ver uint8, cmd uint16, payload []byte, userData *user.User, next func(error)) {
// attack コマンドは Room の敵に攻撃するコマンドなので、
// Room に参加していない場合はエラーとなります。
// Room に参加しているかどうかは roomID を取得して確認します。
roomID := room.GetRoomID(userData)
// room に参加していない場合は、 roomID は空で返却されるので、エラーハンドリングを行います
if roomID == "" {
// ユーザーに userData.ServerRespond() でエラーのレスポンスを返します。
err := errors.New("not in the room")
userData.ServerRespond(derror.ErrData(err.Error(), derror.NotAllowed(0)), ver, cmd, server.Bad, true)
// レスポンスを返したら next(err) して return します。
next(err)
return
}
// pattack.NewAttack() したあとに、req.Unpack(payload) することで
// ペイロードをデシリアライズできます。
req := pattack.NewAttack()
req.Unpack(payload)
// Type を元にダメージを計算します。
damage := 0
switch req.Type {
case 1: // 近接攻撃 (D20)
damage = util.RandomInt(1, 20)
case 2: // 遠距離攻撃 (D12 + 3)
damage = util.RandomInt(1, 12) + 3
default:
err := errors.New("invalid attack type")
userData.ServerRespond(derror.ErrData(err.Error(), derror.InvalidParameter(0)), ver, cmd, server.Bad, true)
next(err)
return
}
// 算出したダメージを敵の合計ダメージに加算します。
// Room に Property として情報を保存することができます。
// ここでは "DAMAGE" というキーに対してダメージを加算しています。
// room.IncrProperty を使うと加算後の数値を取得することができます。
// その他にも Property を扱うための関数が用意されています。詳細は以下をご覧ください。
// https://docs.diarkis.io/docs/server/v1.0.0/diarkis/room/index.html
updatedDamage, updated := room.IncrProperty(roomID, "DAMAGE", int64(damage))
if !updated {
err := errors.New("incr property failed")
userData.ServerRespond(derror.ErrData(err.Error(), derror.Internal(0)), ver, cmd, server.Err, true)
next(err)
return
}
logger.Info("Room %s has been attacked by %s using %s attack by %d damage. Total damage: %d", roomID, userData.ID, req.Type, damage, updatedDamage)
// 誰がダメージを与えたか、ダメージ値と合計ダメージ値を返却し、ルームメンバーに通知します。
// res := pattack.NewAttackResult() したあとに返却するデータをセットします。
res := pattack.NewAttackResult()
res.Type = req.Type
res.Uid = userData.ID
res.Damage = uint16(damage)
res.TotalDamage = uint16(updatedDamage)
// コマンドを実行したユーザーには userData.ServerRespond()
// 他のルームメンバーには room.Relay() を使って結果を通知します。
userData.ServerRespond(res.Pack(), ver, cmd, server.Ok, true)
room.Relay(roomID, userData, ver, cmd, res.Pack(), true)
// レスポンスを返したら next(nil) して return します。
next(nil)
}