爬塔流程设计

爬塔流程设计

爬塔事件类型定义

// 爬塔事件类型
enum emRoguelikeEventType
{
    RoguelikeEvent_Unknow = 0,     // 未知事件
    RoguelikeEvent_Fight = 1,      // 战斗事件
    RoguelikeEvent_Normal = 2,     // 通常事件
    RoguelikeEvent_Select = 3,     // 自选事件
    RoguelikeEvent_Shop = 4,       // 商城事件

    //[2.0]新增
    RoguelikeEvent_QA = 5,         // 答题事件
    RoguelikeEvent_Judge = 6,      // 判定事件
};

爬塔装备类型

enum emEquipType
{
    type_spell = 0,  // 技能
    type_buff,       // buff
    type_card,       // 手牌
    type_equip,      // 装备牌
};

效果来源

// 来源
enum EffectFrom
{
    from_unknow = 0,            // 未知
    from_start = 1,                // 开始新征程
    from_chr_star = 2,            // 武将星级
    from_difficulty = 3,        // 困难度
    from_event_fight = 4,        // 战斗事件
    from_event_normal = 5,        // 普通事件
    from_event_select = 6,        // 选择事件
    from_event_shop = 7,        // 商店事件
    from_level_up = 8,            // 升级
    from_round_pass = 9,        // 通关
    from_round_over = 10,        // 一轮结束(通关失败)
    from_daily_update = 11,        // 每日更新
    from_goods_buy = 12,        // 道具购买
    from_gm = 13,                // gm命令
    from_chr_self = 14,         // 武将本身
    from_skin = 15,             // 武将皮肤
    from_event_qa = 16,            // 答题事件
    from_servant = 17,            // 侍灵
};

爬塔困难度

  • 初始解锁的困难度根据配置而定;
  • 每通关一次, 解锁当前困难度以下未解锁的所有困难度,向上解锁一级;

额外技能槽

  • 选将成功后,必有额外技能槽(配置控制);

事件说明

  • 普通事件和选择事件的选项可以为去战斗;
  • 普通事件和选择事件的选项、等级升级的奖励内容有随机项时,通知客户端选择;
  • 战斗胜利奖励、武将星级加成、困难度加成、战斗事件困难度强化加成的内容中如果有随机项,由服务端确认实际内容;
  • 答题事件由服务端确定题库id、选项排序、错误排除并发送给客户端;
  • 判定事件由服务端确认跳转事件,客户端不需要关心是否是判定事件;

对局击杀奖惩

  • 根据困难度配置击杀奖惩;
  • 目前击杀奖惩只有发牌或弃牌(随机弃手牌),加减体力;
  • 击杀奖惩的动作不会触发技能和buff;

对局结束判断

  • 爬塔挑战者死亡,挑战失败,对局结束;
  • 除爬塔挑战者外其他玩家全部死亡,挑战成功,对局结束;
  • 爬塔挑战者的敌方玩家死亡个数大于等于配置killwin的敌方玩家数目,挑战成功,对局结束;

对局结束处理

  • 只处理在线的非AI玩家数据;

爬塔模式入口数据

  • [2.0]没有变化

开始新征程

  • [2.0] RoguelikeChrData新增maxSkinGrade;
  • [2.0] 删除了困难度openCount/difficultyOpen和武将列表chrCount/chrs字段,新增了爬塔状态字段state;
  • [2.0] result增加 ver_low=5;
ABROAD_ROGUELIKE_START_REQ=48502
struct ClientRoguelikeStartReq : public PacketBase
{
    ClientRoguelikeStartReq() :PacketBase(ABROAD_ROGUELIKE_START_REQ, sizeof(ClientRoguelikeStartReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeStartReq));
    }
};

ABROAD_ROGUELIKE_START_REP=48503
struct ClientRoguelikeStartRep : public PacketBase
{
    const static unsigned int sc_max_difficulty_num = 10;
    const static unsigned int sc_max_chr_num = 30;

    enum emResult
    {
        ok = 0,
        failed = 1,
        no_power = 2, // 没有体力
        no_chr = 3, // 分將失败
        data_error = 4, // 数据异常,收到该错误请返回模式准备界面
        ver_low = 5, // 客户端版本太低[2.0]
    };

    unsigned int result;
    unsigned int power;
    unsigned int state;

    ClientRoguelikeStartRep() :PacketBase(ABROAD_ROGUELIKE_START_REP, sizeof(ClientRoguelikeStartRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeStartRep));
    }
};

继续征程

  • [2.0] RoguelikeChrData新增maxSkinGrade;
  • [2.0] 删除了选将判断needSelectChr、困难度openCount/difficultyOpen和武将列表chrCount/chrs字段,新增了爬塔状态字段state;
  • [2.0] result增加 ver_low=5;
ABROAD_ROGUELIKE_CONTINUE_REQ=48504
struct ClientRoguelikeContinueReq : public PacketBase
{
    ClientRoguelikeContinueReq() :PacketBase(ABROAD_ROGUELIKE_CONTINUE_REP, sizeof(ClientRoguelikeContinueReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeContinueReq));
    }
};

