商店:基础数据及购买

This commit is contained in:
luying
2022-07-25 17:41:04 +08:00
parent 4bf75fcda5
commit 4cf4511ee7
13 changed files with 952 additions and 565 deletions

View File

@@ -1,5 +1,5 @@
import { Application, BackendSession } from "pinus";
import { gameData } from "../../../pubUtils/data";
import { gameData, hasShopType } from "../../../pubUtils/data";
import { parseGoodStr, resResult } from "../../../pubUtils/util";
import { STATUS, GUILD_STRUCTURE, ITID, CONSUME_TYPE, HERO_QUALITY_TYPE, HERO_GROW_MAX, ITEM_CHANGE_REASON } from "../../../consts";
import { DicShopListModel } from "../../../db/DicShopList";
@@ -8,7 +8,7 @@ import { handleCost, addItems } from "../../../services/role/rewardService";
import { GuildModel } from "../../../db/Guild";
import { SHOP } from "../../../pubUtils/dicParam";
import { HeroModel } from "../../../db/Hero";
import { getShopListById } from "../../../services/shopService";
import { getShopDicById, getShopListByType, getShopPrice } from "../../../services/shopService";
import { RewardInter } from "../../../pubUtils/interface";
import { RoleModel } from "../../../db/Role";
@@ -21,20 +21,21 @@ export class ShopHandler {
}
// 获得商品列表
async getShopList(msg: { shopId: number }, session: BackendSession) {
async getShopList(msg: { shop: number, type: number }, session: BackendSession) {
let roleId = session.get('roleId');
let { shopId } = msg;
let serverId = session.get('serverId');
let { shop, type } = msg;
if(!gameData.shopList.has(shopId)) {
if(!hasShopType(shop, type)) {
return resResult(STATUS.WRONG_PARMS);
}
const shopItemList = await getShopListById(shopId, roleId);
return resResult(STATUS.SUCCESS, { shopItemList });
const result = await getShopListByType(shop, type, roleId, serverId);
return resResult(STATUS.SUCCESS, result);
}
// 购买商品
async purchase(msg: { shopItemId: number, count: number }, session: BackendSession) {
async purchase(msg: { activityId: number, shopItemId: number, count: number }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
@@ -42,24 +43,24 @@ export class ShopHandler {
let serverId = session.get('serverId');
let vipStartTime: number = session.get('vipStartTime');
let { shopItemId, count } = msg;
let { activityId, shopItemId, count } = msg;
if( count < 0) return resResult(STATUS.WRONG_PARMS);
let dicShopItem = gameData.shopItem.get(shopItemId);
let dicShopItem = await getShopDicById(activityId, shopItemId, roleId, serverId);
if(!dicShopItem) return resResult(STATUS.DIC_DATA_NOT_FOUND);
if(dicShopItem.guildLvLimit) {
let myGuild = await GuildModel.findByCode(guildCode, serverId);
if(!myGuild || myGuild.lv < dicShopItem.guildLvLimit) return resResult(STATUS.GUILD_LV_LIMIT);
}
if(dicShopItem.lvlimit) {
if(dicShopItem.lvLimit) {
let role = await RoleModel.findByRoleId(roleId, 'lv');
if(role.lv < dicShopItem.lvlimit) {
if(role.lv < dicShopItem.lvLimit) {
return resResult(STATUS.LV_LIMIT);
}
}
let userShop = await UserShopModel.findByRoleAndItem(roleId, dicShopItem.id);
let userShop = await UserShopModel.findByRoleAndItem(roleId, activityId, dicShopItem);
if(dicShopItem.purchaseLimit != -1) {
if(vipStartTime > 0 && dicShopItem.purchaseLimit != -1) {
if(userShop && userShop.count + count > dicShopItem.purchaseLimit + dicShopItem.vipPurchaseLimit) {
@@ -73,20 +74,17 @@ export class ShopHandler {
}
// 消耗
let cost = [{
id: dicShopItem.money,
count: Math.round(dicShopItem.price * count)
}];
let cost = getShopPrice(dicShopItem.money, count, userShop?.count||0, dicShopItem.price);
let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.SHOP_PURCHASE);
if(!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);
// 次数
userShop = await UserShopModel.purchase(roleId, roleName, dicShopItem, count);
userShop = await UserShopModel.purchase(roleId, roleName, activityId, dicShopItem, count);
if(!userShop) return resResult(STATUS.BUY_COUNT_OVER);
// 获得
let reward = [{
id: dicShopItem.goodid,
id: dicShopItem.goodId,
count
}];
let goods = await addItems(roleId, roleName, sid, reward, ITEM_CHANGE_REASON.SHOP_PURCHASE);

View File

@@ -133,7 +133,7 @@ export async function getModuleData(type: string, data: { role: RoleType, sessio
role.spines = role.spines.filter(cur => cur.status);
return pick(role, ENTERY_ROLE_PICK);
case 'shop': // 商店
return await getAllShopList(roleId);
return await getAllShopList(roleId, serverId);
case 'rank': // 排名
const rewards = await getRankFirstReward(role, serverId);
return { rewards }

View File

@@ -1,30 +1,104 @@
import { gameData } from "../pubUtils/data";
import { ShopItemListParam } from '../domain/roleField/shop';
import { UserShopModel } from "../db/UserShop";
import { gameData, getShopType } from "../pubUtils/data";
import { ShopData, ShopReturnData } from '../domain/roleField/shop';
import { UserShopModel, UserShopType } from "../db/UserShop";
import { getActivitiesByType, getActivityById } from "./activity/activityService";
import { ACTIVITY_TYPE } from "../consts";
import { getRoleCreateTime, getServerCreateTime } from "./redisService";
import { decodeArrayListStr } from "../pubUtils/util";
export async function getShopListById(shopId: number, roleId: string) {
export async function getAllShopList(roleId: string, serverId: number) {
let userShopRecs = await UserShopModel.findByRoleId(roleId);
let activities = await getShopActivityDatas(roleId, serverId);
let shopItemList = new Array<ShopItemListParam>(); // 返回
let userShopRecs = await UserShopModel.findMapByShopId(roleId, shopId);
// console.log(JSON.stringify([...userShopRecs]))
let dicShop = gameData.shop.get(shopId)||[];
for(let { id, type } of dicShop) {
let buyCount = userShopRecs.has(id)?userShopRecs.get(id).count: 0;
let param = new ShopItemListParam(id, 1, type, buyCount, 0);
shopItemList.push(param);
let shops: ShopReturnData[] = [];
for(let [_, { shop, type } ] of gameData.shopType) {
let shopData = await getShopListByData(shop, type, userShopRecs, activities);
shops.push(shopData);
}
shopItemList = shopItemList.sort((a, b) => b.order - a.order);
return shopItemList;
return shops;
}
export async function getAllShopList(roleId: string) {
let shopLists: {shopId: number, shopItemList: ShopItemListParam[]}[] = [];
for(let [ shopId ] of gameData.shopList) {
let shopItemList = await getShopListById(shopId, roleId);
shopLists.push({ shopId, shopItemList });
export async function getShopListByType(shop: number, type: number, roleId: string, serverId: number) {
let userShopRecs = await UserShopModel.findByShopType(roleId, shop, type);
let activities = await getShopActivityDatas(roleId, serverId);
return await getShopListByData(shop, type, userShopRecs, activities);
}
async function getShopListByData(shop: number, type: number, userShopRecs: UserShopType[], activities: ShopData[]) {
let result = new ShopReturnData(shop, type);
let dicShopType = getShopType(shop, type);
if(dicShopType && dicShopType.isLimit) {
for(let activity of activities) {
result.setDicByActivity(activity);
}
}
return shopLists;
for(let userShop of userShopRecs) {
result.setUserRecords(userShop);
}
return result;
}
export async function getShopDicById(activityId: number, itemId: number, roleId: string, serverId: number) {
if(activityId == 0) {
return gameData.shopItem.get(itemId);
} else {
let activityData = await getShopActivityById(roleId, serverId, activityId);
return activityData.findByItemId(itemId);
}
}
export async function getShopActivityDatas(roleId: string, serverId: number) {
let createTime = await getRoleCreateTime(roleId);
let serverTime = await getServerCreateTime(serverId);
let activities = await getActivitiesByType(serverId, ACTIVITY_TYPE.SHOP);
return activities.map(activity => {
return new ShopData(activity, createTime, serverTime);
});
}
export async function getShopActivityById(roleId: string, serverId: number, activityId: number) {
let createTime = await getRoleCreateTime(roleId);
let serverTime = await getServerCreateTime(serverId);
let activity = await getActivityById(activityId);
return new ShopData(activity, createTime, serverTime);
}
/**
*
* @param goodId 物品
* @param count 本次购买次数
* @param buyCount 上次购买到多少次
* @param str 字典
*/
export function getShopPrice(goodId: number, count: number, buyCount: number, str: string) {
let arr = parseShopPrice(str);
let result = 0;
for(let i = 0; i < count; i++) {
let curPrice = 0;
for(let { times, price } of arr) {
curPrice = price;
if(buyCount + i <= times) break;
}
result += curPrice;
}
return [{
id: goodId,
count: result
}]
}
function parseShopPrice(str: string) {
let result = new Array<{ times: number, price: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [times, price] of decodeArr) {
if (isNaN(parseInt(times)) || isNaN(parseInt(price))) {
throw new Error('data table format wrong');
}
result.push({ times: parseInt(times), price: parseInt(price) });
}
return result
}

View File

@@ -56,6 +56,7 @@ export enum ACTIVITY_TYPE {
TIME_LIMIT_RANK = 41, // 限时排行榜
TASK_PASS = 42, // 战令活动
GUILD_PAY = 43, // 军团充值人数
SHOP = 44, // 限时商店
}
/**

View File

@@ -493,7 +493,7 @@ export const FILENAME = {
DIC_CITY_ACTIVITY_REWARD: 'dic_zyz_cityActivityReward',
DIC_RACE_ACTIVITY: 'dic_zyz_raceActivity',
DIC_SHOP: 'dic_zyz_shop',
DIC_SHOP_LIST: 'dic_zyz_shopList',
DIC_SHOP_TYPE: 'dic_zyz_shopType',
DIC_RANK: 'dic_zyz_rankingType',
DIC_RANK_REWARD: 'dic_zyz_rankingReward',
DIC_MAIN_TASK: 'dic_zyz_mainTask',

View File

@@ -3,7 +3,6 @@ import { index, getModelForClass, prop, DocumentType, modelOptions } from '@type
import { genCode } from '../pubUtils/util';
import { getZeroPointD } from '../pubUtils/timeUtil';
import { SHOP_REFRESH_TYPE } from '../consts';
import { DicShop } from '../pubUtils/dictionary/DicShop';
/**
* 玩家购买商店记录表,每个商品一条,每次刷新新建一条
@@ -22,7 +21,13 @@ export default class UserShop extends BaseModel {
roleName: string; // 玩家名
@prop({ required: true })
shopId: number; // 商店id
activityId: number; // 商店id
@prop({ required: true })
shop: number; // 商店id
@prop({ required: true })
type: number; // 商店页签id
@prop({ required: true })
itemId: number; // 商品id
@@ -50,36 +55,34 @@ export default class UserShop extends BaseModel {
}
public static async findByShopId(roleId: string, shopId: number) {
public static async findByShopType(roleId: string, shopId: number, typeId: number) {
let timeCondition = this.getRefreshCondition();
let rec: UserShopType[] = await UserShopModel.find({ shopId, roleId, $or: timeCondition }).lean();
let rec: UserShopType[] = await UserShopModel.find({ shopId, typeId, roleId, $or: timeCondition }).lean();
return rec;
}
public static async findMapByShopId(roleId: string, shopId: number) {
let rec = await this.findByShopId(roleId, shopId);
let map = new Map<number, UserShopType>();
for(let userShop of rec) {
map.set(userShop.itemId, userShop);
}
return map
}
public static async findByRoleAndItem(roleId: string, itemId: number) {
public static async findByRoleId(roleId: string) {
let timeCondition = this.getRefreshCondition();
let rec: UserShopType = await UserShopModel.findOne({ roleId, itemId, $or: timeCondition }).lean();
let rec: UserShopType[] = await UserShopModel.find({ roleId, $or: timeCondition }).lean();
return rec;
}
public static async purchase(roleId: string, roleName: string, dicShopItem: DicShop, inc: number) {
public static async findByRoleAndItem(roleId: string, activityId: number, dicShopItem: { id: number, shop: number, type: number }) {
let timeCondition = this.getRefreshCondition();
let { id, shop, type } = dicShopItem;
let rec: UserShopType = await UserShopModel.findOne({ roleId, itemId: id, shop, type, activityId, $or: timeCondition }).lean();
return rec;
}
public static async purchase(roleId: string, roleName: string, activityId: number, dicShopItem: { id: number, goodId: number, refreshType: number, shop: number, type: number }, inc: number) {
let code = genCode(8);
let timeCondition = this.getRefreshCondition();
let { id, goodid, refreshType, shop } = dicShopItem;
let { id, goodId, refreshType, shop, type } = dicShopItem;
let rec: UserShopType = await UserShopModel.findOneAndUpdate(
{ roleId, itemId: id, $or: timeCondition },
{ $setOnInsert: { roleName, code, goodId: goodid, refreshType, shopId: shop }, $inc: { count: inc } },
{ roleId, itemId: id, $or: timeCondition, activityId, shop, type },
{ $setOnInsert: { roleName, code, goodId, refreshType }, $inc: { count: inc } },
{ new: true, upsert: true }
).lean();
return rec;

View File

@@ -1,17 +1,134 @@
import { SHOP_REFRESH_TYPE } from "../../consts";
import { ActivityModelType } from "../../db/Activity";
import { UserShopType } from "../../db/UserShop";
import { ActivityBase } from "../activityField/activityField";
// hero表内属性基础格式
export class ShopItemListParam {
interface ShopInDb {
shop: number;
type: number;
list: ShopItemInDb[];
}
interface ShopItemInDb {
id: number;
goodId: number;
goodName: string;
guildLvLimit: number;
lvLimit: number;
purchaseLimit: number;
vipPurchaseLimit: number;
refreshType: number;
money: number;
price: string;
productID: string;
chosen: number;
indirectId: number;
createTime: number;
beginTime: number;
endTime: number;
}
export class ShopData extends ActivityBase {
shop: number;
type: number;
list: ShopDicData[] = [];
constructor(activityData: ActivityModelType, createTime: number, serverTime: number) {
super(activityData, createTime, serverTime)
this.initData(activityData.data)
}
public initData(data: string) {
let result: ShopInDb = JSON.parse(data);
if(result) {
this.shop = result.shop;
this.type = result.type;
for (let obj of result.list) {
this.list.push(new ShopDicData(obj))
}
}
}
public findByItemId(itemId: number) {
let item = this.list.find(cur => cur.id == itemId);
if(!item) return null;
return {...item, shop: this.shop, type: this.type};
}
}
export class ShopDicData {
isNew: boolean = false;
id: number;
goodId: number;
goodName: string;
guildLvLimit: number;
lvLimit: number;
purchaseLimit: number;
vipPurchaseLimit: number;
refreshType: SHOP_REFRESH_TYPE;
money: number;
price: string;
productID: string = '';
chosen: number;
indirectId: number;
createTime: number;
beginTime: number;
endTime: number;
constructor(obj: ShopItemInDb) {
this.id = obj.id;
this.goodId = obj.goodId;
this.goodName = obj.goodName;
this.guildLvLimit = obj.guildLvLimit;
this.lvLimit = obj.lvLimit;
this.purchaseLimit = obj.purchaseLimit;
this.vipPurchaseLimit = obj.vipPurchaseLimit;
this.refreshType = obj.refreshType;
this.money = obj.money;
this.price = obj.price;
this.productID = obj.productID;
this.chosen = obj.chosen;
this.indirectId = obj.indirectId;
this.createTime = obj.createTime;
this.beginTime = obj.beginTime;
this.endTime = obj.endTime;
}
}
export class ShopReturnData {
activityId: number = 0;
shop: number;
type: number;
dic: ShopDicData[] = [];
records: ShopReturnRecordData[] = [];
constructor(shop: number, type: number) {
this.shop = shop;
this.type = type;
}
public setDicByActivity(activityData: ShopData) {
if(!activityData || activityData.shop != this.shop || activityData.type != this.type) return;
this.activityId = activityData.activityId;
this.dic.push(...activityData.list)
}
public setUserRecords(record: UserShopType) {
if(!record || record.shop != this.shop || record.type != this.type) return;
let recordResult = new ShopReturnRecordData(record);
if(recordResult && recordResult.shopItemId) this.records.push(recordResult)
}
}
export class ShopReturnRecordData {
shopItemId: number;
discount: number;
typeId: number;
buyCount: number;
order: number;
constructor(shopItemId: number, discount: number, typeId: number, buyCount: number, order: number) {
this.shopItemId = shopItemId;
this.discount = discount;
this.typeId = typeId;
this.buyCount = buyCount;
this.order = order;
constructor(record: UserShopType) {
if(!record) return;
this.shopItemId = record.itemId;
this.buyCount = record.count;
}
}

View File

@@ -59,8 +59,8 @@ import { GUILDACTIVITY, RECRUIT } from "./dicParam";
import * as param from "./dicParam";
import { decodeIdCntArrayStr, parseGoodStr, decodeArrayListStr, getRandEelm, readTsFile, getRandEelmWithWeight } from "./util";
import { RACE_EVENT_TYPE } from "../consts";
import { dicShop, dicShopItem, loadShop } from "./dictionary/DicShop";
import { dicShopList, loadShopList } from "./dictionary/DicShopList";
import { dicShopByType, dicShopItem, loadShop } from "./dictionary/DicShop";
import { dicShopType, loadShopType } from "./dictionary/DicShopType";
import { dicRank, loadRank } from "./dictionary/DicRank";
import { dicRankReward, loadRankReward } from "./dictionary/DicRankReward";
import { dicTaskType, taskMap, dicMainTask, dicDailyTask, dicAchievement, loadTask, dicPvpDailyTask } from "./dictionary/DicTask";
@@ -198,9 +198,9 @@ export const gameData = {
raceActivityEncounter: { events: new Map<number, number>(), eventNum: 0 },
raceNormalItems: new Array<{id: number, total: number, max: number}>(),
raceEventItems: new Array<{id: number, count: number}>(),
shop: dicShop,
shopItemByType: dicShopByType,
shopItem: dicShopItem,
shopList: dicShopList,
shopType: dicShopType,
rank: dicRank,
generalRankReward: dicRankReward,
taskType: dicTaskType,
@@ -1010,6 +1010,15 @@ export function getLadderRankReward(myRank: number) {
return null
}
export function hasShopType(shop: number, type: number) {
return gameData.shopType.has(`${shop}_${type}`);
}
export function getShopType(shop: number, type: number) {
let key = `${shop}_${type}`;
return gameData.shopType.get(key)||null;
}
// 初始加载
function initDatas() {
parseDicParam();
@@ -1138,7 +1147,7 @@ function loadDatas() {
loadCityActivityReward();
loadRaceActivity();
loadShop();
loadShopList();
loadShopType();
loadRank();
loadRankReward();
loadTask();

View File

@@ -7,9 +7,9 @@ export interface DicShop {
// 商品id
readonly id: number;
// 物品表id
readonly goodid: number;
readonly goodId: number;
// 物品表名
readonly goodname: number;
readonly goodName: number;
// 商店id 对应 dic_zyz_shoplist的id
readonly shop: number;
// 商店标签 对应 dic_zyz_shopType的id
@@ -17,7 +17,7 @@ export interface DicShop {
// 军团等级需求
readonly guildLvLimit: number;
// 玩家等级需求
readonly lvlimit: number;
readonly lvLimit: number;
// 购买次数限制
readonly purchaseLimit: number;
// vip购买次数限制
@@ -27,26 +27,27 @@ export interface DicShop {
// 用于购买的货币
readonly money: number;
// 价格
readonly price: number;
readonly price: string;
}
export const dicShopItem = new Map<number, DicShop>(); // itemid => DicShop
export const dicShop = new Map<number, DicShop[]>(); // shop => DicShop[]
export const dicShopByType = new Map<string, { shop: number, type: number, items: DicShop[] }>(); // shop + type => DicShop[]
export function loadShop() {
dicShopItem.clear();
dicShop.clear();
dicShopByType.clear();
let arr = readFileAndParse(FILENAME.DIC_SHOP);
arr.forEach(o => {
o.guildLvLimit = o.guildlvlimit == '&'?0: o.guildlvlimit;
o.lvlimit = o.lvlimit == '&'?0: o.lvlimit;
o.purchaseLimit = o.purchaselimit == '&'?-1: (o.purchaselimit||0);
o.vipPurchaseLimit = o.vipPurchaseLimit == '&'?-1: (o.vipPurchaseLimit||0);
if(!dicShop.has(o.shop)) {
dicShop.set(o.shop, [o]);
o.guildLvLimit = o.guildLvLimit == '&'?0: o.guildLvLimit;
o.lvLimit = o.lvLimit == '&'?0: o.lvLimit;
o.purchaseLimit = o.purchaseLimit == '&'?-1: (o.purchaseLimit||0);
o.vipPurchaseLimit = o.vipPurchaseLimit == '&'? 0: (o.vipPurchaseLimit||0);
let key = `${o.shop}_${o.type}`;
if(!dicShopByType.has(key)) {
dicShopByType.set(key, { shop: o.shop, type: o.type, items: [] });
} else {
dicShop.get(o.shop).push(o);
dicShopByType.get(key).items.push(o);
}
dicShopItem.set(o.id, o);
});

View File

@@ -1,28 +0,0 @@
// 商店表
import { readFileAndParse, parseNumberList } from '../util'
import { FILENAME } from '../../consts'
export interface DicShopList {
// 商店id
readonly shopid: number;
// 商店名
readonly shopname: number;
// 商店内标签,小分类
readonly type: number[];
// 使用的货币
readonly money: number[];
}
export const dicShopList = new Map<number, DicShopList>();
export function loadShopList() {
dicShopList.clear();
let arr = readFileAndParse(FILENAME.DIC_SHOP_LIST);
arr.forEach(o => {
o.type = parseNumberList(o.typeid);
o.money = parseNumberList(o.moneyid);
dicShopList.set(o.shopid, o);
});
arr = undefined;
}

View File

@@ -0,0 +1,26 @@
// 商店表
import { readFileAndParse } from '../util'
import { FILENAME } from '../../consts'
export interface DicShopType {
// 商店id
readonly shop: number;
// 商店内标签,小分类
readonly type: number;
// 是否限时
readonly isLimit: boolean;
}
export const dicShopType = new Map<string, DicShopType>(); // shop+type => DicShopType
export function loadShopType() {
dicShopType.clear();
let arr = readFileAndParse(FILENAME.DIC_SHOP_TYPE);
arr.forEach(o => {
o.type = o.typeid;
if(o.shop != '&') {
dicShopType.set(`${o.shop}_${o.type}`, o);
}
});
arr = undefined;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,146 @@
[
{
"id": 1,
"typeid": 1,
"name": "武器"
"name": "武器",
"shop": "&",
"isLimit": 0,
"openLimit": "&"
},
{
"id": 2,
"typeid": 2,
"name": "宝石"
"name": "宝石",
"shop": 7,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 3,
"typeid": 3,
"name": "藏宝图"
"name": "藏宝图",
"shop": 6,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 4,
"typeid": 4,
"name": "精魄"
"name": "精魄",
"shop": "&",
"isLimit": 0,
"openLimit": "&"
},
{
"id": 5,
"typeid": 5,
"name": "奇珍"
"name": "奇珍",
"shop": 1,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 6,
"typeid": 6,
"name": "防具"
"name": "防具",
"shop": "&",
"isLimit": 0,
"openLimit": "&"
},
{
"id": 7,
"typeid": 7,
"name": "臻选"
"name": "臻选",
"shop": 5,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 8,
"typeid": 9,
"name": "将魂回收"
"name": "将魂回收",
"shop": 8,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 9,
"typeid": 10,
"name": "将魂兑换"
"name": "将魂兑换",
"shop": 8,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 10,
"typeid": 11,
"name": "图纸"
"name": "图纸",
"shop": 7,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 11,
"typeid": 12,
"name": "竞技"
"name": "竞技",
"shop": 5,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 12,
"typeid": 13,
"name": "名将擂台",
"shop": 4,
"isLimit": 0,
"openLimit": "0&124"
},
{
"id": 13,
"typeid": 14,
"name": "擂台限定",
"shop": 4,
"isLimit": 0,
"openLimit": "0&124"
},
{
"id": 14,
"typeid": 15,
"name": "巅峰演武",
"shop": 4,
"isLimit": 0,
"openLimit": "50&"
},
{
"id": 15,
"typeid": 11,
"name": "图纸",
"shop": 2,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 16,
"typeid": 5,
"name": "奇珍",
"shop": 2,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 17,
"typeid": 5,
"name": "奇珍",
"shop": 3,
"isLimit": 0,
"openLimit": "&"
},
{
"id": 18,
"typeid": 16,
"name": "限时",
"shop": 5,
"isLimit": 1,
"openLimit": "&"
}
]