1. async function initTable() {//创建玩家数据表
    2. return await db.sql`
    3. CREATE TABLE IF NOT EXISTS "player" (
    4. "coin" INT NOT NULL,
    5. "exp" INT NOT NULL,
    6. "item" TEXT NOT NULL,
    7. "bbzdhl" INT NOT NULL,
    8. "weapon" TEXT,
    9. "userKey" CHAR(16) PRIMARY KEY UNIQUE NOT NULL
    10. )
    11. `;
    12. };
    13. async function saveUser(user) {//玩家存档
    14. await db.sql`
    15. INSERT INTO "player" (
    16. "exp",
    17. "coin",
    18. "item",
    19. "bbzdhl",
    20. "weapon",
    21. "userKey"
    22. ) VALUES (
    23. ${user.exp},
    24. ${user.coin},
    25. ${JSON.stringify(user.item)},
    26. ${user.bbzdhl},
    27. ${user.weapon},
    28. ${user.player.userKey}
    29. )
    30. ON CONFLICT("userKey")
    31. DO UPDATE SET
    32. "exp"=excluded."exp",
    33. "coin"=excluded."coin",
    34. "item"=excluded."item",
    35. "bbzdhl"=excluded."bbzdhl",
    36. "weapon"=excluded."weapon"
    37. `;
    38. };
    39. async function loadUser(user) {//玩家读档
    40. const [data] = await db.sql`SELECT * FROM "player" WHERE "userKey"=${user.player.userKey} LIMIT 1`
    41. if (data) {//如果这个玩家已经有存档
    42. user.exp = data.exp
    43. user.coin = data.coin
    44. user.item = JSON.parse(data.item)
    45. user.bbzdhl = data.bbzdhl
    46. user.weapon = data.weapon
    47. } else {//玩家无存档
    48. saveUser(user)
    49. }
    50. };
    51. async function poll(fn) {//无限轮询执行sql语句直到成功
    52. while (true) {
    53. try {
    54. return await fn()//一旦成功执行, 停止无限轮询, 并返回查询结果
    55. } catch (e) {
    56. const m = e.message//需要简略显示的异常消息
    57. //sql偶尔会执行超时, 但如果反复显示timeout 15秒以上一直不停, 大概是数据库出了故障只能等官方修复
    58. if (m.includes('timeout')) {
    59. world.say(m);
    60. }
    61. else {
    62. world.say(e.stack);//如果sql执行出错, 广播错误消息, 用来排查错误原因
    63. }
    64. };
    65. await sleep(2000); //每2秒重试一次
    66. };
    67. };
    68. async function showPlayerData() {
    69. var t = []
    70. for (var row of await db.sql`SELECT * FROM "player"`) {
    71. t.push(JSON.stringify(row))
    72. }
    73. console.log(t.join('\n'))
    74. }
    75. ; (async() => {//当地图开始运行
    76. await poll(()=>{db.sql`SELECT 1`});
    77. await poll(initTable);
    78. console.log("数据库启动成功")
    79. showPlayerData()
    80. })();
    81. var wp_data = {//玩家背包数据处理
    82. "物品": {
    83. context: "物品说明",
    84. fun(entity) {
    85. //物品使用后触发的函数
    86. }
    87. }, "武器": {
    88. context: "武器说明",
    89. fun(entity) {//物品装备后触发的函数
    90. var handWears = entity.player.wearables(Box3BodyPart.RIGHT_HAND)///查找装备在右手上的东西
    91. entity.player.removeWearable(handWears[0])//删除装备在右手上的东西
    92. entity.player.addWearable({//装备该物品
    93. bodyPart: Box3BodyPart.RIGHT_HAND,//装备在右手上
    94. mesh: 'mesh/武器.vb',//模型mesh
    95. orientation: new Box3Quaternion(12, 12, -12, 12).rotateY(Math.PI / 2),//其他属性请自行修改
    96. scale: new Box3Vector3(0.5, 0.7, 0.5),
    97. offset: new Box3Vector3(0, 0.1, 0.7),
    98. })
    99. }
    100. },
    101. };
    102. var isWeapons = ['武器'];
    103. world.onPlayerJoin(async ({ entity }) => {
    104. //初始化数据
    105. entity.exp = 0;//经验
    106. entity.coin = 0;//金币
    107. entity.item = [];//背包
    108. entity.weapon = '空气';//已装备的武器
    109. entity.bbzdhl = 25;//背包最大含量
    110. await loadUser(entity);//读取数据
    111. entity.player.onRelease(async ({ button }) => {
    112. if (button == 'action1') {
    113. async function cd1() {//定义函数,做出哈哈农场那样的菜单
    114. entity.bbts = '';//我的背包 的提示语
    115. const sj = await entity.player.dialog({
    116. type: Box3DialogType.SELECT,
    117. title: "我的信息",
    118. content: `昵称:${entity.player.name}
    119. BoxID${entity.player.boxId}
    120. 等级:Lv${exp_judge(entity.exp)[0] + `
    121. 距离升级还有` + exp_judge(entity.exp)[1] + "经验"}
    122. 总经验:${entity.exp}
    123. 血量:${entity.hp + '/' + entity.maxHp}
    124. 金币:${entity.coin}
    125. `,//对话框内容
    126. options: ['我的背包 (' + entity.item.length + '/' + entity.bbzdhl + ')', '保存数据'],//按钮
    127. });
    128. if (sj) {//判断是否点击右上角的×
    129. if (sj.index == 0) {
    130. async function cd2() {//定义函数,做出哈哈农场那样的菜单
    131. if (entity.item.length != 0) {//判断背包是否不是空的
    132. const wp = await entity.player.dialog({//查看背包里的东西
    133. type: Box3DialogType.SELECT,
    134. title: '我的背包 (' + entity.item.length + '/' + entity.bbzdhl + ')',
    135. content: entity.bbts,
    136. options: entity_item_slice(entity.item, entity.weapon),
    137. });
    138. if (wp) {//判断是否点击右上角的×
    139. async function cd3() {//定义函数,做出哈哈农场那样的菜单
    140. var options, isWeapon;//定义变量
    141. if (isWeapons.includes(entity_item_read(wp.value))) {
    142. options = [entity.weapon != entity_item_read(wp.value) ? '装备' : "卸下", "丢弃"];
    143. isWeapon = '(可装备物品)';
    144. } else {
    145. options = ['使用', "丢弃"];
    146. isWeapon = '';
    147. }
    148. const wpaz = await entity.player.dialog({
    149. type: Box3DialogType.SELECT,
    150. title: entity_item_read(wp.value) + isWeapon,
    151. content: wp_data[entity_item_read(wp.value)].context,
    152. options,
    153. });
    154. if (wpaz) {//判断是否点击右上角的×
    155. if (wpaz.value == '使用') {//如果点击的是使用按钮
    156. entity.bbts = "使用了“" + entity_item_read(wp.value) + "”";//设置提示语
    157. wp_data[entity_item_read(wp.value)].fun(entity);//运行使用函数
    158. } else if (wpaz.value == '丢弃') {//如果点击的是丢弃按钮
    159. const diuqi = await entity.player.dialog({//询问是否丢弃
    160. title: '丢弃物品',
    161. content: '确认丢弃' + entity_item_read(wp.value) + '?\n提示:丢弃后不可恢复,请谨慎选择',
    162. options: ['确认', '取消']
    163. })
    164. if (diuqi) {//判断是否点击右上角的×
    165. if (diuqi.index == 0) {//如果点击的是确认按钮
    166. entity.bbts = "丢弃了“" + entity_item_read(wp.value) + "”";//设置提示语
    167. entity.item.splice(entity.item.indexOf(entity_item_read(wp.value)), 1);//丢弃物品
    168. }
    169. }
    170. } else if (wpaz.value == '卸下') {//如果点击的是卸下按钮
    171. entity.bbts = "卸下了“" + entity_item_read(wp.value) + "”";//设置提示语
    172. var handWears = entity.player.wearables(Box3BodyPart.RIGHT_HAND);///查找装备在右手上的东西
    173. entity.player.removeWearable(handWears[0]);//删除装备在右手上的东西
    174. entity.weapon = '空气';//设置已装备物品为空气
    175. } else if (wpaz.value == '装备') {//如果点击的是装备按钮
    176. entity.bbts = "装备了“" + entity_item_read(wp.value) + "”";//设置提示语
    177. wp_data[entity_item_read(wp.value)].fun(entity);//运行使用函数
    178. entity.weapon = entity_item_read(wp.value);//设置已装备物品为这个物品的名字
    179. }
    180. cd2();//返回背包界面
    181. } else {
    182. cd2();//返回背包界面
    183. }
    184. } cd3();//运行
    185. } else { cd1();/*返回主菜单*/ }
    186. } else {//背包是空的
    187. const wp = await entity.player.dialog({//弹出提示
    188. type: Box3DialogType.SELECT,
    189. title: "我的背包",
    190. content: `你背包里还没有物品呢`,
    191. });
    192. cd1();//返回主菜单
    193. }
    194. }; cd2();//运行
    195. } else if (sj.index == 1) {
    196. await saveUser(entity)//保存数据
    197. await entity.player.dialog({//弹出提示
    198. type: Box3DialogType.SELECT,
    199. title: "保存数据",
    200. content: `保存成功!`,
    201. });
    202. cd1();//返回主菜单
    203. }
    204. }
    205. }; cd1();//运行
    206. }
    207. })
    208. while (true) {//为了防止服务器卡顿,我们不采用world.onTick来实现自动保存
    209. await saveUser(entity);
    210. await sleep(1000);//每一秒保存一次
    211. if (entity.destroyed) { return };//如果玩家离开了地图,就取消这个玩家自动保存循环
    212. };
    213. });
    214. function exp_judge(exp) {//等级判断函数
    215. var levels = -1;
    216. var mathss = 0;
    217. var leiji = 0;
    218. var leijijy = 0;
    219. do {
    220. if (leijijy > mathss) {
    221. leiji += mathss;
    222. }
    223. levels += 1;
    224. var mathss = ((Math.floor(levels / 10) + 1) * 30 * (levels + 1)) + ((Math.floor(levels / 10) + 1) * 70);
    225. var leijijy = exp - leiji;
    226. }
    227. while (leijijy > mathss)
    228. var dj = String(levels + 1);
    229. var sy = dj == '1' ? String(100 - leijijy) : String(mathss - leijijy);
    230. return [dj, sy];
    231. }
    232. function entity_item_slice(arr, weapon) {//背包物品整理函数
    233. if (!arr) { return }
    234. var newarr = [];
    235. var copyarr = arr.slice();
    236. for (var i = 0; i < copyarr.length; i++) {
    237. var temp = copyarr[i];
    238. var count = 0;
    239. for (var j = 0; j < copyarr.length; j++) {
    240. if (copyarr[j] == temp) {
    241. count++;
    242. copyarr[j] = -1;
    243. }
    244. }
    245. if (temp != -1) {
    246. newarr.push(temp + '×' + count);
    247. }
    248. }
    249. for (var i = 0; i < newarr.length; i++) {
    250. if (entity_item_read(newarr[i]) == weapon) {
    251. newarr[i] = '[已装备] ' + newarr[i];
    252. }
    253. }
    254. return newarr;
    255. }
    256. function entity_item_read(data = "") {//返回出物品名字函数
    257. var str = data
    258. if (data.substring(0, 6) == "[已装备] ") {
    259. str = data.substring(6)
    260. }
    261. var string = ''
    262. for (var i = 0; i < str.length; i++) {
    263. var test = str[i]
    264. if (test == '×') {
    265. break
    266. } else {
    267. string = string + test
    268. }
    269. }
    270. return string
    271. }