ABROAD_ROGUELIKE_CONTINUE_REP=48505
enum emResult
    {
        ok = 0,
        failed = 1,
        not_start = 2, // 未开始
        no_chr = 3, // 分將失败
        data_error = 4, // 数据异常,收到该错误请返回模式准备界面
        ver_low = 5, // 客户端版本太低[2.0]
    };

    unsigned int result;
    bool roundWaitComplete;     // 已有征程是否等待结算,为true是发送ABROAD_ROGUELIKE_CHECK_ROUND_OVER
    unsigned int state;

    ClientRoguelikeContinueRep() :PacketBase(ABROAD_ROGUELIKE_CONTINUE_REP, sizeof(ClientRoguelikeContinueRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeContinueRep));
    }
};

可选武将列表通知

  • [2.0] 新增
  • 原可选困难度和武将列表新包包含在开始新征程和继续征程的回包里,现在单独拆出来通知客户端
struct RoguelikeChrData
{
    enum emHave
    {
        unusable = 0,  // 不可用
        had = 1, // 拥有(客户端检查正常还是使用)
        free = 2, // 免费
    };

    unsigned int chrId;   // 武将id
    unsigned int have;  // 是否拥有武将 emHave
    unsigned int chrStar; // 星级
    unsigned int maxSkinGrade; // 武将的最高皮肤品级
    unsigned int coin; // 初始金币
    unsigned int cardInit; // 初始手牌数
    int cardLimit; // 手牌上限增量值
    unsigned int hpInit; // 初始血量
    unsigned int hpLimit; // 血量上限
    unsigned int sqkUse; // 手气卡每局可使用次数
    unsigned int spellExCnt; // 技能槽数据
    unsigned int param[2];  // 备用
};

ABROAD_ROGUELIKE_SELECT_CHR_NTF=48549
struct ClientRoguelikeSelectChrNtf : public PacketBase
{
    const static unsigned int sc_max_difficulty_num = 10;
    const static unsigned int sc_max_chr_num = 30;

    unsigned int openCount;
    unsigned int difficultyOpen[sc_max_difficulty_num];   // 开放的困难等级
    unsigned int chrCount;
    RoguelikeChrData chrs[sc_max_chr_num];  // 可选武将列表

    ClientRoguelikeSelectChrNtf() :PacketBase(ABROAD_ROGUELIKE_SELECT_CHR_NTF, sizeof(ClientRoguelikeSelectChrNtf))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSelectChrNtf));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeSelectChrNtf& t)
    {
        bos << t.openCount;
        for (unsigned int i = 0; i < t.openCount; i++)
        {
            bos << t.difficultyOpen[i];
            if (i >= ClientRoguelikeSelectChrNtf::sc_max_difficulty_num) break;
        }
        bos << t.chrCount;
        for (unsigned int i = 0; i < t.chrCount; i++)
        {
            bos << t.chrs[i].chrId << t.chrs[i].have << t.chrs[i].chrStar << t.chrs[i].maxSkinGrade << t.chrs[i].coin << t.chrs[i].cardInit
                << t.chrs[i].cardLimit << t.chrs[i].hpInit << t.chrs[i].hpLimit << t.chrs[i].sqkUse << t.chrs[i].spellExCnt
                << t.chrs[i].param[0] << t.chrs[i].param[1];
            if (i >= ClientRoguelikeSelectChrNtf::sc_max_chr_num) break;
        }
        return bos;
    }
};

选择武将和困难度

  • [2.0] 回包增加了爬塔状态state
  • [2.0] 选择成功后,服务端会下发可选侍灵列表通知
ABROAD_ROGUELIKE_CHR_SELECT_REQ=48506
struct ClientRoguelikeSelectChrReq : public PacketBase
{
    unsigned int chrId;   // 选择的武将
    unsigned int difficulty;  // 选择的困难度
    ClientRoguelikeSelectChrReq() :PacketBase(ABROAD_ROGUELIKE_CHR_SELECT_REQ, sizeof(ClientRoguelikeSelectChrReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSelectChrReq));
    }
};

ABROAD_ROGUELIKE_CHR_SELECT_REP=48507
struct ClientRoguelikeSelectChrRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        not_assign = 2,   // 选择的将不是分配的
        difficulty_invalid = 3, // 困难等级非法
        select_error = 4, // 选择异常
        data_error = 5, // 数据异常,收到该错误请返回模式准备界面
    };

    unsigned int result;
    unsigned int difficulty;
    unsigned int chrId;
    unsigned int chrStar;  // 选择武将时,该武将的星级
    unsigned int state;
    ClientRoguelikeSelectChrRep() :PacketBase(ABROAD_ROGUELIKE_CHR_SELECT_REP, sizeof(ClientRoguelikeSelectChrRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSelectChrRep));
    }
};

可选侍灵列表

  • [2.0] 新增
  • 在选择武将成功后(开始新征程或继续征程)服务端下发
  • 在爬塔界面,客户端可以请求侍灵切换
struct ServantItemData
{
    unsigned int servantId;
    unsigned int level; // 等级
    unsigned int flag;  // 拥有标记,0:永久,大于0表示试用结束日期20210706
    unsigned int param[2];  // 备用
};

