diff --git a/_server/blockly/MotaAction.g4 b/_server/blockly/MotaAction.g4 index 29c55899..039a2f99 100644 --- a/_server/blockly/MotaAction.g4 +++ b/_server/blockly/MotaAction.g4 @@ -215,6 +215,8 @@ action | setWeather_s | move_s | moveHero_s + | jump_s + | jumpHero_s | playBgm_s | pauseBgm_s | resumeBgm_s @@ -797,6 +799,45 @@ var code = '{"type": "moveHero"'+Int_0+', "steps": '+JSON.stringify(StepString_0 return code; */; +jump_s + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? BGNL? '动画时间' Int? '消失时无动画时间' Bool Newline + + +/* jump_s +tooltip : jump: 让某个NPC/怪物跳跃 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=jump%EF%BC%9A%E8%AE%A9%E6%9F%90%E4%B8%AANPC%2F%E6%80%AA%E7%89%A9%E8%B7%B3%E8%B7%83 +default : ["","","","",500,null] +colour : this.eventColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr += ', "from": ['+PosString_0+','+PosString_1+']'; +} +if (PosString_2 && PosString_3) { + floorstr += ', "to": ['+PosString_2+','+PosString_3+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +var code = '{"type": "jump"'+floorstr+''+Int_0+', "immediateHide": '+Bool_0+'},\n'; +return code; +*/; + +jumpHero_s + : '跳跃勇士' 'x' PosString? ',' 'y' PosString? '动画时间' Int? Newline + + +/* jumpHero_s +tooltip : jumpHero: 跳跃勇士 +helpUrl : https://ckcz123.github.io/mota-js/#/event?id=jumpHero%EF%BC%9A%E8%B7%B3%E8%B7%83%E5%8B%87%E5%A3%AB +default : ["","",500] +colour : this.dataColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +var code = '{"type": "jumpHero"'+floorstr+Int_0+'},\n'; +return code; +*/; + playBgm_s : '播放背景音乐' EvalString Newline @@ -1439,6 +1480,17 @@ ActionParser.prototype.parseAction = function() { this.next = MotaActionBlocks['moveHero_s'].xmlText([ data.time||0,this.StepString(data.steps),this.next]); break; + case "jump": // 跳跃事件 + data.from=data.from||[]; + data.to=data.to||[]; + this.next = MotaActionBlocks['jump_s'].xmlText([ + data.from[0]||'',data.from[1]||'',data.to[0]||'',data.to[1]||'',data.time||0,data.immediateHide,this.next]); + break; + case "jumpHero": // 跳跃勇士 + data.loc=data.loc||[] + this.next = MotaActionBlocks['jumpHero_s'].xmlText([ + data.loc[0]||'',data.loc[1]||'',data.time||0,this.next]); + break; case "changeFloor": // 楼层转换 this.next = MotaActionBlocks['changeFloor_s'].xmlText([ data.floorId,data.loc[0],data.loc[1],data.direction,this.time||0,this.next]); diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js index b05bb3d7..c547ebef 100644 --- a/_server/editor_blockly.js +++ b/_server/editor_blockly.js @@ -85,6 +85,7 @@ editor_blockly = function () { MotaActionBlocks['input_s'].xmlText(), MotaActionBlocks['update_s'].xmlText(), MotaActionBlocks['moveHero_s'].xmlText(), + MotaActionBlocks['jumpHero_s'].xmlText(), MotaActionBlocks['changeFloor_s'].xmlText(), MotaActionBlocks['changePos_0_s'].xmlText(), MotaActionBlocks['changePos_1_s'].xmlText(), @@ -103,6 +104,7 @@ editor_blockly = function () { MotaActionBlocks['hide_s'].xmlText(), MotaActionBlocks['trigger_s'].xmlText(), MotaActionBlocks['move_s'].xmlText(), + MotaActionBlocks['jump_s'].xmlText(), MotaActionBlocks['disableShop_s'].xmlText(), '', MotaActionBlocks['sleep_s'].xmlText(), diff --git a/docs/api.md b/docs/api.md index 77c03049..e2a9fd86 100644 --- a/docs/api.md +++ b/docs/api.md @@ -138,12 +138,12 @@ localStorage core.js实际上是所有API的入口(路由),核心API的实现在其他几个文件中,core.js主要进行转发操作。 -core.nextX() -获得勇士面向的下一个位置的x坐标 +core.nextX(n) +获得勇士面向的第n个位置的x坐标,n可以省略默认为1(即正前方) -core.nextY() -获得勇士面向的下一个位置的y坐标 +core.nextY(n) +获得勇士面向的第n个位置的y坐标,n可以省略默认为1(即正前方) core.openDoor(id, x, y, needKey, callback) [异步] diff --git a/docs/event.md b/docs/event.md index a1ccef79..814a2ff7 100644 --- a/docs/event.md +++ b/docs/event.md @@ -833,6 +833,48 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate 不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。 +### jump:让某个NPC/怪物跳跃 + +如果我们需要移动某个NPC或怪物,可以使用`{"type": "jump"}`。 + +下面是该事件常见的写法: + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "jump", "from": [3,6], "to": [2,1], "time": 750, "immediateHide": true}, +] +``` + +from为需要跳跃的事件位置。可以省略,如果省略则移动本事件。 + +to为要跳跃到的坐标。可以省略,如果省略则跳跃到当前坐标。 + +time选项必须指定,为全程跳跃所需要用到的时间。 + +immediateHide为一个可选项,同上代表该跳跃完毕后是否立刻消失。如果该项指定了并为true,则跳跃完毕后直接消失,否则以动画效果消失。 + +值得注意的是,当调用jump事件时,实际上是使事件脱离了原始地点。 + +为了避免冲突,同move事件一样规定:jump事件会自动调用该点的hide事件。 + +如果是本地跳跃,需要在跳跃完毕后再启用事件。 + +### jumpHero:跳跃勇士 + +如果我们需要跳跃勇士,可以使用`{"type": "jumpHero"}`。 + +下面是该事件常见的写法: + +``` js +"x,y": [ // 实际执行的事件列表 + {"type": "jump", "loc": [3,6], "time": 750}, +] +``` + +loc为目标坐标,可以忽略表示原地跳跃(请注意是原地跳跃而不是跳跃到当前事件点)。 + +time选项为该跳跃所需要用到的时间。 + ### playBgm:播放背景音乐 使用playBgm可以播放一个背景音乐。 diff --git a/libs/control.js b/libs/control.js index 4d71c13d..33bcab03 100644 --- a/libs/control.js +++ b/libs/control.js @@ -290,9 +290,11 @@ control.prototype.resetStatus = function(hero, hard, floorId, route, maps, value if (core.isset(values)) core.values = core.clone(values); + else core.values = core.clone(core.data.values); if (core.isset(flags)) core.flags = core.clone(flags); + else core.flags = core.clone(core.data.flags); core.events.initGame(); @@ -850,6 +852,66 @@ control.prototype.eventMoveHero = function(steps, time, callback) { }, time / 8 / core.status.replay.speed) } +////// 勇士跳跃事件 ////// +control.prototype.jumpHero = function (ex, ey, time, callback) { + var sx=core.status.hero.loc.x, sy=core.status.hero.loc.y; + if (!core.isset(ex)) ex=sx; + if (!core.isset(ey)) ey=sy; + + time = time || 500; + core.clearMap('ui', 0, 0, 416, 416); + core.setAlpha('ui', 1.0); + core.status.replay.animate=true; + + core.playSound('jump.mp3'); + + var dx = ex-sx, dy=ey-sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); + var jump_peak = 6 + distance, jump_count = jump_peak * 2; + var currx = sx, curry = sy; + + var heroIcon = core.material.icons.hero[core.getHeroLoc('direction')]; + var status = 'stop'; + var height = core.material.icons.hero.height; + + var drawX = function() { + return currx * 32; + } + var drawY = function() { + var ret = curry * 32; + if(jump_count >= jump_peak){ + var n = jump_count - jump_peak; + }else{ + var n = jump_peak - jump_count; + } + return ret - (jump_peak * jump_peak - n * n) / 2; + } + var updateJump = function() { + jump_count--; + currx = (currx * jump_count + ex) / (jump_count + 1.0); + curry = (curry * jump_count + ey) / (jump_count + 1.0); + } + + var animate=window.setInterval(function() { + + if (jump_count>0) { + core.clearMap('hero', drawX(), drawY()-height+32, 32, height); + updateJump(); + core.canvas.hero.drawImage(core.material.images.hero, heroIcon[status] * 32, heroIcon.loc * height, 32, height, drawX(), drawY() + 32-height, 32, height); } + else { + clearInterval(animate); + core.setHeroLoc('x', ex); + core.setHeroLoc('y', ey); + core.drawHero(); + core.status.replay.animate=false; + if (core.isset(callback)) callback(); + } + + }, time / 16 / core.status.replay.speed); + + + +} + ////// 每移动一格后执行的事件 ////// control.prototype.moveOneStep = function() { core.status.hero.steps++; @@ -928,25 +990,25 @@ control.prototype.getHeroLoc = function (itemName) { } ////// 获得勇士面对位置的x坐标 ////// -control.prototype.nextX = function() { +control.prototype.nextX = function(n) { var scan = { 'up': {'x': 0, 'y': -1}, 'left': {'x': -1, 'y': 0}, 'down': {'x': 0, 'y': 1}, 'right': {'x': 1, 'y': 0} }; - return core.getHeroLoc('x')+scan[core.getHeroLoc('direction')].x; + return core.getHeroLoc('x')+scan[core.getHeroLoc('direction')].x*(n||1); } ////// 获得勇士面对位置的y坐标 ////// -control.prototype.nextY = function () { +control.prototype.nextY = function (n) { var scan = { 'up': {'x': 0, 'y': -1}, 'left': {'x': -1, 'y': 0}, 'down': {'x': 0, 'y': 1}, 'right': {'x': 1, 'y': 0} }; - return core.getHeroLoc('y')+scan[core.getHeroLoc('direction')].y; + return core.getHeroLoc('y')+scan[core.getHeroLoc('direction')].y*(n||1); } ////// 更新领域、夹击、阻击的伤害地图 ////// @@ -1391,8 +1453,9 @@ control.prototype.updateFg = function () { else if (damage < hero_hp * 2 / 3) color = '#FFFF00'; else if (damage < hero_hp) color = '#FF7F00'; else color = '#FF0000'; - damage = core.formatBigNumber(damage); + if (core.enemys.hasSpecial(core.material.enemys[id], 19)) + damage += "+"; } core.setFillStyle('fg', '#000000'); diff --git a/libs/core.js b/libs/core.js index c080e875..ed43bd8a 100644 --- a/libs/core.js +++ b/libs/core.js @@ -463,6 +463,11 @@ core.prototype.eventMoveHero = function(steps, time, callback) { core.control.eventMoveHero(steps, time, callback); } +////// 使用事件让勇士跳跃。这个函数将不会触发任何事件 ////// +core.prototype.jumpHero = function (ex,ey,time,callback) { + core.control.jumpHero(ex,ey,time,callback); +} + ////// 每移动一格后执行的事件 ////// core.prototype.moveOneStep = function() { core.control.moveOneStep(); @@ -494,13 +499,13 @@ core.prototype.getHeroLoc = function (itemName) { } ////// 获得勇士面对位置的x坐标 ////// -core.prototype.nextX = function() { - return core.control.nextX(); +core.prototype.nextX = function(n) { + return core.control.nextX(n); } ////// 获得勇士面对位置的y坐标 ////// -core.prototype.nextY = function () { - return core.control.nextY(); +core.prototype.nextY = function (n) { + return core.control.nextY(n); } /////////// 自动行走 & 行走控制 END /////////// @@ -663,6 +668,11 @@ core.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { core.maps.moveBlock(x,y,steps,time,immediateHide,callback) } +////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// +core.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { + core.maps.jumpBlock(sx,sy,ex,ey,time,immediateHide,callback); +} + ////// 显示/隐藏某个块时的动画效果 ////// core.prototype.animateBlock = function (loc,type,time,callback) { core.maps.animateBlock(loc,type,time,callback) diff --git a/libs/events.js b/libs/events.js index add5938c..06ec8646 100644 --- a/libs/events.js +++ b/libs/events.js @@ -85,12 +85,14 @@ events.prototype.startGame = function (hard) { if (core.flags.showBattleAnimateConfirm) { // 是否提供“开启战斗动画”的选择项 core.status.event.selection = core.flags.battleAnimate ? 0 : 1; core.ui.drawConfirmBox("你想开启战斗动画吗?\n之后可以在菜单栏中开启或关闭。\n(强烈建议新手开启此项)", function () { + core.data.flags.battleAnimate = true; core.flags.battleAnimate = true; core.setLocalStorage('battleAnimate', true); core.startGame(hard); core.utils.__init_seed(); core.events.setInitData(hard); }, function () { + core.data.flags.battleAnimate = false; core.flags.battleAnimate = false; core.setLocalStorage('battleAnimate', false); core.startGame(hard); @@ -432,6 +434,34 @@ events.prototype.doAction = function() { core.events.doAction(); }); break; + case "jump": // 跳跃事件 + { + var sx=x, sy=y, ex=x,ey=y; + if (core.isset(data.from)) { + sx=core.calValue(data.from[0]); + sy=core.calValue(data.from[1]); + } + if (core.isset(data.to)) { + ex=core.calValue(data.to[0]); + ey=core.calValue(data.to[1]); + } + core.jumpBlock(sx,sy,ex,ey,data.time,data.immediateHide,function() { + core.events.doAction(); + }); + break; + } + case "jumpHero": + { + var ex=core.status.hero.loc.x, ey=core.status.hero.loc.y; + if (core.isset(data.loc)) { + ex=core.calValue(data.loc[0]); + ey=core.calValue(data.loc[1]); + } + core.jumpHero(ex,ey,data.time,function() { + core.events.doAction(); + }); + break; + } case "changeFloor": // 楼层转换 { var heroLoc = {"x": core.calValue(data.loc[0]), "y": core.calValue(data.loc[1])}; @@ -820,7 +850,7 @@ events.prototype.getNextItem = function() { ////// 获得某个物品 ////// events.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) { - // core.getItemAnimate(itemId, itemNum, itemX, itemY); + itemNum=itemNum||1; core.playSound('item.mp3'); var itemCls = core.material.items[itemId].cls; core.items.getItemEffect(itemId, itemNum); diff --git a/libs/items.js b/libs/items.js index 6e0e0665..82ee9204 100644 --- a/libs/items.js +++ b/libs/items.js @@ -123,6 +123,7 @@ items.prototype.removeItem = function (itemId) { ////// 增加某个物品的个数 ////// items.prototype.addItem = function (itemId, itemNum) { + itemNum=itemNum||1; var itemData = core.material.items[itemId]; var itemCls = itemData.cls; if (itemCls == 'items') return; diff --git a/libs/maps.js b/libs/maps.js index a35ce7e7..cdfd6b36 100644 --- a/libs/maps.js +++ b/libs/maps.js @@ -558,7 +558,7 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { core.clearMap('animate', 0, 0, 416, 416); - var block = core.getBlock(x,y,core.status.floorId,false); + var block = core.getBlock(x,y); if (block==null) {// 不存在 if (core.isset(callback)) callback(); return; @@ -650,6 +650,92 @@ maps.prototype.moveBlock = function(x,y,steps,time,immediateHide,callback) { }, time / 16 / core.status.replay.speed); } +////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// +maps.prototype.jumpBlock = function(sx,sy,ex,ey,time,immediateHide,callback) { + time = time || 500; + core.status.replay.animate=true; + core.clearMap('animate', 0, 0, 416, 416); + var block = core.getBlock(sx,sy); + if (block==null) { + if (core.isset(callback)) callback(); + return; + } + + // 需要删除该块 + core.removeBlock(sx,sy); + core.clearMap('ui', 0, 0, 416, 416); + core.setAlpha('ui', 1.0); + + block=block.block; + var blockIcon = core.material.icons[block.event.cls][block.event.id]; + var blockImage = core.material.images[block.event.cls]; + var height = block.event.height || 32; + + var opacityVal = 1; + core.setOpacity('animate', opacityVal); + core.canvas.animate.drawImage(blockImage, 0, blockIcon * height, 32, height, block.x * 32, block.y * 32 +32 - height, 32, height); + + core.playSound('jump.mp3'); + + var dx = ex-sx, dy=ey-sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); + var jump_peak = 6 + distance, jump_count = jump_peak * 2; + var currx = sx, curry = sy; + + var drawX = function() { + return currx * 32; + } + + var drawY = function() { + var ret = curry * 32; + if(jump_count >= jump_peak){ + var n = jump_count - jump_peak; + }else{ + var n = jump_peak - jump_count; + } + return ret - (jump_peak * jump_peak - n * n) / 2; + } + + var updateJump = function() { + jump_count--; + currx = (currx * jump_count + ex) / (jump_count + 1.0); + curry = (curry * jump_count + ey) / (jump_count + 1.0); + } + + var animateValue = block.event.animate || 1; + var animateCurrent = 0; + var animateTime = 0; + var animate=window.setInterval(function() { + + animateTime += time / 16 / core.status.replay.speed; + if (animateTime >= core.values.animateSpeed) { + animateCurrent++; + animateTime = 0; + if (animateCurrent >= animateValue) animateCurrent = 0; + } + + if (jump_count>0) { + core.clearMap('animate', drawX(), drawY()-height+32, 32, height); + updateJump(); + core.canvas.animate.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, drawX(), drawY()-height+32, 32, height); + } + else { + if (immediateHide) opacityVal=0; + else opacityVal -= 0.06; + core.setOpacity('animate', opacityVal); + core.clearMap('animate', drawX(), drawY()-height+32, 32, height); + core.canvas.animate.drawImage(blockImage, animateCurrent * 32, blockIcon * height, 32, height, drawX(), drawY()-height+32, 32, height); + if (opacityVal<=0) { + clearInterval(animate); + core.clearMap('animate', 0, 0, 416, 416); + core.setOpacity('animate', 1); + core.status.replay.animate=false; + if (core.isset(callback)) callback(); + } + } + + }, time / 16 / core.status.replay.speed); +} + ////// 显示/隐藏某个块时的动画效果 ////// maps.prototype.animateBlock = function (loc,type,time,callback) { if (type!='hide') type='show'; diff --git a/libs/ui.js b/libs/ui.js index 5cfedd53..b5eb301d 100644 --- a/libs/ui.js +++ b/libs/ui.js @@ -1305,6 +1305,8 @@ ui.prototype.drawBook = function (index) { if (damage<=0) color = '#00FF00'; damage = core.formatBigNumber(damage); + if (core.enemys.hasSpecial(core.material.enemys[enemy.id], 19)) + damage += "+"; } core.fillText('ui', damage, damageOffset, 62 * i + 50, color, 'bold 13px Verdana'); diff --git a/project/data.js b/project/data.js index c6cab035..6a33d6e3 100644 --- a/project/data.js +++ b/project/data.js @@ -14,7 +14,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = 'bgm.mp3' ], "sounds" : [ - 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3' + 'floor.mp3', 'attack.mp3', 'door.mp3', 'item.mp3', 'zone.mp3', 'jump.mp3' ], "startBackground" : "bg.jpg", "startLogoStyle" : "color: black", diff --git a/project/icons.js b/project/icons.js index b7461654..2d7e35df 100644 --- a/project/icons.js +++ b/project/icons.js @@ -235,7 +235,8 @@ icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = 'knife': 42, 'moneyPocket': 46, 'shoes': 47, - 'hammer': 48 + 'hammer': 48, + 'jumpShoes': 49 }, 'autotile': { // 所有的Autotile列表;后面的index简单取0即可 'autotile': 0, diff --git a/project/images/items.png b/project/images/items.png index 626b63b7..cce829cc 100644 Binary files a/project/images/items.png and b/project/images/items.png differ diff --git a/project/items.js b/project/items.js index 772b13e0..7ef11789 100644 --- a/project/items.js +++ b/project/items.js @@ -227,6 +227,11 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "cls": "tools", "name": "生命魔杖", "text": "可以恢复100点生命值" + }, + "jumpShoes": { + "cls": "tools", + "name": "跳跃靴", + "text": "能跳跃到前方两格处" } }, "itemEffect": { @@ -304,7 +309,8 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "shield3": "core.plugin.useEquipment(itemId)", "shield4": "core.plugin.useEquipment(itemId)", "shield5": "core.plugin.useEquipment(itemId)", - "lifeWand": "core.insertAction([\n\t{\"type\": \"input\", \"text\": \"请输入生命魔杖使用次数:(0-${item:lifeWand})\"},\n\t{\"type\": \"if\", \"condition\": \"flag:input<=item:lifeWand\",\n\t\t\"true\": [\n\t\t\t{\"type\": \"setValue\", \"name\": \"item:lifeWand\", \"value\": \"item:lifeWand-flag:input\"},\n\t\t\t{\"type\": \"setValue\", \"name\": \"status:hp\", \"value\": \"status:hp+flag:input*100\"},\n\t\t\t\"成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。\"\n\t\t],\n\t\t\"false\": [\"输入不合法!\"]\n\t},\n]);\ncore.setItem('lifeWand', core.itemCount('lifeWand')+1);" + "lifeWand": "core.insertAction([\n\t{\"type\": \"input\", \"text\": \"请输入生命魔杖使用次数:(0-${item:lifeWand})\"},\n\t{\"type\": \"if\", \"condition\": \"flag:input<=item:lifeWand\",\n\t\t\"true\": [\n\t\t\t{\"type\": \"setValue\", \"name\": \"item:lifeWand\", \"value\": \"item:lifeWand-flag:input\"},\n\t\t\t{\"type\": \"setValue\", \"name\": \"status:hp\", \"value\": \"status:hp+flag:input*100\"},\n\t\t\t\"成功使用${flag:input}次生命魔杖,恢复${flag:input*100}点生命。\"\n\t\t],\n\t\t\"false\": [\"输入不合法!\"]\n\t},\n]);\ncore.setItem('lifeWand', core.itemCount('lifeWand')+1);", + "jumpShoes": "core.insertAction({\"type\":\"jumpHero\",\"loc\":[core.nextX(2),core.nextY(2)]});" }, "canUseItemEffect": { "book": "true", @@ -336,6 +342,7 @@ items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = "shield4": "true", "shiled5": "true", "shield5": "true", - "lifeWand": "true" + "lifeWand": "true", + "jumpShoes": "var nx=core.nextX(2),ny=core.nextY(2);nx>=0&&nx<=12&&ny>=0&&ny<=12&&core.getBlock(nx,ny)==null" } } \ No newline at end of file diff --git a/project/maps.js b/project/maps.js index c410b9d0..afaaaa8c 100644 --- a/project/maps.js +++ b/project/maps.js @@ -80,6 +80,7 @@ maps_90f36752_8815_4be8_b32b_d7fad1d0542e = '66':{'cls': 'items', 'id': 'sword0'}, // 空剑 '67':{'cls': 'items', 'id': 'shield0'}, // 空盾 '68':{'cls': 'items', 'id': 'lifeWand'}, // 生命魔杖 + '69':{'cls': 'items', 'id': 'jumpShoes'}, // 生命魔杖 ////////////////////////// 门、楼梯、传送点部分 ////////////////////////// diff --git a/project/sounds/jump.mp3 b/project/sounds/jump.mp3 new file mode 100644 index 00000000..b8ce8f78 Binary files /dev/null and b/project/sounds/jump.mp3 differ