在游戏设计中,好的导表设计可以大大降低策划的配置量,降低程序与策划的沟通量,以及简化代码程序的使用。
以下时一个可供参考的导表配置测试#xxx#1000.xlsx
以及说明:
ID | 定义 | 名称 | 描述 | int | bool | 枚举 | 引用 | 数组 | 对象 | 时间 | 日期 | 对象数组 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
id | string | int | bool | enum | ref>item |
int[5] |
{weight:int,val:int} |
time | date | {weight:int,val:int}[10] |
||
普通 common 1 稀有 rare 2 史诗 epic 3 |
||||||||||||
only_client | only_server | |||||||||||
1 | test | 测试 | 1 | true | 普通 | 金币 | 1,2,3 | 1,1 | 1h | 2025-01-01 00:00:00 | 1,2 1,2 |
定义列
会根据填入的名称自动生成对应的const值或者宏定义对应id值。例如#define k_xxx_test 1
。对应 #define k_<table_name>_<define_name> <id>
。此列的好处是无需开发人员在对各种特殊id写死在代码里,仅需要调表导表即可,可以统一前后端对特殊值的定义名称列
是对这一行的简单可视化描述,用于其他表ref引用。例如ref>item
则表示引用道具表,只需填列名称即可,不需要填id。填id可视化极差int
bool
time
date
列则是对应类型的数据会自动转化成int值。填入的时候只需要按照可视化的方式填写即可。例如time: 1h
1d
1s
1m
分别表示 1小时,1天,1秒,1分钟。date则直接按照时间格式填入会转化成时间戳。,
隔开,单行填入,
,;
,\n
,|
隔开。特殊的:,
优先用于对象匹配only_client
表示此行仅客户端导出,only_server
表示此列仅服务导出。没有填写则表示都导出。第二列没有填写的不导出,第一列id列为空的列不导出。使用脚本读取表格数据,然后处理各种数据转换,和引用转换,最终生成一份json配置文件(或者按照表分开成多个json文件)。
根据上边思路,下边使用nodejs实现一个表格数据的解析的参考示例:
注意下边的解析暂时没有加入
only_server
,only_client
列的区分。导出的是用于c/c++
的.h
文件。string
类型区分成string
,text
,path
对应不同长度限制的字符串
-> nodejs依赖
{
"dependencies": {
"exceljs": "^4.4.0"
}
}
-> 导表解析xlsx文件
const ExcelJS = require('exceljs');
const fs = require("fs");
const { makeConfig } = require("./config");
process.on('unhandledRejection', (reason, promise) => {
console.log('导表出错:\n', reason.message);
process.exit(-1)
});
(async function () {
let dir = "./xlsx";
let files = fs.readdirSync(dir);
let sheets = [];
for (const file of files) {
if (!file.endsWith(".xlsx") || file.startsWith("~") || file.startsWith("!")) {
continue;
}
let ns = file.substring(0, file.length - '.xlsx'.length).split("#");
if (ns.length != 3) {
continue;
}
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.readFile(dir + "/" + file);
for (const sheet of workbook.worksheets) {
if (sheet.name == "data") { // 仅导出名为data的sheet, 如需多个配置在一个xlsx中,需要修改此行
let datas = [];
for (let r = 0; r < sheet.rowCount; r++) {
let row = sheet.getRow(r + 1);
let rowValues = [];
for (let cl = 0; cl < row.cellCount; cl++) {
rowValues.push(row.getCell(cl + 1).text);
}
datas.push(rowValues);
}
sheets.push({
sheetName: ns[0],
key: ns[1],
size: Number.parseInt(ns[2]),
datas
});
break;
}
}
}
makeConfig(sheets);
})();
-> 导表转化配置数据为json,并且生成.h
文件
const strSize = {
string: 128,
path: 256,
text: 1024,
big_text: 10240,
huge_text: 102400
};
function md5compute(str){
const crypto = require('crypto');
const hash = crypto.createHash('md5');
hash.update(str, 'utf8');
return hash.digest('hex');
}
function fileUpdate(path, content, force) {
const fs = require("fs");
if (!fs.existsSync(path) || force) {
console.log("update: " + path);
fs.writeFileSync(path, content);
return;
}
let oldContent = fs.readFileSync(path).toString();
if (oldContent == content) {
return;
} else {
console.log("update: " + path);
fs.writeFileSync(path, content);
}
}
function makeStruct(sheetName, key, names, fileds, types, enums) {
let text = "";
let have_enums = false;
for (let i = 0; i < enums.length; i++) {
if (!enums[i]) {
continue;
}
have_enums = true;
let its = enums[i].replace(/ +/g, " ").split("\n");
let filed = fileds[i];
for (let it of its) {
it = it.trim();
if (!it) {
continue;
}
let defs = it.split(" ");
text += `#define k_${key}_${filed}_${defs[1]} ${defs[2]} // ${defs[0].replace(/[\n\r\t]/g, " ")}\n`;
}
}
if (have_enums) {
text += "\n";
}
text += `// ${sheetName.replace(/[\n\r\t]/g, " ")}\n`;
// 生成struct定义
text += `struct res_${key} {\n`;
for (let i = 0; i < fileds.length; i++) {
if (!fileds[i]) {
continue;
}
let type = types[i].replace(/ /g, "").trim();
let reg = /(.+)(\[\d+\])/g;
let rst = reg.exec(type);
let suffix = "";
if (rst) {
type = rst[1];
suffix = rst[2];
}
function append(tab, type, name, suffix, commont) {
if (type == "int" || type == "enum" || type == "int64" || type == "time" || type == "bool" || type == "date") {
if (type == "enum") {
type = "int";
}
if (type == "time") {
type = "int64";
}
if (type == "date") {
type = "int64";
}
if (type == "bool") {
type = "char";
}
text += tab + `${type} ${name}${suffix};${commont}`;
if (suffix) {
text += tab + `int ${name}_count_;\n`;
}
return;
}
if (type.startsWith("ref>")) {
if (commont.trim().length != 0) {
commont = commont.trim();
commont += " ";
commont += type;
} else {
commont = " //";
commont += type;
}
text += tab + `int ${name}${suffix};${commont}\n`;
if (suffix) {
text += tab + `int ${name}_count_;\n`;
}
return;
}
if (type == "float" || type == "double") {
text += tab + `${type} ${name}${suffix};${commont}`;
if (suffix) {
text += tab + `int ${name}_count_;\n`;
}
return;
}
if (type == "string" || type == "path" || type == "text" || type == "big_text" || type == "huge_text") {
text += tab + `char ${name}[${strSize[type]}];${commont}`;
return;
}
reg = /\{([a-z_0-9]+:[^,]+,?)+\}/g;
if (reg.test(type)) {
text += tab + `struct {\n`;
reg = /([a-z_0-9]+):([^,\}]+),?/g;
rst = reg.exec(type);
while (rst && rst[1] && rst[2]) {
let _type = rst[2];
let _reg = /(.+)(\[\d+\])/g;
let _rst = _reg.exec(_type);
let _suffix = "";
if (_rst) {
_type = _rst[1];
_suffix = _rst[2];
}
append(tab + " ", _type, rst[1], _suffix, "\n"); // 这里不需要支持数组,excel中无法正确输入数据
rst = reg.exec(type);
}
text += tab + `} ${name}${suffix};${commont}`;
if (suffix) {
text += tab + `int ${name}_count_;\n`;
}
return;
}
throw new Error(`${sheetName} 字段[${name}]类型[${type}]不合法`);
}
append(" ", type, fileds[i], suffix, ` // ${names[i].replace(/[\n\r\t]/g, " ")}\n`);
}
text += '};\n\n';
return text;
}
let allRefs = [];
function makeJson(sheetName, data, fileds, types, enums, values) {
for (let i = 0; i < fileds.length; i++) {
if (!fileds[i]) {
continue;
}
let type = types[i].replace(/ /g, "").trim();
let reg = /(.+)(\[\d+\])/g;
let rst = reg.exec(type);
let size = "";
if (rst) {
type = rst[1];
size = Number.parseInt(rst[2].substring(1, rst[2].length - 1));
}
function append(target, value, type, name, size, enums) {
if (type == "int" || type == "int64") {
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
target[name] = [];
for (const v of value) {
if (!/[\-\+0-9\. ]+/.test(v)) {
throw new Error(`[${sheetName}] 列${name}值${v}不是整数`);
}
target[name].push(Number.parseInt(v));
}
if (target[name].length > size) {
throw new Error(`[${sheetName}] 列${name}size大小不足`);
}
} else {
value = value || "0";
target[name] = Number.parseInt(value);
}
return;
}
if (type == "bool") {
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
target[name] = [];
for (const v of value) {
target[name].push(v == 'true' || v == '1' ? 1 : 0);
}
if (target[name].length > size) {
throw new Error(`[${sheetName}] 列${name}size大小不足`);
}
} else {
target[name] = value == 'true' ? 1 : 0;
}
return;
}
if (type.startsWith("ref>")) {
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
target[name] = [];
for (const v of value) {
target[name].push(v);
}
if (target[name].length > size) {
throw new Error(`[${sheetName}] 列${name}size大小不足`);
}
} else {
value = value || "";
target[name] = value;
}
allRefs.push({
sheetName,
target,
name,
ref: type.substring(4)
});
return;
}
if (type == "float" || type == "double") {
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
target[name] = [];
for (const v of value) {
if (!/[\-\+0-9\. ]+/.test(v)) {
throw new Error(`[${sheetName}] 列${name}值${v}非法`);
}
target[name].push(Number.parseFloat(v));
}
if (target[name].length > size) {
throw new Error(`[${sheetName}] 列${name}size大小不足`);
}
} else {
value = value || "0";
target[name] = Number.parseFloat(value);
}
return;
}
if (type == "time") {
let list = [];
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
} else {
value = [value + ""];
}
for (let v of value) {
if (v.endsWith("s" || v.endsWith("second"))) {
list.push(Number.parseInt(v.substring(0, v.length - 1)));
} else if (v.endsWith("m") || v.endsWith("min")) {
list.push(Number.parseInt(v.substring(0, v.length - 1)) * 60);
} else if (v.endsWith("h" || v.endsWith("hour"))) {
list.push(Number.parseInt(v.substring(0, v.length - 1)) * 60 * 60);
} else if (v.endsWith('d' || v.endsWith("day"))) {
list.push(Number.parseInt(v.substring(0, v.length - 1)) * 60 * 60 * 24);
} else {
list.push(Number.parseInt(v));
}
}
if (size) {
target[name] = list;
} else {
target[name] = list[0];
}
return;
}
if (type == "date") {
let list = [];
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
} else {
value = [value + ""];
}
for (let v of value) {
let add = 0;
if (v.startsWith("+")) {
add = 1704038400; // 2024-01-01 00:00:00 这个是shanghai时间周一,需要根据不同的区域进行调整
v = v.substring(1);
}
if (v.endsWith("s" || v.endsWith("second"))) {
list.push(add + Number.parseInt(v.substring(0, v.length - 1)));
} else if (v.endsWith("m") || v.endsWith("min")) {
list.push(add + Number.parseInt(v.substring(0, v.length - 1)) * 60);
} else if (v.endsWith("h" || v.endsWith("hour"))) {
list.push(add + Number.parseInt(v.substring(0, v.length - 1)) * 60 * 60);
} else if (v.endsWith('d' || v.endsWith("day"))) {
list.push(add + Number.parseInt(v.substring(0, v.length - 1)) * 60 * 60 * 24);
} else if (/^[0-9]+$/.test(v)) {
list.push(add + Number.parseInt(v));
} else {
list.push(new Date(v).getTime() + 8 * 60 * 60 * 1000);
}
}
if (size) {
target[name] = list;
} else {
target[name] = list[0];
}
return;
}
if (type == "enum") {
let its = enums.replace(/ +/g, " ").trim().split("\n");
let list = [];
if (size) {
value = (value + "").trim().split(/,|\n|;|\|/g);
} else {
value = [value + ""];
}
for (const v of value) {
let find = false;
for (let it of its) {
it = it.trim();
let defs = it.split(" ");
if (defs[0].trim() == v.trim()) {
if (defs[2] === null) {
throw new Error(`[${sheetName}] 枚举没有对应值: ${v}`);
}
list.push(Number.parseInt(defs[2]));
find = true;
break;
}
}
if (!find) {
throw new Error(`[${sheetName}] 找不到枚举: ${v}`);
}
}
if (size) {
target[name] = list;
} else {
target[name] = list[0];
}
return;
}
if (type == "string" || type == "path" || type == "text" || type == "big_text" || type == "huge_text") {
value = value || "";
target[name] = value.trim();
if (Buffer.from(target[name]).byteLength > strSize[type]) {
throw new Error(`[${sheetName}] 文本[${name}]大小超出限制[${strSize[type]}]`);
}
return;
}
reg = /\{([a-z_0-9]+:[^,]+,?)+\}/g;
if (reg.test(type)) {
if (size) {
value = (value + "").trim().split(/\n|;|\|/g);
} else {
value = [value + ""];
}
let list = [];
for (const v of value) {
let vs = v.trim().split(",");
let tmp = {};
reg = /([a-z_0-9]+):([^,\}]+),?/g;
rst = reg.exec(type);
let i = 0;
while (rst && rst[1] && rst[2]) {
let _type = rst[2];
let _reg = /(.+)(\[\d+\])/g;
let _rst = _reg.exec(_type);
let _size = "";
if (_rst) {
_type = _rst[1];
_size = Number.parseInt(_rst[2].substring(1, _rst[2].length - 1));
}
append(tmp, vs[i++], _type, rst[1], _size, enums);
rst = reg.exec(type);
}
list.push(tmp);
}
if (size) {
target[name] = list;
} else {
target[name] = list[0];
}
return;
}
throw new Error(`[${sheetName}] 未知类型: ${type}`);
}
if (values[i] !== undefined && values[i] !== null && values[i] !== "") {
append(data, values[i], type, fileds[i], size, enums[i]);
}
}
}
function makeConfig(sheetFiles) {
const fs = require("fs");
let codeSource = '#pragma once\n\n';
codeSource += '#ifndef int64\n';
codeSource += '#define int64 long long\n';
codeSource += '#endif\n\n';
let jsonDatas = {};
let sheets = {};
for (const one of sheetFiles) {
sheets[one.key] = {
name: one.sheetName,
size: one.size,
};
let sheetName = one.sheetName;
let key = one.key;
let datas = one.datas;
let results = [];
let names = datas.splice(0, 1)[0];
let fileds = datas.splice(0, 1)[0];
let types = datas.splice(0, 1)[0];
let enums = datas.splice(0, 1)[0] || [];
let define_at = undefined;
let ref_at = undefined;
if (fileds.findIndex(v => v.trim() == "id") >= 0) {
sheets[one.key].id_sort = true;
}
if (!types) {
throw new Error(`[${sheetName}] 表格前4行内容配置不足`);
}
for (let k = 0; k < names.length; k++) {
if (names[k] == '定义') {
define_at = k;
}
if (names[k] == '引用') {
ref_at = k;
}
}
let mapData = {};
for (const it of datas) {
if (!it[0]) {
continue;
}
let item = {};
if (ref_at && it[ref_at] !== undefined && it[ref_at] !== null && it[ref_at] !== "") {
item = JSON.parse(JSON.stringify(mapData[Number.parseInt(it[ref_at])])); // 引用数据
}
makeJson(sheetName, item, fileds, types, enums, it);
mapData[item.id] = item;
if (define_at && it[define_at]) {
codeSource += `#define k_${key}_${it[define_at]} ${item.id} // ${item.name ? item.name : ''} \n`;
}
results.push(item);
}
codeSource += makeStruct(sheetName, key, names, fileds, types, enums);
jsonDatas[key] = results;
}
// 更新数据
let namesRef = {};
let idsRef = {};
for (const sheetName in jsonDatas) {
namesRef[sheetName] = {};
idsRef[sheetName] = {};
for (const one of jsonDatas[sheetName]) {
if (one.name) {
if (namesRef[sheetName][one.name]) {
throw Error(`表${sheetName}中名字重复: ${one.name}`);
}
namesRef[sheetName][one.name] = one.id;
}
idsRef[sheetName][one.id] = one.id;
}
}
function findRef(val, ref, sheetName) {
val = val.trim();
let idRef = idsRef[ref];
let nameRef = namesRef[ref];
if (/^[0-9]+$/.test(val.trim())) {
if (idRef[val] == undefined) {
throw new Error(`[${sheetName}] 找不到引用的列ID: ${val}`);
}
return idRef[val];
} else {
if (nameRef[val] == undefined) {
throw new Error(`[${sheetName}] 找不到引用的列名称: ${val}`);
}
return nameRef[val];
}
}
for (const one of allRefs) {
let val = one.target[one.name];
if (typeof (val) == "string") {
one.target[one.name] = findRef(val, one.ref, one.sheetName);
} else {
let list = [];
for (const v of val) {
list.push(findRef(v, one.ref, one.sheetName));
}
one.target[one.name] = list;
}
}
// 排序
for (const sheetName in jsonDatas) {
let sort = {};
if (jsonDatas[sheetName].length > 0) {
let sortables = [];
if (sheets[sheetName].id_sort) {
sortables = ['id'];
} else {
let keys = Object.keys(jsonDatas[sheetName][0]);
for (const key of keys) {
if (typeof (jsonDatas[sheetName][0][key]) == "number") {
sortables.push(key);
}
}
}
jsonDatas[sheetName].sort((a, b) => {
for (const key of sortables) {
if (a[key] != b[key]) {
sort[key] = true;
return a[key] - b[key];
}
}
throw new Error(`[${sheetName}] 无法排序: \n${JSON.stringify(a)} \n${JSON.stringify(b)}`);
});
}
}
codeSource += "#define k_res_config_uuid_len 33\n";
codeSource += "struct res_config {\n";
for (const key in sheets) {
codeSource += ` struct res_${key} ${key}[${sheets[key].size}];\n`;
codeSource += ` int ${key}_count_;\n`;
}
codeSource += " char uuid[k_res_config_uuid_len];\n";
codeSource += "};\n\n";
let content = JSON.stringify(jsonDatas, null, 4)
jsonDatas.uuid = md5compute(content);
// console.log(JSON.stringify(jsonDatas, null, 4));
// console.log(codeSource);
fileUpdate("config.json", JSON.stringify(jsonDatas, null, 4));
fileUpdate("config.h", codeSource);
console.log("success!");
}
module.exports = {
makeConfig
};
-> 一个.h
文件导出效果参考:
#pragma once
#ifndef int64
#define int64 long long
#endif
#define k_tips_login_password_error 1001 //
#define k_tips_login_failed 1002 //
#define k_tips_login_server_closed 1003 //
#define k_tips_login_server_full 1004 //
#define k_tips_server_maintenance 1005 //
#define k_tips_server_will_maintenance 1006 //
#define k_tips_item_get 2001 //
#define k_tips_item_not_enough 2002 //
#define k_tips_item_use 2003 //
#define k_tips_item_error 2004 //
#define k_tips_item_full_to_email 2005 //
#define k_tips_already_cliamed 3001 //
#define k_tips_cannot_use_self_code 3002 //
#define k_tips_mail_get_one 4001 //
#define k_tips_type_notification 1 // 通知
#define k_tips_type_system 2 // 系统
#define k_tips_type_alter 3 // 弹窗
// Tips
struct res_tips {
int id; // ID
int type[2]; // 提示类型
int type_count_;
int time; // 通知显示时间
char template[128]; // 模版内容
};
#define k_global_params_draw_card 1 //
#define k_global_params_init_give 2 //
#define k_global_params_invited_gift 3 //
#define k_global_params_invite_reward 4 //
#define k_global_params_daily_reward 5 //
#define k_global_params_stage_reward 6 //
#define k_global_params_ad_draw_limit 7 //
#define k_global_params_invite_multi_reward 8 //
#define k_global_params_sweep_cost 9 //
// 全局参数
struct res_global_params {
int id; // ID
int integer[5]; // 整数参数
int integer_count_;
int64 time[5]; // 时间参数
int time_count_;
};
#define k_stage_max 50 // 关卡50
// 关卡
struct res_stage {
int id; // 关卡ID
char name[128]; // 名称
struct {
int min;
int max;
} reward; // 奖励金币
struct {
int attr; //ref>attr
float min;
float max;
int weight;
} buff_select[6]; // buff选择
int buff_select_count_;
char monster_name[128]; // 怪物名称
struct {
int attr; //ref>attr
int val;
} monster_attrs[10]; // 怪物属性
int monster_attrs_count_;
};
#define k_skill_define_damage 1 // 伤害
#define k_skill_define_lock_health 2 // 锁血
#define k_skill_define_heal 3 // 回血
#define k_skill_define_add_shield 4 // 加盾
#define k_skill_define_silence 5 // 沉默
#define k_skill_define_silence_resist 6 // 沉默抵抗
#define k_skill_define_block 7 // 格挡
#define k_skill_define_continuous_heal 8 // 持续回血
#define k_skill_define_weakness 9 // 无力
#define k_skill_define_debuff 10 // 弱化
#define k_skill_define_lifesteal 11 // 吸血
#define k_skill_define_copy 12 // 复制
#define k_skill_define_transform 13 // 变换
#define k_skill_define_card_enhance 14 // 卡牌变强
#define k_skill_define_cooldown 15 // 冷却
#define k_skill_define_increase_action 16 // 增加行动力
#define k_skill_define_purify 17 // 净化
#define k_skill_define_freeze_type 18 // 冻结类型
#define k_skill_define_freeze_multiple 19 // 冻结多张
#define k_skill_define_continuous_damage 20 // 持续掉血
#define k_skill_define_continuous_damage_buff 21 // 持续增伤
#define k_skill_define_continuous_damage_reduction 22 // 持续减伤
#define k_skill_define_hand_type_damage_buff 23 // 手持类型增加伤害
#define k_skill_define_hand_card_damage_buff 24 // 手持卡牌增加伤害
#define k_skill_define_discard_pile_type_damage_buff 25 // 弃卡堆类型增加伤害
#define k_skill_define_discard_pile_card_damage_buff 26 // 弃卡堆牌增加伤害
#define k_skill_define_draw_card 27 // 抽卡
#define k_skill_define_attack_draw 28 // 攻击抽卡
#define k_skill_define_health_cost_draw 29 // 扣血抽卡
#define k_skill_define_discard_damage 30 // 弃卡伤害
#define k_skill_define_discard_heal 31 // 弃卡恢复
#define k_skill_define_discard 32 // 弃卡
#define k_skill_define_discard_draw 33 // 弃卡抽取
#define k_skill_define_discard_increase_action 34 // 弃卡增加行动
#define k_skill_define_no_effect 35 // 无效
#define k_skill_define_no_effect_target_card 36 // 无效敌方手牌
#define k_skill_define_move_from_target_card 37 // 移敌方牌
#define k_skill_define_disappare_target_card 38 // 消敌方牌
#define k_skill_define_reflect_damage 39 // 反伤
#define k_skill_define_exchange_health 40 // 换血
#define k_skill_define_max 44 //
#define k_skill_define_type_damage 1 // 伤害
#define k_skill_define_type_damage_increase 2 // 增伤
#define k_skill_define_type_shield 3 // 护盾
#define k_skill_define_type_recovery 4 // 恢复
#define k_skill_define_type_effect 5 // 效果
#define k_skill_define_type_discard 6 // 弃卡
#define k_skill_define_type_draw_card 7 // 抽卡
#define k_skill_define_type_no_effect 8 // 无效
#define k_skill_define_type_max 9 // max
#define k_skill_define_target_self 1 // 自己
#define k_skill_define_target_enemy 2 // 敌方
// 卡牌技能定义
struct res_skill_define {
int id; // ID
char name[128]; // 名称
char desc[1024]; // 描述
int type; // 类型
int target; // 使用目标
char negative_effect; // 负面效果
};
// 卡牌表
struct res_card {
int id; // ID
char name[128]; // 名称
int skill;// 技能ID ref>skill_define
int skill_params[4]; // 技能参数
int skill_params_count_;
int discard; // 弃卡回合
int action; // 消耗行动
struct {
int stage;
int weight;
} weight_add[10]; // 过关增加权重(可以是负数)
int weight_add_count_;
int bottom_line; // 多少抽保底
int send; // 开局赠送
};
#define k_quality_blue 1 // 蓝色
#define k_quality_orange 2 // 橙色
#define k_quality_red 3 // 红色
// 品质
struct res_quality {
int id; // ID
char name[128]; // 名称
};
// 奖励
struct res_reward {
int id; // ID
char name[128]; // 名称
int items[10];// 随机物品列表 ref>item
int items_count_;
struct {
int min;
int max;
} nums[10]; // 随机物品数量(max=0表示固定取min)
int nums_count_;
int weights[10]; // 权重随机-权重
int weights_count_;
int weights_times; // 权重随机-次数
char weights_repeatable; // 权重随机-重复获得
};
#define k_attr_health 1 // 生命
#define k_attr_physical_resistance 2 // 物盾
#define k_attr_magic_resistance 3 // 魔盾
#define k_attr_critical_strike_rate 4 // 暴率
#define k_attr_critical_strike_damage 5 // 暴伤
#define k_attr_physical_damage 7 // 物伤
#define k_attr_magic_damage 8 // 法伤
#define k_attr_max 9 //
// 属性
struct res_attr {
int id; // ID
char name[128]; // 名称
char show[128]; // 显示文本
char percent; // 是百分比
char icon[256]; // ICON
int player_base; // 玩家基础值
};
// 条件
struct res_condition {
int id; // ID(条件类型*100000+x)
char use[128]; // 通途
char name[128]; // 名称
int condition_type;// 条件类型 ref>condition_type
int condtion_params[5]; // 条件参数
int condtion_params_count_;
int condition_times; // 条件次数
};
#define k_condition_type_condition_type_type_cumulation 1 // 累积
#define k_condition_type_condition_type_type_get 2 // 获取
// 条件类型
struct res_condition_type {
int id; // ID
char name[128]; // 名称
int condition_type_type; // 类型
char desc[128]; // 描述
};
#define k_help_where_battle_failed 1 // 战败
#define k_help_where_home 2 // 主界面
// 游戏提示表
struct res_help {
int id; // ID
int where; // 出现条件
char content[1024]; // 内容
};
#define k_item_gold 1 // 金币
#define k_item_type_resource 1 // 资源
#define k_item_type_func_item 2 // 功能
#define k_item_func_reward 1 // 奖励
#define k_item_func_equip 2 // 装备宝箱
#define k_item_level_start1 1 // 蓝色
#define k_item_level_start2 2 // 橙色
#define k_item_level_start3 3 // 红色
// 道具
struct res_item {
int id; // ID
char name[128]; // 名称
int type; // 类型
int func; // 使用功能
int func_params[5]; // 功能参数
int func_params_count_;
char auto_open; // 自动打开
int level; // 级别
int bag; // 背包显示
char icon[256]; // 图标
int day_limit; // 每日获取上限
int limit; // 拥有上限
char desc[1024]; // 描述
int get_channel; // 获取渠道
int64 expire; // 时间限制
};
#define k_mail_level_achive 1 //
#define k_mail_join_alliance 2 //
#define k_mail_item_full 3 //
#define k_mail_type_system 1 // 系统
#define k_mail_type_battle 2 // 战报
#define k_mail_type_notice 3 // 公告
#define k_mail_type_alliance 4 // 公会
// 邮件
struct res_mail {
int id; // ID
int type; // 类型
char title[128]; // 标题
char simple[128]; // 摘要/发送着
char template[1024]; // 模板
int64 expire_time; // 过期时间
};
#define k_res_config_uuid_len 33
struct res_config {
struct res_tips tips[1000];
int tips_count_;
struct res_global_params global_params[1000];
int global_params_count_;
struct res_stage stage[100];
int stage_count_;
struct res_skill_define skill_define[100];
int skill_define_count_;
struct res_card card[1000];
int card_count_;
struct res_quality quality[10];
int quality_count_;
struct res_reward reward[1000];
int reward_count_;
struct res_attr attr[100];
int attr_count_;
struct res_condition condition[1000];
int condition_count_;
struct res_condition_type condition_type[100];
int condition_type_count_;
struct res_help help[100];
int help_count_;
struct res_item item[1000];
int item_count_;
struct res_mail mail[1000];
int mail_count_;
char uuid[k_res_config_uuid_len];
};
该文件可以配合【jsonc】 工具生成对应的json解析代码,快速实现config.json
配置文件解析