ABROAD_ROGUELIKE_SERVENT_LIST_REQ=48550
struct ClientRoguelikeServantListReq : public PacketBase
{
    enum emType
    {
        servant_switch = 0,  // 切换
        servant_select = 1,  // 选择
    };

    unsigned int opType;
    ClientRoguelikeServantListReq() :PacketBase(ABROAD_ROGUELIKE_SERVENT_LIST_REQ, sizeof(ClientRoguelikeServantListReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeServantListReq));
    }
};

ABROAD_ROGUELIKE_SERVENT_LIST_REP=48551
struct ClientRoguelikeServantListRep : public PacketBase
{
    const static unsigned int sc_max_servant_num = 30;

    unsigned int opType;
    unsigned int chrId;
    unsigned int fightServant;
    unsigned int count;
    ServantItemData servants[sc_max_servant_num];

    ClientRoguelikeServantListRep() :PacketBase(ABROAD_ROGUELIKE_SERVENT_LIST_REP, sizeof(ClientRoguelikeServantListRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeServantListRep));
    }
};

选择侍灵

  • [2.0] 新增
  • 根据服务端下发的侍灵列表进行选择,如果无侍灵可选择,可选择跳过
ABROAD_ROGUELIKE_SERVENT_SELECT_REQ=48552
struct ClientRoguelikeSelectServantReq : public PacketBase
{
    enum emType
    {
        type_select = 0,
        type_skip = 1,
    };

    unsigned int opType;
    unsigned int servantId;

    ClientRoguelikeSelectServantReq() :PacketBase(ABROAD_ROGUELIKE_SERVENT_SELECT_REQ, sizeof(ClientRoguelikeSelectServantReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSelectServantReq));
    }
};

ABROAD_ROGUELIKE_SERVENT_SELECT_REP=48553
struct ClientRoguelikeSelectServantRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        not_select = 2, // 没有做选择
        not_had = 3, // 未拥有
        data_error = 4,
    };

    unsigned int result;
    unsigned int opType;
    unsigned int servantId;
    unsigned int servantLevel;
    unsigned int state;

    ClientRoguelikeSelectServantRep() :PacketBase(ABROAD_ROGUELIKE_SERVENT_SELECT_REP, sizeof(ClientRoguelikeSelectServantRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSelectServantRep));
    }
};

获取武将数据

  • [2.0]新增武将皮肤相关信息
ABROAD_ROGUELIKE_CHR_DATA_REQ=48508,
struct ClientRoguelikeChrDataReq : public PacketBase
{
    ClientRoguelikeChrDataReq() :PacketBase(ABROAD_ROGUELIKE_CHR_DATA_REQ, sizeof(ClientRoguelikeChrDataReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeChrDataReq));
    }
};

ABROAD_ROGUELIKE_CHR_DATA_REP=48509
struct ClientRoguelikeChrDataRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        not_start = 2,  // 未开始
        not_select = 3, // 没有选将
        data_error = 4, // 数据异常,收到该错误请返回模式准备界面并请求ABROAD_ROGUELIKE_MODE_DATA_REQ
    };

    unsigned int result;
    unsigned int chrId;  // 武将id
    unsigned int skinId; // 武将皮肤
    bool         isDynamic;  // skinId是否是动态皮肤[2.0]
    unsigned int skinMaxGrade;  // 武将皮肤拥有的最高品级[2.0]
    unsigned int chrStar;  // 武将星级
    unsigned int coinNum;  // 金币数
    unsigned int spellNum; // 技能槽数目(额外获得的+武将自带的)
    unsigned int difficulty; // 困难度
    unsigned int playerLevel; // 等级
    unsigned int playerExp; // 当前经验值
    unsigned int playerUpExp; // 升级的经验值 playerExp > playerUpExp表示可升级,客户端可主动请求ABROAD_ROGUELIKE_LEVEL_UP_REQ
    unsigned int initCardNum; // 手牌初始数目
    int limitCardNum; // 手牌上限增量值
    unsigned int initHpNum; // 初始体力
    unsigned int limitHpNum; // 体力上限
    unsigned int useSQKNum; // 可使用手气卡次数
    unsigned int score;  // 积分
    ClientRoguelikeChrDataRep() :PacketBase(ABROAD_ROGUELIKE_CHR_DATA_REP, sizeof(ClientRoguelikeChrDataRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeChrDataRep));
    }
};

获取技能数据

  • [2.0]添加来源
ABROAD_ROGUELIKE_SPELL_DATA_REQ=48510
struct ClientRoguelikeSpellDataReq : public PacketBase
{
    unsigned int spellType;  // emEquipType [2.0]
    ClientRoguelikeSpellDataReq() :PacketBase(ABROAD_ROGUELIKE_SPELL_DATA_REQ, sizeof(ClientRoguelikeSpellDataReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellDataReq));
    }
};

[2.0]
struct SpellItem
{
    unsigned int id; 
    unsigned int from;   // 来源 EffectFrom
    unsigned int eventId;  // 来源是事件时的事件id
};

[2.0]
ABROAD_ROGUELIKE_SPELL_DATA_REP=48511
struct ClientRoguelikeSpellDataRep : public PacketBase
{
    static const unsigned int sc_max_spell_num = 60;
    enum emResult
    {
        ok = 0,
        failed = 1,
        data_error = 2, // 数据异常,收到该错误请返回模式准备界面
    };

    unsigned int result; 
    unsigned int spellType;  // emEquipType
    bool hasSwitch;  // 是否有可替换的操作

    unsigned int count; 
    SpellItem items[sc_max_spell_num];

    ClientRoguelikeSpellDataRep() :PacketBase(ABROAD_ROGUELIKE_SPELL_DATA_REP, sizeof(ClientRoguelikeSpellDataRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellDataRep));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeSpellDataRep& t)
    {
        bos << t.result << t.spellType << t.hasSwitch << t.count;
        for (unsigned int i = 0; i < t.count; i++)
        {
            bos << t.items[i].id << t.items[i].from << t.items[i].eventId;
            if (i >= ClientRoguelikeSpellDataRep::sc_max_spell_num) break;
        }
        return bos;
    }
};

获取技能替换数据

  • [2.0]添加来源
ABROAD_ROGUELIKE_SPELL_GET_SWITCH_REQ=48512
struct ClientRoguelikeSpellGetSwitchReq : public PacketBase
{
    unsigned int spellType;  // emEquipType
    ClientRoguelikeSpellGetSwitchReq() :PacketBase(ABROAD_ROGUELIKE_SPELL_GET_SWITCH_REQ, sizeof(ClientRoguelikeSpellGetSwitchReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellGetSwitchReq));
    }
};

struct ClientRoguelikeSpellGetSwitchRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        no_switch = 2, // 没有调整操作
        data_error = 3, // 数据异常,收到该错误请返回模式准备界面
    };

    unsigned int result;
    unsigned int spellType;  // emEquipType
    unsigned int switchFlag;  // 替换组标记
    unsigned int waitCount; // 可以替换的技能
    SpellItem waits[ClientRoguelikeSpellDataRep::sc_max_spell_num];  // [2.0]
    unsigned int selectCount; // 身上已有的且可被替换的技能
    SpellItem selects[ClientRoguelikeSpellDataRep::sc_max_spell_num / 2];  // [2.0]

    ClientRoguelikeSpellGetSwitchRep() :PacketBase(ABROAD_ROGUELIKE_SPELL_GET_SWITCH_REP, sizeof(ClientRoguelikeSpellGetSwitchRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellGetSwitchRep));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeSpellGetSwitchRep& t)
    {
        bos << t.result << t.spellType << t.switchFlag << t.waitCount;
        for (unsigned int i = 0; i < t.waitCount; i++)
        {
            bos << t.waits[i].id << t.waits[i].from << t.waits[i].eventId;
            if (i >= ClientRoguelikeSpellDataRep::sc_max_spell_num) break;
        }
        bos << t.selectCount;
        for (unsigned int i = 0; i < t.selectCount; i++)
        {
            bos << t.selects[i].id << t.selects[i].from << t.selects[i].eventId;
            if (i >= ClientRoguelikeSpellDataRep::sc_max_spell_num / 2) break;
        }
        return bos;
    }
};

技能替换操作

  • [2.0]result添加结果
ABROAD_ROGUELIKE_SPELL_DO_SWITCH_REQ=48514
struct ClientRoguelikeSpellDoSwitchReq : public PacketBase
{
    unsigned int spellType; //emEquipType
    unsigned int switchFlag; // 替换组标记

    // 选择的技能/装备
    // 如果是装备,需要将武器牌、防具牌、宝物*2依次填入,无论是否有牌
    // 将选择后身上装备的技能/装备填入selects
    unsigned int selectCount; 
    unsigned int selects[ClientRoguelikeSpellDataRep::sc_max_spell_num]; 
    ClientRoguelikeSpellDoSwitchReq() :PacketBase(ABROAD_ROGUELIKE_SPELL_DO_SWITCH_REQ, sizeof(ClientRoguelikeSpellDoSwitchReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellDoSwitchReq));
    }
};

ABROAD_ROGUELIKE_SPELL_DO_SWITCH_REP=48515
struct ClientRoguelikeSpellDoSwitchRep : public PacketBase
{
    struct ClientRoguelikeSpellDoSwitchRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        had_done = 2, // 该替换组已执行过操作
        self_cannot = 3,  // 武将的技能不可以被替换
        data_error = 4, // 数据异常,收到该错误请返回模式准备界面
        equip_fill_error = 5, // 装备替换但没有将武器牌、防具牌、宝物*2一次填入
        select_same = 6, // 有重复数据
        select_count_err = 5, // 选择的数据个数错误
    };

    unsigned int result;
    unsigned int spellType;
    unsigned int switchFlag;

    ClientRoguelikeSpellDoSwitchRep() :PacketBase(ABROAD_ROGUELIKE_SPELL_DO_SWITCH_REP, sizeof(ClientRoguelikeSpellDoSwitchRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeSpellDoSwitchRep));
    }
};

等级更新请求

  • [2.0]没有变化

等级升级奖励选择

  • [2.0]没有变化

章节数据请求

  • [2.0]没有变化

商店数据请求

  • [2.0]添加溢价buff触发提示
ABROAD_ROGUELIKE_SHOP_DATA_REQ = 48527
struct ClientRoguelikeShopDataReq : public PacketBase
{
    unsigned int chapterNo;  // 当前第几个章节
    unsigned int eventNo; // 商城事件序号
    ClientRoguelikeShopDataReq() :PacketBase(ABROAD_ROGUELIKE_SHOP_DATA_REQ, sizeof(ClientRoguelikeShopDataReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeShopDataReq));
    }
};

ABROAD_ROGUELIKE_SHOP_DATA_REP=48528
struct ShopItem
{
    unsigned int id;
    unsigned int price;
}
struct ClientRoguelikeShopDataRep : public PacketBase
{
    static const unsigned int sc_max_shoptab_num = 4;
    static const unsigned int sc_max_shopitem_num = 10;

    enum emResult
    {
        ok = 0,
        failed = 1,
        not_start = 2, // 未开始
        eventno_invalid = 3,
        data_error = 4, // 数据异常,收到该错误请返回模式准备界面并请求ABROAD_ROGUELIKE_MODE_DATA_REQ
    };

    struct ShopTab
    {
        unsigned int tab;   // 分页类型
        unsigned int buyCnt; // 购买次数
        unsigned int premium; // 单次购买溢价
        bool buffValid;  // 免溢价buff是否生效  [2.0]
        unsigned int count; // 商品数目
        ShopItem items[sc_max_shopitem_num]; // 商品信息
    };

    unsigned int result;
    unsigned int chapterNo;  // 当前第几个章节
    unsigned int eventNo; // 第几个事件
    unsigned int eventId;  // 事件id
    unsigned int tabCount;  // 有几个分页
    ShopTab tabs[sc_max_shoptab_num]; // 分页数据

    ClientRoguelikeShopDataRep() :PacketBase(ABROAD_ROGUELIKE_SHOP_DATA_REQ, sizeof(ClientRoguelikeShopDataRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeShopDataRep));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeShopDataRep& t)
    {
        bos << t.result << t.chapterNo << t.eventNo << t.eventId << t.tabCount;
        for (unsigned int i = 0; i < t.tabCount; i++)
        {
            bos << t.tabs[i].tab << t.tabs[i].buyCnt << t.tabs[i].premium << t.tabs[i].buffValid << t.tabs[i].count;
            auto &item = t.tabs[i].items;
            for (unsigned int k = 0; k < t.tabs[i].count; k++)
            {
                bos << item[k].id << item[k].price;
                if (k >= sc_max_shopitem_num) break;
            }
            if (i >= sc_max_shoptab_num) break;
        }
        return bos;
    }
};

商城购买

  • [2.0]没有变化

事件操作请求

  • [2.0] result添加结果
ABROAD_ROGUELIKE_EVENT_OP_REQ=48522
struct ClientRoguelikeEventOpReq : public PacketBase
{
    enum emOpType
    {
        op_close = 0, // 关闭移除事件
        op_select, // 选项选择
        op_sure, // 确认
    };

    unsigned int chapterNo;  // 当前章节
    unsigned int eventNo; // 第几个事件
    unsigned int opType; // emOpType
    unsigned int select; // 选择了第几项 或者 答题的答案序号
    ClientRoguelikeEventOpReq() :PacketBase(ABROAD_ROGUELIKE_EVENT_OP_REQ, sizeof(ClientRoguelikeEventOpReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEventOpReq));
    }
};

ABROAD_ROGUELIKE_EVENT_OP_REP=48523
struct ClientRoguelikeEventOpRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        event_invalid = 2, // 事件非法
        event_not_find = 3, // 事件找不到
        select_invlid = 4,  // 选择非法
        fight_data_get_failed = 5, // 战斗数据获取失败
        chapter_over = 6, // 本章节已结束
        data_error = 7, // 数据异常,收到该错误请返回模式准备界面
        no_spell_pos_fill = 8, // 无法选择技能,没有额外技能槽
        qa_select_err = 9, // 回答错误 [2.0]
    };

    unsigned int result;  
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int eventId;  // [2.0]
    unsigned int eventType;  // [2.0]
    unsigned int opType;
    unsigned int select;

    ClientRoguelikeEventOpRep() :PacketBase(ABROAD_ROGUELIKE_EVENT_OP_REP, sizeof(ClientRoguelikeEventOpRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEventOpRep));
    }
};

数据更新通知

  • [2.0] 添加通知更新类型
ABROAD_ROGUELIKE_VALUE_UPDATE=48526
struct ClientRoguelikeValueUpdateNtf : public PacketBase
{
    static const unsigned int sc_max_update_num = 20;
    enum emType
    {
        update_power = 0, // 进入模式的体力更新
        update_hp = 1,  // 武将体力初始值与上限
        update_coin = 2, // 金币
        update_handcard_num = 3, // 手牌初始数目与上限
        update_spell_ex_num = 4, // 技能槽额外数目
        update_exp = 5, // 经验
        update_sqk_use = 6, // 手气卡使用次数
        update_score = 7, // 积分
        update_difficulty = 8, // 难度解锁通知
        update_state = 9, // 爬塔状态通知

        update_max,
    };

    struct UpdateValue
    {
        unsigned int type;
        union
        {
            struct {
                unsigned int value;
            }common;  // update_power/update_coin/update_spell_ex_num/update_sqk_use/update_score/update_state

            struct {
                unsigned int count;
                unsigned int value[sc_max_update_num];
            }multi;  // update_difficulty


            struct {
                unsigned int init;  // 初始数目
                int limit; // 上限(增量值)
            }hpCard;  // update_hp/update_handcard_num

            struct {
                unsigned int level; // 当前的等级
                unsigned int exp; // 当前的经验值,大于nextUpExp时表示可升级
                unsigned int nextUpExp; // 升级到下一级需要的经验值
            }exp;  // update_exp
        };
    };

    unsigned int count;
    UpdateValue update[sc_max_update_num];

    ClientRoguelikeValueUpdateNtf() :PacketBase(ABROAD_ROGUELIKE_VALUE_UPDATE, sizeof(ClientRoguelikeValueUpdateNtf))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeValueUpdateNtf));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeValueUpdateNtf& t)
    {
        bos << t.count;
        for (unsigned int i = 0; i < t.count; i++)
        {
            switch (t.update[i].type)
            {
            case update_power:
            case update_coin:
            case update_spell_ex_num:
            case update_sqk_use:
            case update_score:
            case update_state:
                bos << t.update[i].type << t.update[i].common.value;
                break;
            case update_hp:
            case update_handcard_num:
                bos << t.update[i].type << t.update[i].hpCard.init << t.update[i].hpCard.limit;
                break;
            case update_exp:
                bos << t.update[i].type << t.update[i].exp.level << t.update[i].exp.exp << t.update[i].exp.nextUpExp;
                break;
            case update_difficulty:
                bos << t.update[i].type << t.update[i].multi.count;
                for (unsigned int j = 0; j < t.update[i].multi.count; j++)
                {
                    bos << t.update[i].multi.value[j];
                }
                break;
            default:
                break;
            }
            if (i >= sc_max_update_num) break;
        }
        return bos;
    }
};

获取boss数据

  • [2.0]RoguelikePlayerData结构变动
ABROAD_ROGUELIKE_BOSS_DATA_REQ=48529
struct ClientRoguelikeBossDataReq : public PacketBase
{
    bool inGame;  // 是否在游戏对局中
    unsigned int seatId;  // inGame=true时,请求对应座位号的boss数据
    unsigned int chapterNo; 
    unsigned int eventNo; 
    unsigned int optionId;  // 事件选项id,如果某个事件的选项是战斗则需要填充是第几个选项,没有填0 [2.0]

    ClientRoguelikeBossDataReq() :PacketBase(ABROAD_ROGUELIKE_BOSS_DATA_REQ, sizeof(ClientRoguelikeBossDataReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeBossDataReq));
    }
};

// [2.0]
struct RoguelikePlayerData
{
    unsigned int playerType;  // 玩家类型, 0:玩家,1:boss,2:玩家队友 
    unsigned int chrId;  // 武将id
    unsigned int chrStar; // 武将星级(暂时无用)
    unsigned int sqkUse; // 每局手气卡可使用次数(暂时无用)
    unsigned int level;  // 等级
    unsigned int seatid; // 座位
    unsigned int figure; // 身份
    unsigned int initCardNum; // 手牌初始数目
    int limitCardNum; // 手牌上限增量值
    unsigned int initHpNum; // 初始体力
    unsigned int limitHpNum; // 体力上限
    unsigned int buffCount; //buff
    unsigned int buffIds[ClientRoguelikeSpellDataRep::sc_max_spell_num];
    unsigned int cardCount; // 手牌
    unsigned int cardIds[ClientRoguelikeSpellDataRep::sc_max_spell_num];
    unsigned int spellCount; // 技能
    unsigned int spellIds[ClientRoguelikeSpellDataRep::sc_max_spell_num];
    unsigned int equipCount;
    unsigned int equipIds[ClientRoguelikeSpellDataRep::sc_max_spell_num];
};

ABROAD_ROGUELIKE_BOSS_DATA_REP=48530
struct ClientRoguelikeBossDataRep : public PacketBase
{
    static const unsigned int sc_max_boss_count = 8;

    enum emResult
    {
        ok = 0,
        failed = 1,
        event_invalid = 2, 
        data_error = 3, // 数据异常,收到该错误请返回模式准备界面并请求ABROAD_ROGUELIKE_MODE_DATA_REQ
        not_in_roguelike_game = 4, // 不在爬塔游戏对局中
        seat_not_boss = 5, // 该位置不是boss
    };

    unsigned int result;
    bool inGame;
    unsigned int seatId;
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int eventId;
    unsigned int optionId;  // [2.0]
    unsigned int count;
    RoguelikePlayerData player[sc_max_player_count];

    ClientRoguelikeBossDataRep() :PacketBase(ABROAD_ROGUELIKE_BOSS_DATA_REP, sizeof(ClientRoguelikeBossDataRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeBossDataRep));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeBossDataRep& t)
    {
        bos << t.result << t.inGame << t.seatId << t.chapterNo << t.eventNo << t.eventId << t.optionId << t.count;
        for (unsigned int i = 0; i < t.count; i++)
        {
            bos << t.boss[i].playerType << t.boss[i].chrId << t.boss[i].chrStar << t.boss[i].skinId << t.boss[i].sqkUse << t.boss[i].level 
                << t.boss[i].seatid << t.boss[i].figure << t.boss[i].aiLevel << t.boss[i].initCardNum << t.boss[i].limitCardNum 
                << t.boss[i].initHpNum << t.boss[i].limitHpNum << t.boss[i].weaponId << t.boss[i].armorId << t.boss[i].treasureIds[0] 
                << t.boss[i].treasureIds[1] << t.boss[i].buffCount;
            for (unsigned int x = 0; x < t.boss[i].buffCount; x++)
            {
                bos << t.boss[i].buffIds[x];
                if (x >= ClientRoguelikeSpellDataRep::sc_max_spell_num) break;
            }
            bos << t.boss[i].cardCount;
            for (unsigned int x = 0; x < t.boss[i].cardCount; x++)
            {
                bos << t.boss[i].cardIds[x];
                if (x >= ClientRoguelikeSpellDataRep::sc_max_spell_num) break;
            }
            bos << t.boss[i].spellCount;
            for (unsigned int x = 0; x < t.boss[i].spellCount; x++)
            {
                bos << t.boss[i].spellIds[x];
                if (x >= ClientRoguelikeSpellDataRep::sc_max_spell_num) break;
            }
            if (i >= sc_max_boss_count) break;
        }
        return bos;
    }
};

检查爬塔一轮是否结束

  • [2.0]没有变化

爬塔结果通知

  • [2.0]没有变化

爬塔对局结果

战斗请求

  • [2.0]新增
  • 请求战斗使用该协议,不再使用ABROAD_ROGUELIKE_EVENT_OP_REQ = 48522协议;
ABROAD_ROGUELIKE_TO_FIGHT_REQ=48537
struct ClientRoguelikeToFightReq : public PacketBase
{
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int optionId;  // 事件选项

    ClientRoguelikeToFightReq() :PacketBase(ABROAD_ROGUELIKE_TO_FIGHT_REQ, sizeof(ClientRoguelikeToFightReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeToFightReq));
    }
};

ABROAD_ROGUELIKE_TO_FIGHT_REP=48538
struct ClientRoguelikeToFightRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        event_invalid = 2,
        not_fight = 3, 
        data_error = 4,
    };

    unsigned int result;
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int optionId;

    ClientRoguelikeToFightRep() :PacketBase(ABROAD_ROGUELIKE_TO_FIGHT_REP, sizeof(ClientRoguelikeToFightRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeToFightRep));
    }
};

随机项选择通知

  • [2.0]新增
  • 事件内容选择为随机项时,通知客户端可选择内容
struct EffectItemData
{
    // 类型,emRoguelikeEffectType
    unsigned int type;

    // 参数值
    // 一般只有value[0]有效
    // type=Effect_Hp,  value[0]为增加上限(需支持负值,至多到0);value[1]为体力,可不配,不配默认和value[0]一致,不可大于value[0]值
    // type=Effect_Spell/Effect_Buff/Effect_Card/Effect_Equip,  value[0]为指定技能id(可不配),value[1]为取特定组随机,配了0就不用管1了
    int value[3];
};

ABROAD_ROGUELIKE_EFFECT_SELECT_NTF=48539
struct ClientRoguelikeEffectSelectNtf : public PacketBase
{
    static const unsigned int sc_effect_max_num = 10;

    unsigned int chapterNo;  
    unsigned int eventNo; 
    unsigned int optionId; 
    unsigned int mustCnt;
    EffectItemData mustEffect[sc_effect_max_num];
    unsigned int randomGetNum;  // 等于0不需要做选择请求
    unsigned int randomCnt; 
    EffectItemData randomEffect[sc_effect_max_num];

    ClientRoguelikeEffectSelectNtf() :PacketBase(ABROAD_ROGUELIKE_EFFECT_SELECT_NTF, sizeof(ClientRoguelikeEffectSelectNtf))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEffectSelectNtf));
    }

    friend bostream& operator<<(bostream& bos, const ClientRoguelikeEffectSelectNtf& t)
    {
        bos << t.chapterNo << t.eventNo << t.optionId << t.mustCnt;
        for (unsigned int i = 0; i < t.mustCnt; i++)
        {
            bos << t.mustEffect[i].type << t.mustEffect[i].value[0] << t.mustEffect[i].value[1] << t.mustEffect[i].value[2];
            if (i >= sc_effect_max_num) break;
        }
        bos << t.randomGetNum << t.randomCnt;
        for (unsigned int i = 0; i < t.randomCnt; i++)
        {
            bos << t.randomEffect[i].type << t.randomEffect[i].value[0] << t.randomEffect[i].value[1] << t.randomEffect[i].value[2];
            if (i >= sc_effect_max_num) break;
        }
        return bos;
    }
};

随机项选择

  • [2.0]新增
ABROAD_ROGUELIKE_EFFECT_SELECT_REQ=48540
struct ClientRoguelikeEffectSelectReq : public PacketBase
{
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int optionId;
    unsigned int selectCount;
    unsigned int selectRandomIds[ClientRoguelikeLevelUpRep::sc_effect_max_num];
    ClientRoguelikeEffectSelectReq() :PacketBase(ABROAD_ROGUELIKE_EFFECT_SELECT_REQ, sizeof(ClientRoguelikeEffectSelectReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEffectSelectReq));
    }
};

ABROAD_ROGUELIKE_EFFECT_SELECT_REP=48541
struct ClientRoguelikeEffectSelectRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed = 1,
        data_error = 2,
        event_invalid = 3,
        event_not_find = 4,
        select_invalid = 5,
    };

    unsigned int result;
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int optionId;
    ClientRoguelikeEffectSelectRep() :PacketBase(ABROAD_ROGUELIKE_EFFECT_SELECT_REP, sizeof(ClientRoguelikeEffectSelectRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEffectSelectRep));
    }
};

答题事件数据

  • [2.0]新增
ABROAD_ROGUELIKE_QA_EVENT_REQ=48542
struct ClientRoguelikeQaEventReq : public PacketBase
{
    unsigned int chapterNo;  // 当前第几个章节
    unsigned int eventNo; // 事件序号

    ClientRoguelikeQaEventReq() :PacketBase(ABROAD_ROGUELIKE_QA_EVENT_REQ, sizeof(ClientRoguelikeQaEventReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeQaEventReq));
    }
};

ABROAD_ROGUELIKE_QA_EVENT_REP=48543
struct ClientRoguelikeQaEventRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed,
        data_error,
        event_invalid,
        event_not_find,
    };
    unsigned int result;
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int eventId;
    unsigned int qaId;  // 题库id
    unsigned int excludeIdx;  // buff 排除的错误答案序号 大于0有效
    unsigned int randoms[4]; // 答案序号1234的随机排序  
    ClientRoguelikeQaEventRep() :PacketBase(ABROAD_ROGUELIKE_QA_EVENT_REP, sizeof(ClientRoguelikeQaEventRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeQaEventRep));
    }
};

装备牌升级通知

  • [2.0]新增
  • buff 20050 马钧锻造可以升级部分装备牌
ABROAD_ROGUELIKE_EQUIP_UP_NTF=48544
struct ClientRoguelikeEquipUpNtf : public PacketBase
{
    static const unsigned int sc_up_max_num = 10;

    struct UpItem
    {
        unsigned int from;
        unsigned int to;
    };
    unsigned int spellType;  // emEquipType,目前只有装备牌(3)的武器和防具可以升级
    unsigned int count;
    UpItem upItems[sc_up_max_num];

    ClientRoguelikeEquipUpNtf() :PacketBase(ABROAD_ROGUELIKE_EQUIP_UP_NTF, sizeof(ClientRoguelikeEquipUpNtf))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeEquipUpNtf));
    }
};

判定事件更新通知

  • [2.0]新增
ABROAD_ROGUELIKE_JUDGE_EVENT_UPDATE=48545
struct ClientRoguelikeJudgeEventUpdate: public PacketBase
{
    unsigned int chapterNo;
    unsigned int eventNo;  // 几号事件
    unsigned int eventId;  // 更新后的事件id

    ClientRoguelikeJudgeEventUpdate() :PacketBase(ABROAD_ROGUELIKE_JUDGE_EVENT_UPDATE, sizeof(ClientRoguelikeJudgeEventUpdate))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeJudgeEventUpdate));
    }
};

对局外buff触发通知

  • [2.0]新增
ABROAD_ROGUELIKE_BUFF_TRIGGER_NTF=48546
struct ClientRoguelikeBuffTriggerNtf : public PacketBase
{
    static const unsigned int sc_buff_max_num = 50;

    unsigned int count;
    unsigned int buff[sc_buff_max_num];

    ClientRoguelikeBuffTriggerNtf() :PacketBase(ABROAD_ROGUELIKE_BUFF_TRIGGER_NTF, sizeof(ClientRoguelikeBuffTriggerNtf))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeBuffTriggerNtf));
    }
};

对局数据

  • [2.0]新增
ABROAD_ROGUELIKE_GAME_DATA_REQ=48547
struct ClientRoguelikeGameDataReq : public PacketBase
{
    ClientRoguelikeGameDataReq() :PacketBase(ABROAD_ROGUELIKE_GAME_DATA_REQ, sizeof(ClientRoguelikeGameDataReq))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeGameDataReq));
    }
};

ABROAD_ROGUELIKE_GAME_DATA_REP=48548
struct ClientRoguelikeGameDataRep : public PacketBase
{
    enum emResult
    {
        ok = 0,
        failed,
        not_in_roguelike_game,
    };

    unsigned int result;
    unsigned int chapterNo;
    unsigned int eventNo;
    unsigned int eventId;
    unsigned int optionId;
    unsigned int difficulty;
    unsigned int fightId;

    ClientRoguelikeGameDataRep() :PacketBase(ABROAD_ROGUELIKE_GAME_DATA_REP, sizeof(ClientRoguelikeGameDataRep))
    {
        PRO_ZERO_MEMORY(this, sizeof(ClientRoguelikeGameDataRep));
    }
};