diff --git a/_server/MotaAction.g4 b/_server/MotaAction.g4 new file mode 100644 index 0000000..bcf8bfe --- /dev/null +++ b/_server/MotaAction.g4 @@ -0,0 +1,4242 @@ +// 编辑此文件用的vscode插件: https://marketplace.visualstudio.com/items?itemName=zhaouv.vscode-mota-js-extension +// 此文件通过antlr-blockly生成编辑器中的图块, 相关帮助说明: https://zhaouv.github.io/antlr-blockly/docs/#/README +// 添加和修改图块的说明见 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改事件编辑器 + +/* +特殊注入demo +doubleclicktext : EvalString_1 +previewBlock : true +// [x, y, floorId, forceFloor] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +// 自动补全 +allIds : ['EvalString_1'] +allEnemys : ['EvalString_1'] +allItems : ['EvalString_1'] +allImages : ['EvalString_1'] +allAnimates : ['EvalString_1'] +allBgms : ['EvalString_1'] +allSounds : ['EvalString_1'] +allShops : ['EvalString_1'] +allFloorIds : ['EvalString_1'] +// 选择素材 +material : ["./project/animates/", "IdString_0"] +*/ + + +grammar MotaAction; + +//===============parser=============== +//===blockly语句=== + +common_m + : '编辑事件' BGNL? Newline action+ BEND + + +/* common_m +tooltip : 编辑事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + + +//事件 事件编辑器入口之一 +event_m + : '事件' BGNL? Newline '覆盖触发器' Bool '启用' Bool '通行状态' B_0_List '显伤' Bool '不透明度' Number BGNL? Newline '该点特效' '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number BGNL? Newline action+ BEND + + +/* event_m +tooltip : 编辑魔塔的事件 +helpUrl : /_docs/#/instruction +default : [false,true,null,true,1,0,0,0,false,0,null] +B_0_List_0=eval(B_0_List_0); +if (Number_0 < 0 || Number_0 > 1) throw '不透明度需要在0~1之间'; +if (Number_1 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_2 < 0 || Number_2 > 1) throw '灰度需要在0~1之间'; +if (Number_3 < 0) throw '阴影不得小于0;0为完全没有阴影'; +var code = { + 'trigger': Bool_0?'action':null, + 'enable': Bool_1, + 'noPass': B_0_List_0, + 'displayDamage': Bool_2, + 'opacity': Number_0, + 'filter': { + 'blur': Number_1, + 'hue': Int_0, + 'grayscale': Number_2, + 'invert': Bool_3, + 'shadow': Number_3 + }, + 'data': 'data_asdfefw' +} +if (!Bool_0 && Bool_1 && B_0_List_0===null && Bool_2 && Number_0==1.0 && Number_1==0 && Int_0==0 && Number_2==0 && !Bool_3 && Number_3==0) + code = 'data_asdfefw'; +code=JSON.stringify(code,null,2).split('"data_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; + + +//自动事件 事件编辑器入口之一 +autoEvent_m + : '自动事件:' '触发条件' EvalString_Multi '优先级' Int BGNL? Newline '仅在本层检测' Bool '事件流中延迟执行' Bool '允许多次执行' Bool BGNL? Newline action+ BEND + + +/* autoEvent_m +tooltip : 自动事件 +helpUrl : /_docs/#/instruction +default : ["flag:__door__===2",0,true,false,false,null] +var code = { + "condition": 'autoEvent_condition', // 条件不可为null + "currentFloor": Bool_0, // 是否仅在本层检测 + "priority": Int_0, // 优先级 + "delayExecute": Bool_1, // 延迟执行 + "multiExecute": Bool_2, // 是否允许多次执行 + "data": 'autoEvent_asdfefw', // 事件列表 +}; +code=JSON.stringify(code,null,2).replace('autoEvent_condition', EvalString_Multi_0).split('"autoEvent_asdfefw"').join('[\n'+action_0+']\n'); +return code; +*/; + +//升级 事件编辑器入口之一 +level_m + : '等级提升' BGNL? Newline levelCase+ BEND + + +/* level_m +tooltip : 升级事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+levelCase_0+']\n'; +return code; +*/; + +levelCase + : '需求' expression '称号' EvalString? '是否扣除经验' Bool BGNL? Newline action+ + + +/* levelCase +tooltip : 升级设定 +helpUrl : /_docs/#/instruction +default : [0,"",false,null] +colour : this.subColor +Bool_0 = Bool_0?', "clear": true':''; +var code = '{"need": "'+expression_0+'", "title": "'+EvalString_0+'"'+Bool_0+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +//商店 事件编辑器入口之一 +shop_m + : '全局商店列表' BGNL? Newline shoplist+ + +/* shop_m +tooltip : 全局商店列表 +helpUrl : /_docs/#/instruction +var code = '['+shoplist_0+']\n'; +return code; +*/; + +shoplist + : shopsub + | shopitem + | shopcommonevent + | emptyshop + ; + +emptyshop + : Newline + + +/* emptyshop +var code = ' \n'; +return code; +*/; + +shopsub + : '商店 id' IdString '标题' EvalString? '图像' IdString? BGNL? Newline '文字' EvalString_Multi? BGNL? Newline '快捷名称' EvalString '未开启不显示' Bool '不可预览' Bool BGNL? Newline shopChoices+ BEND + + +/* shopsub +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +allIds : ['IdString_1'] +default : ["shop1","贪婪之神","moneyShop","勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:","金币商店",false,false] +var title=''; +if (EvalString_0==''){ + if (IdString_1=='') title=''; + else title='\t['+IdString_1+']'; +} else { + if (IdString_1=='')title='\t['+EvalString_0+']'; + else title='\t['+EvalString_0+','+IdString_1+']'; +} +title += EvalString_Multi_0; +var code = '{\n"id": "'+IdString_0+'",\n"text": "'+title+'",\n"textInList": "'+EvalString_1+'",\n"mustEnable": '+Bool_0+',\n"disablePreview": '+Bool_1+',\n"choices":[\n'+shopChoices_0+']},\n'; +return code; +*/; + +shopChoices + : '商店选项' EvalString '使用条件' EvalString BGNL? Newline '图标' IdString? '颜色' ColorString? Colour '出现条件' EvalString? BGNL? Newline action+ BEND + + +/* shopChoices +tooltip : 商店选项 +helpUrl : /_docs/#/instruction +default : ["攻击+1","status:money>=20+2*flag:shop1","","","rgba(255,255,255,1)",""] +allIds : ['IdString_0'] +colour : this.subColor +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"') +IdString_0 = IdString_0? (', "icon": "'+IdString_0+'"'):''; +var code = '{"text": "'+EvalString_0+'", "need": "'+EvalString_1+'"'+IdString_0+ColorString_0+EvalString_2+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +shopitem + : '道具商店 id' IdString '快捷名称' EvalString '使用' ShopUse_List '未开启不显示' Bool BGNL? Newline shopItemChoices+ BEND + + +/* shopitem +tooltip : 道具商店 +helpUrl : /_docs/#/instruction +default : ["itemShop","道具商店",false] +var code = { + 'id': IdString_0, + 'item': true, + 'textInList': EvalString_0, + 'use': ShopUse_List_0 || 'money', + 'mustEnable': Bool_0, + 'choices': 'choices_aqwedsa' +} +code=JSON.stringify(code,null,2).split('"choices_aqwedsa"').join('[\n'+shopItemChoices_0+']')+',\n'; +return code; +*/; + +shopItemChoices + : '道具名' IdString '存量' IntString? '买入价格' EvalString? '卖出价格' EvalString? '出现条件' EvalString? BEND + + + +/* shopItemChoices +tooltip : 道具商店选项,每一项是道具名;买入或卖出可以不填表示只能卖出或买入 +helpUrl : /_docs/#/instruction +default : ["yellowKey","","10","",""] +colour : this.subColor +IntString_0 = IntString_0 ? (', "number": '+IntString_0) : ''; +EvalString_0 = EvalString_0 ? (', "money": "'+EvalString_0+'"') : ''; +EvalString_1 = EvalString_1 ? (', "sell": "'+EvalString_1+'"') : ''; +if (!EvalString_0 && !EvalString_1) throw "买入金额和卖出金额至少需要填写一个"; +EvalString_2 = EvalString_2 ? (', "condition": "'+EvalString_2+'"') : ''; +var code = '{"id": "' + IdString_0 + '"' + IntString_0 + EvalString_0 + EvalString_1 + EvalString_2 + '},\n'; +return code; +*/; + +shopcommonevent + : '公共事件商店 id' IdString '快捷名称' EvalString '未开启不显示' Bool BGNL? '执行的公共事件名' EvalString '参数列表' JsonEvalString? + +/* shopcommonevent +tooltip : 全局商店, 执行一个公共事件 +helpUrl : /_docs/#/instruction +default : ["shop1","回收钥匙商店",false,"回收钥匙商店",""] +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); +} +var code = { + 'id': IdString_0, + 'textInList': EvalString_0, + 'mustEnable': Bool_0, + 'commonEvent': EvalString_1 +} +if (JsonEvalString_0) code.args = JSON.parse(JsonEvalString_0); +code=JSON.stringify(code,null,2)+',\n'; +return code; +*/; + +//beforeBattle 事件编辑器入口之一 +beforeBattle_m + : '战斗开始前' BGNL? Newline action+ BEND + + +/* beforeBattle_m +tooltip : 战斗开始前 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//afterBattle 事件编辑器入口之一 +afterBattle_m + : '战斗结束后' BGNL? Newline action+ BEND + + +/* afterBattle_m +tooltip : 系统引发的战后 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//afterGetItem 事件编辑器入口之一 +afterGetItem_m + : '获取道具后' '轻按时不触发' Bool BGNL? Newline action+ BEND + + +/* afterGetItem_m +tooltip : 系统引发的道具后事件 +helpUrl : /_docs/#/instruction +if (Bool_0) { + return '{"disableOnGentleClick": true, "data": [\n'+action_0+']\n}'; +} else { + return '[\n'+action_0+']\n'; +} +*/; + +//afterOpenDoor 事件编辑器入口之一 +afterOpenDoor_m + : '打开门后' BGNL? Newline action+ BEND + + +/* afterOpenDoor_m +tooltip : 系统引发的自定义事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//firstArrive 事件编辑器入口之一 +firstArrive_m + : '首次到达楼层' BGNL? Newline action+ BEND + + +/* firstArrive_m +tooltip : 首次到达楼层 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//eachArrive 事件编辑器入口之一 +eachArrive_m + : '每次到达楼层' BGNL? Newline action+ BEND + + +/* eachArrive_m +tooltip : 每次到达楼层 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//changeFloor 事件编辑器入口之一 +changeFloor_m + : '楼梯, 传送门' BGNL? Newline Floor_List IdString? Stair_List 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List '动画时间' IntString? '穿透性' IgnoreChangeFloor_List BEND + + +/* changeFloor_m +tooltip : 楼梯, 传送门, 如果目标楼层有多个楼梯, 写upFloor或downFloor可能会导致到达的楼梯不确定, 这时候请使用loc方式来指定具体的点位置 +helpUrl : /_docs/#/instruction +default : [null,"MTx",null,"","",null,"",null] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +allFloorIds : ['IdString_0'] +var toFloorId = IdString_0; +if (Floor_List_0!='floorId') toFloorId = Floor_List_0; +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+', '+PosString_1+']'; +} +if (Stair_List_0===':now') loc = ''; +else if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +if (IgnoreChangeFloor_List_0!='null') { + IgnoreChangeFloor_List_0 = ', "ignoreChangeFloor": '+IgnoreChangeFloor_List_0; +} else { + IgnoreChangeFloor_List_0 = ''; +} +var code = '{"floorId": "'+toFloorId+'"'+loc+DirectionEx_List_0+IntString_0+IgnoreChangeFloor_List_0+' }\n'; +return code; +*/; + +//commonEvent 事件编辑器入口之一 +commonEvent_m + : '公共事件' BGNL? Newline action+ BEND + + +/* commonEvent_m +tooltip : 公共事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +//item 事件编辑器入口之一 +item_m + : '使用道具事件' BGNL? Newline action+ BEND + + +/* item_m +tooltip : 使用道具事件 +helpUrl : /_docs/#/instruction +var code = '[\n'+action_0+']\n'; +return code; +*/; + +// levelChoose 事件编辑器入口之一 +levelChoose_m + : '难度分歧' BGNL? levelChooseList+ BEND + + +/* levelChoose_m +tooltip : 难度分歧 +helpUrl : /_docs/#/instruction +var code = '[\n'+levelChooseList_0+']\n'; +return code; +*/; + +levelChooseList + : levelChooseChoice + | levelChooseEmpty; + +levelChooseEmpty + : Newline + +/* levelChooseEmpty +var code = ' \n'; +return code; +*/; + +levelChooseChoice + : '难度分歧项' '名称' EvalString '简写' EvalString '变量:hard值' NInt '颜色' ColorString? Colour BGNL Newline action+ BEND + +/* levelChooseChoice +tooltip : 难度分歧项 +helpUrl : /_docs/#/instruction +default : ['简单','Easy',1,''] +ColorString_0 = ColorString_0 ? (', "color": [' + ColorString_0 + ']') : ''; +var code = '{"title": "'+EvalString_0+'", "name": "'+EvalString_1+'", "hard": '+NInt_0+ColorString_0+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +floorPartition_m + : '高层塔分区管理' BGNL? floorPartitionList+ BEND + + +/* floorPartition_m +tooltip : 高层塔分区管理 +helpUrl : /_docs/#/instruction +var code = '[\n'+floorPartitionList_0+']\n'; +return code; +*/; + +floorPartitionList + : floorPartitionItem + | floorPartitionEmpty; + +floorPartitionEmpty + : Newline + +/* floorPartitionEmpty +var code = ' \n'; +return code; +*/; + +floorPartitionItem + : '分区项' '起始楼层ID' IdString '终止楼层ID(不填代表到最后一层)' IdString? BEND + +/* floorPartitionItem +tooltip : 难度分歧项 +helpUrl : /_docs/#/instruction +default : ['MTx',''] +IdString_1 = IdString_1 ? (', "'+IdString_1+'"') : ''; +var code = '["'+IdString_0+'"'+IdString_1+'],\n'; +return code; +*/; + + +// equip 事件编辑器入口之一 +equip_m + : '装备' '类型' EvalString '装备动画(第一个装备格有效)' IdString? BGNL? '数值提升项' equipList+ '百分比提升项' equipList+ '穿上时事件' action+ '脱下时事件' action+ '此道具cls须为equips并设置canUseItemEffect' BEND + + +/* equip_m +tooltip : 装备 +default : ['0', ''] +helpUrl : /_docs/#/instruction +allAnimates : ['IdString_0'] +if (!/^\d+$/.test(EvalString_0)) { + EvalString_0 = '"' + EvalString_0 + '"'; +} +IdString_0 = IdString_0 && (', "animate": "'+IdString_0+'"'); +if (action_0.trim()) action_0 = ', "equipEvent": [\n' + action_0 + ']'; +if (action_1.trim()) action_1 = ', "unequipEvent": [\n' + action_1 + ']'; +var code = '{"type": '+EvalString_0+IdString_0+', "value": {\n'+equipList_0+'\n}, "percentage": {\n'+equipList_1+'\n}'+action_0+action_1+'}'; +return code; +*/; + +equipList + : equipKnown + | equipUnknown + | equipEmpty; + + +equipKnown + : Equip_List ':' EvalString BEND + + +/* equipKnown +tooltip : 装备项 +default : ['atk', 10] +helpUrl : /_docs/#/instruction +if (!/^[+-]?\d+(\.\d+)?$/.test(EvalString_0)) EvalString_0 = '"' + EvalString_0 + '"'; +return '"'+Equip_List_0+'": '+EvalString_0+', '; +*/; + +equipUnknown + : EvalString ':' EvalString BEND + + +/* equipUnknown +tooltip : 装备项 +default : ['speed', 10] +helpUrl : /_docs/#/instruction +if (!/^[+-]?\d+(\.\d+)?$/.test(EvalString_1)) EvalString_1 = '"' + EvalString_1 + '"'; +return '"'+EvalString_0+'": '+EvalString_1+', '; +*/; + + +equipEmpty + : Newline + +/* equipEmpty +var code = ' \n'; +return code; +*/; + +floorImage_m + : '楼层贴图' BGNL? Newline floorImageList+ BEND + + +/* floorImage_m +tooltip : 楼层贴图 +helpUrl : /_docs/#/instruction +var code = '[\n'+floorImageList_0+']\n'; +return code; +*/; + +floorImageList + : floorOneImage + | floorEmptyImage; + +floorOneImage + : '图片名' EvalString '翻转' Reverse_List '图层' Bg_Fg2_List '绘制坐标' 'x' NInt 'y' NInt '初始禁用' Bool BGNL? Newline + '裁剪起点坐标' 'x' IntString? 'y' IntString? '宽' IntString? '高' IntString? '帧数' IntString? BEND + + +/* floorOneImage +tooltip : 楼层贴图 +default : ["bg.jpg","null","bg",0,0,false,"","","","",""] +helpUrl : /_docs/#/instruction +allImages : ['EvalString_0'] +previewBlock : true +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +Bool_0 = Bool_0 ? (', "disable": true') : ''; +IntString_0 = IntString_0 && (', "sx": '+IntString_0); +IntString_1 = IntString_1 && (', "sy": '+IntString_1); +IntString_2 = IntString_2 && (', "w": '+IntString_2); +IntString_3 = IntString_3 && (', "h": '+IntString_3); +IntString_4 = IntString_4 && (', "frame": '+IntString_4); +return '{"name": "'+EvalString_0+'"'+Reverse_List_0+', "canvas": "'+Bg_Fg2_List_0+'", "x": '+NInt_0+', "y": '+NInt_1+Bool_0+IntString_0+IntString_1+IntString_2+IntString_3+IntString_4+'},\n'; +*/; + +floorEmptyImage + : Newline + +/* floorEmptyImage +var code = ' \n'; +return code; +*/; + + +// doorInfo 事件编辑器入口之一 +doorInfo_m + : '门信息' '开关门时间' Int '开门音效' EvalString? '关门音效' EvalString? BGNL? Newline '需要钥匙' doorKeyList+ '如需撞到开门还需要把图块触发器改成 openDoor' BGNL? Newline '开门后事件' action+ BEND + + +/* doorInfo_m +tooltip : 开门信息 +default : [160, 'door.mp3', 'door.mp3'] +helpUrl : /_docs/#/instruction +EvalString_0 = EvalString_0 && (', "openSound": "' + EvalString_0 + '"'); +EvalString_1 = EvalString_1 && (', "closeSound": "' + EvalString_1 + '"'); +if (action_0.trim()) action_0 = ', "afterOpenDoor": [\n' + action_0 + ']'; +var code = '{"time": '+Int_0+EvalString_0+EvalString_1+', "keys": {\n'+doorKeyList_0+'\n}'+action_0.trim()+'}'; +return code; +*/; + +doorKeyList + : doorKeyKnown + | doorKeyUnknown + | doorKeyEmpty; + + +doorKeyKnown + : Key_List ':' Int '需要但不消耗' Bool BEND + + +/* doorKeyKnown +tooltip : 开门需要钥匙 +default : ['yellowKey', 1, false] +helpUrl : /_docs/#/instruction +if (Bool_0) Key_List_0 += ':o'; +return '"'+Key_List_0+'": '+Int_0+', '; +*/; + +doorKeyUnknown + : IdString ':' Int '需要但不消耗' Bool BEND + + +/* doorKeyUnknown +tooltip : 开门需要钥匙 +default : ['orangeKey', 1, false] +helpUrl : /_docs/#/instruction +allItems : ['IdString_0'] +if (Bool_0) IdString_0 += ':o'; +return '"'+IdString_0+'": '+Int_0+', '; +*/; + + +doorKeyEmpty + : Newline + +/* doorKeyEmpty +var code = ' \n'; +return code; +*/; + + +faceIds_m + : '行走图朝向:' BGNL? Newline '向下ID' IdString? '向左ID' IdString? '向右ID' IdString? '向上ID' IdString? BEND + + +/* faceIds_m +tooltip : 行走图朝向 +default : ["","","",""] +allIds : ['IdString_0','IdString_1','IdString_2','IdString_3'] +helpUrl : /_docs/#/instruction +return '{' + [ + IdString_0 && ('"down": "' + IdString_0 +'"'), + IdString_1 && ('"left": "' + IdString_1 +'"'), + IdString_2 && ('"right": "' + IdString_2 +'"'), + IdString_3 && ('"up": "' + IdString_3 +'"'), +].filter(function (x) { return x; }).join(', ') + '}\n'; +*/; + + +mainStyle_m + : '主要样式设置:' '标题界面背景图:' EvalString BGNL? Newline + '竖屏标题界面背景图:' EvalString BGNL? Newline + '标题样式;可写 display: none 隐藏标题' EvalString BGNL? Newline + '标题按钮样式:' EvalString BGNL? Newline + '横屏状态栏背景;url(...) 0 0/100% 100% no-repeat 可将图片拉伸自适配' BGNL? Newline EvalString BGNL? Newline + '竖屏状态栏背景:' EvalString BGNL? Newline + '竖屏工具栏背景:' EvalString BGNL? Newline + '楼层切换样式:' EvalString BGNL? Newline + '状态栏颜色' ColorString Colour '边框颜色' ColorString Colour BGNL? Newline + '选中框颜色' ColorString Colour '全局字体' EvalString BEND + +/* mainStyle_m +tooltip : 主要样式设置 +default : ["project/images/bg.jpg", "project/images/bg.jpg", "color: white", "background-color: #32369F; opacity: 0.85; color: #FFFFFF; border: #FFFFFF 2px solid; caret-color: #FFD700;", "url(project/materials/ground.png) repeat", "url(project/materials/ground.png) repeat", "url(project/materials/ground.png) repeat", "background-color: black; color: white", "255,255,255,1", "rgba(255,255,255,1)", "204,204,204,1", "rgba(204,204,204,1)", "255,215,0,1", "rgba(255,215,0,1)", "Verdana"] +helpUrl : /_docs/#/instruction +var code = { + startBackground: EvalString_0, + startVerticalBackground: EvalString_1, + startLogoStyle: EvalString_2, + startButtonsStyle: EvalString_3, + statusLeftBackground: EvalString_4, + statusTopBackground: EvalString_5, + toolsBackground: EvalString_6, + floorChangingStyle: EvalString_7, + statusBarColor: JSON.parse('['+ColorString_0+']'), + borderColor: JSON.parse('['+ColorString_1+']'), + selectColor: JSON.parse('['+ColorString_2+']'), + font: EvalString_8 +}; +return JSON.stringify(code); +*/; + +nameMap_m + : '文件别名设置' '(可以游戏中使用此别名代替原始文件名)' BGNL? Newline nameMapList+ BEND + +/* nameMap_m +tooltip : 文件别名设置 +helpUrl : /_docs/#/instruction +var value = nameMapList_0.trim(); +if (value.startsWith(',')) value = value.substring(1); +return '{'+value+'}'; +*/; + +nameMapList + : nameMapBgm + | nameMapSoundKnown + | nameMapSoundUnknown + | nameMapImage + | nameMapAnimate + | nameMapUnknown + | nameMapEmpty; + +nameMapBgm + : '映射背景音乐' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapBgm +tooltip : 映射背景音乐 +default : ['背景音乐', 'bgm.mp3'] +allBgms : ['EvalString_1'] +material : ["./project/bgms/", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapSoundKnown + : '映射系统音效' '名称' NameMap_List '映射到文件' EvalString BEND + +/* nameMapSoundKnown +tooltip : 映射系统音效 +default : ['确定', 'confirm.mp3'] +allSounds : ['EvalString_0'] +material : ["./project/sounds/", "EvalString_0"] +helpUrl : /_docs/#/instruction +return ',"'+NameMap_List_0+'":"'+EvalString_0+'"'; +*/; + +nameMapSoundUnknown + : '映射音效' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapSoundUnknown +tooltip : 映射音效 +default : ['攻击', 'attack.mp3'] +allSounds : ['EvalString_1'] +material : ["./project/sounds/", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapImage + : '映射图片' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapImage +tooltip : 映射图片 +default : ['背景图', 'bg.jpg'] +allImages : ['EvalString_1'] +material : ["./project/images/:images", "EvalString_1"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapAnimate + : '映射动画' '名称' EvalString '映射到文件' IdString BEND + +/* nameMapAnimate +tooltip : 映射图片 +default : ['领域', 'zone'] +allAnimates : ['IdString_0'] +material : ["./project/animates/", "IdString_0"] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+IdString_0+'"'; +*/; + +nameMapUnknown + : '未知映射' '名称' EvalString '映射到文件' EvalString BEND + +/* nameMapUnknown +tooltip : 未知映射 +default : ['文件名', 'file.jpg'] +helpUrl : /_docs/#/instruction +return ',"'+EvalString_0+'":"'+EvalString_1+'"'; +*/; + +nameMapEmpty + : Newline + +/* nameMapEmpty +return ' \n'; +*/; + + +splitImages_m + : '图片切分(你可以将一张png格式的大图切分为若干小图)' BGNL? splitImagesList+ BEND + + +/* splitImages_m +tooltip: 图片裁剪 +helpUrl : /_docs/#/instruction +var code = '[\n'+splitImagesList_0+']\n'; +return code; +*/; + +splitImagesList + : splitImagesOne + | splitImagesEmpty; + + +splitImagesOne + : '图片切分项' '图片名' EvalString '每个小图宽度' Int '高度' Int '生成小图的前缀' EvalString BEND + +/* splitImagesOne +tooltip : 图片裁剪项 +helpUrl : /_docs/#/instruction +default : ['hero.png', 32, 32, 'hero_'] +material : ["./project/images/:images", "EvalString_0"] +allImages : ['EvalString_0'] +var code = '{"name": "'+EvalString_0+'", "width": '+Int_0+', "height": '+Int_1+', "prefix": "'+EvalString_1+'"},\n'; +return code; +*/; + +splitImagesEmpty + : Newline + +/* splitImagesEmpty +var code = ' \n'; +return code; +*/; + +//为了避免关键字冲突,全部加了_s +//动作 +action + : text_0_s + | text_1_s + | text_2_s + | moveTextBox_s + | clearTextBox_s + | comment_s + | autoText_s + | scrollText_s + | setText_s + | tip_s + | setValue_s + | setEnemy_s + | setEnemyOnPoint_s + | resetEnemyOnPoint_s + | moveEnemyOnPoint_s + | moveEnemyOnPoint_1_s + | setEquip_s + | setFloor_s + | setGlobalAttribute_s + | setGlobalValue_s + | setGlobalFlag_s + | setNameMap_s + | show_s + | hide_s + | setBlockOpacity_s + | setBlockFilter_s + | trigger_s + | insert_1_s + | insert_2_s + | exit_s + | setBlock_s + | turnBlock_s + | showFloorImg_s + | hideFloorImg_s + | showBgFgMap_s + | hideBgFgMap_s + | setBgFgBlock_s + | setHeroIcon_s + | update_s + | showStatusBar_s + | hideStatusBar_s + | setHeroOpacity_s + | sleep_s + | wait_s + | waitAsync_s + | stopAsync_s + | battle_s + | battle_1_s + | openDoor_s + | closeDoor_s + | changeFloor_s + | changePos_s + | setViewport_s + | setViewport_1_s + | lockViewport_s + | useItem_s + | loadEquip_s + | unloadEquip_s + | openShop_s + | disableShop_s + | follow_s + | unfollow_s + | animate_s + | animate_1_s + | stopAnimate_s + | vibrate_s + | showImage_s + | showImage_1_s + | hideImage_s + | showTextImage_s + | moveImage_s + | rotateImage_s + | scaleImage_s + | showGif_s + | setCurtain_0_s + | setCurtain_1_s + | screenFlash_s + | setWeather_s + | move_s + | moveAction_s + | moveHero_s + | jump_s + | jump_1_s + | jumpHero_s + | jumpHero_1_s + | playBgm_s + | pauseBgm_s + | resumeBgm_s + | loadBgm_s + | freeBgm_s + | playSound_s + | playSound_1_s + | stopSound_s + | setVolume_s + | setBgmSpeed_s + | win_s + | lose_s + | restart_s + | if_s + | if_1_s + | switch_s + | for_s + | forEach_s + | while_s + | dowhile_s + | break_s + | continue_s + | input_s + | input2_s + | choices_s + | confirm_s + | callBook_s + | callSave_s + | autoSave_s + | forbidSave_s + | callLoad_s + | previewUI_s + | clearMap_s + | setAttribute_s + | setFilter_s + | fillText_s + | fillBoldText_s + | drawTextContent_s + | fillRect_s + | strokeRect_s + | drawLine_s + | drawArrow_s + | fillPolygon_s + | strokePolygon_s + | fillEllipse_s + | strokeEllipse_s + | fillArc_s + | strokeArc_s + | drawImage_s + | drawImage_1_s + | drawIcon_s + | drawBackground_s + | drawSelector_s + | drawSelector_1_s + | unknown_s + | function_s + | pass_s + ; + +text_0_s + : '显示文章' ':' EvalString_Multi Newline + + +/* text_0_s +tooltip : text:显示一段文字(剧情) +helpUrl : /_docs/#/instruction +previewBlock : true +default : ["欢迎使用事件编辑器(双击方块可直接预览)"] +var code = '"'+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled()) { + code = '{"type": "text", "text": '+code; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +text_1_s + : '标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '起点 px' PosString? 'py' PosString? '宽' PosString? '编号' Int '不等待操作' Bool BGNL? Newline EvalString_Multi Newline + + +/* text_1_s +tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 +helpUrl : /_docs/#/instruction +previewBlock : true +allIds : ['EvalString_1'] +default : ["小妖精","fairy","","","","",0,false,"欢迎使用事件编辑器(双击方块可直接预览)"] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +var pos = ''; +if (PosString_0 || PosString_1) { + if (EvalString_2) throw new Error('对话框效果和起点像素位置只能设置一项!'); + pos = '[' + (PosString_0||0) + ',' + (PosString_1||0); + if (PosString_2) pos += ',' + PosString_2; + pos += ']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '"'+title+EvalString_2+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled() || pos || Int_0 || Bool_0) { + code = '{"type": "text", "text": '+code; + if (pos) code += ', "pos": ' + pos; + if (Int_0) code += ', "code": ' + Int_0; + if (Bool_0) code += ', "async": true'; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +text_2_s + : '标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '起点 px' PosString? 'py' PosString? '宽' PosString? '编号' Int '不等待操作' Bool BGNL? Newline EvalString_Multi BGNL? Newline textDrawingList* Newline + + +/* text_2_s +tooltip : text:显示一段文字(剧情),选项较多请右键点击帮助 +helpUrl : /_docs/#/instruction +previewBlock : true +allIds : ['EvalString_1'] +default : ["小妖精","fairy","","","","",0,"欢迎使用事件编辑器(双击方块可直接预览)",null] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +var pos = ''; +if (PosString_0 || PosString_1) { + if (EvalString_2) throw new Error('对话框效果和起点像素位置只能设置一项!'); + pos = '[' + (PosString_0||0) + ',' + (PosString_1||0); + if (PosString_2) pos += ',' + PosString_2; + pos += ']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '"'+title+EvalString_2+textDrawingList_0.replace(/\s/g, '')+EvalString_Multi_0+'"'; +if (block.isCollapsed() || !block.isEnabled() || pos || Int_0 || Bool_0) { + code = '{"type": "text", "text": '+code; + if (pos) code += ', "pos": ' + pos; + if (Int_0) code += ', "code": ' + Int_0; + if (Bool_0) code += ', "async": true'; + if (block.isCollapsed()) code += ', "_collapsed": true'; + if (!block.isEnabled()) code += ', "_disabled": true'; + code += '}'; +} +return code+',\n'; +*/; + +textDrawingList + : textDrawing + | textDrawingEmpty; + + +textDrawing + : '立绘' EvalString '翻转' Reverse_List '绘制坐标' 'x' IntString 'y' IntString '宽' IntString? '高' IntString? BGNL? Newline + '裁剪坐标' 'x' IntString? 'y' IntString? '宽' IntString? '高' IntString? '不透明度' EvalString? '旋转角度' IntString? + +/* textDrawing +tooltip : 立绘 +helpUrl : /_docs/#/instruction +default : ["fairy.png","null","0","0","","","","","","","",""] +colour : this.subColor +previewBlock : true +allImages : ['EvalString_0'] +if (Reverse_List_0 && Reverse_List_0 != 'null') EvalString_0 += Reverse_List_0; +var list = [EvalString_0, IntString_0, IntString_1]; +if (IntString_2 || IntString_3) { + if (list.length != 3 || !IntString_2 || !IntString_3) { + throw "绘制的宽和高需同时设置"; + } + list.push(IntString_2); + list.push(IntString_3); +} +if (IntString_4 || IntString_5 || IntString_6 || IntString_7) { + if (list.length != 5) throw "如设置裁剪区域,请先设置绘制区域的宽高"; + if (!IntString_4 || !IntString_5 || !IntString_6 || !IntString_7) { + throw "如设置裁剪区域,请同时设置全部的裁剪坐标和宽高"; + } + list.splice(1, 0, IntString_4, IntString_5, IntString_6, IntString_7); +} +if (EvalString_1) { + if (list.length != 9) throw "如设置不透明度,需填满所有坐标和宽高"; + var opacity = parseFloat(EvalString_1); + if (isNaN(opacity) || opacity < 0 || opacity > 1) throw "不合法的不透明度,必须是0到1之间" + list.push(opacity); +} +if (IntString_8) { + if (list.length != 10) throw "如设置旋转角度,需填满所有坐标和宽高,以及不透明度"; + list.push(IntString_8); +} +return "\\f[" + list.join(",")+"]"; +*/; + +textDrawingEmpty + : Newline + +/* textDrawingEmpty +var code = ''; +return code; +*/; + +moveTextBox_s + : '移动对话框' ':' Int 'px' PosString 'py' PosString '使用增量' Bool '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + +/* moveTextBox_s +tooltip : 移动对话框 +helpUrl : /_docs/#/instruction +default : [1,"0","0",false,'',500,false] +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +Bool_0 = Bool_0 ?', "relative": true':''; +Bool_1 = Bool_1 ?', "async": true':''; +var code = '{"type": "moveTextBox", "code": '+Int_0+', "loc": ['+PosString_0+','+PosString_1+']'+Bool_0+MoveMode_List_0+', "time": '+Int_1+Bool_1+'},\n'; +return code; +*/; + +clearTextBox_s + : '清除对话框' ':' EvalString? Newline + +/* clearTextBox_s +tooltip : 清除对话框 +helpUrl : /_docs/#/instruction +default : ["1"] +if (EvalString_0 && !/^\d+(,\d+)*$/.test(EvalString_0)) throw new Error('对话框编号需要以逗号分隔'); +EvalString_0 = EvalString_0 ? (', "code": ['+EvalString_0+']') : ''; +var code = '{"type": "clearTextBox"'+EvalString_0+'},\n'; +return code; +*/; + + +comment_s + : '添加注释' ':' EvalString_Multi Newline + + +/* comment_s +tooltip : comment:添加一段会被游戏跳过的注释内容 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +default : ["可以在这里写添加任何注释内容"] +colour : this.commentColor +var code = '{"type": "comment", "text": "'+EvalString_Multi_0+'"},\n'; +return code; +*/; + +autoText_s + : '自动剧情文本: 标题' EvalString? '图像' EvalString? '对话框效果' EvalString? '时间' Int BGNL? EvalString_Multi Newline + + +/* autoText_s +tooltip : autoText:自动剧情文本,用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +allIds : ['EvalString_1'] +default : ["小妖精","fairy","",3000,"用户无法跳过自动剧情文本,大段剧情文本请添加“是否跳过剧情”的提示"] +var title=''; +if (EvalString_0==''){ + if (EvalString_1=='' )title=''; + else title='\\t['+EvalString_1+']'; +} else { + if (EvalString_1=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+EvalString_1+']'; +} +if(EvalString_2 && !(/^(up|center|down|hero|this)(,(hero|null|\d+,\d+|\d+))?$/.test(EvalString_2))) { + throw new Error('对话框效果的用法请右键点击帮助'); +} +EvalString_2 = EvalString_2 && ('\\b['+EvalString_2+']'); +var code = '{"type": "autoText", "text": "'+title+EvalString_2+EvalString_Multi_0+'", "time": '+Int_0+'},\n'; +return code; +*/; + +scrollText_s + : '滚动剧情文本:' '时间' Int '行距' Number '不等待执行完毕' Bool? BGNL? EvalString_Multi Newline + + +/* scrollText_s +tooltip : scrollText:滚动剧情文本,将从下到上进行滚动显示。 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +default : [5000,1.4,false,"时间是总时间,可以使用setText事件来控制字体、颜色、大小、偏移量等"] +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "scrollText", "text": "'+EvalString_Multi_0+'"'+Bool_0+', "time" :'+Int_0+', "lineHeight": '+Number_0+'},\n'; +return code; +*/; + +setText_s + : '设置剧情文本的属性' '位置' SetTextPosition_List '偏移像素' IntString? '对齐' TextAlign_List? '粗体' B_1_List? BGNL? '标题颜色' ColorString? Colour '正文颜色' ColorString? Colour '背景色' EvalString? Colour BGNL? '标题大小' IntString? '正文大小' IntString? '行距' IntString? '打字间隔' IntString? '字符间距' IntString? '淡入淡出时间' IntString? Newline + + +/* setText_s +tooltip : setText:设置剧情文本的属性,颜色为RGB三元组或RGBA四元组,打字间隔为剧情文字添加的时间间隔,为整数或不填,字符间距为字符之间的距离,为整数或不填。 +helpUrl : /_docs/#/instruction +previewBlock : true +default : [null,"",null,null,"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","","","","",""] +SetTextPosition_List_0 =SetTextPosition_List_0==='null'?'': ', "position": "'+SetTextPosition_List_0+'"'; +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +var colorRe = MotaActionFunctions.pattern.colorRe; +IntString_0 = IntString_0 ? (', "offset": '+IntString_0) : ''; +ColorString_0 = ColorString_0 ? (', "title": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "text": ['+ColorString_1+']') : ''; +if (EvalString_0) { + if (colorRe.test(EvalString_0)) { + EvalString_0 = ', "background": ['+EvalString_0+']'; + } + else if (/^\w+\.png$/.test(EvalString_0)) { + EvalString_0 = ', "background": "'+EvalString_0+'"'; + } + else { + throw new Error('背景格式错误,必须是形如0~255,0~255,0~255,0~1的颜色,或一个WindowSkin的png图片名称'); + } +} +IntString_1 = IntString_1 ? (', "titlefont": '+IntString_1) : ''; +IntString_2 = IntString_2 ? (', "textfont": '+IntString_2) : ''; +IntString_3 = IntString_3 ? (', "lineHeight": '+IntString_3) : ''; +IntString_4 = IntString_4 ? (', "time": '+IntString_4) : ''; +IntString_5 = IntString_5 ? (', "letterSpacing": '+IntString_5) : ''; +IntString_6 = IntString_6 ? (', "animateTime": ' + IntString_6) : ''; +B_1_List_0 = B_1_List_0==='null'?'':', "bold": '+B_1_List_0; +var code = '{"type": "setText"'+SetTextPosition_List_0+IntString_0+TextAlign_List_0+B_1_List_0+ColorString_0+ColorString_1+EvalString_0+IntString_1+IntString_2+IntString_3+IntString_4+IntString_5+IntString_6+'},\n'; +return code; +*/; + +tip_s + : '显示提示' ':' EvalString '图标ID' IdString? Newline + + +/* tip_s +tooltip : tip:显示一段提示文字 +helpUrl : /_docs/#/instruction +allIds : ['IdString_0'] +default : ["这段话将在左上角以气泡形式显示",""] +IdString_0 = IdString_0 && (', "icon": "' + IdString_0 + '"'); +var code = '{"type": "tip", "text": "'+EvalString_0+'"'+IdString_0+'},\n'; +return code; +*/; + +setValue_s + : '数值操作' ':' '名称' idString_e AssignOperator_List expression '不刷新状态栏' Bool Newline + + +/* setValue_s +tooltip : setValue:设置勇士的某个属性、道具个数, 或某个变量/Flag的值 +helpUrl : /_docs/#/instruction +default : ["","","",false] +colour : this.dataColor +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setValue", "name": "'+idString_e_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"' + Bool_0 + '},\n'; +return code; +*/; + + +setEnemy_s + : '设置怪物属性' ':' '怪物ID' IdString '的' EnemyId_List AssignOperator_List expression '不刷新显伤' Bool Newline + + +/* setEnemy_s +tooltip : setEnemy:设置某个怪物的属性 +helpUrl : /_docs/#/instruction +default : ["greenSlime", "atk", "=", "", false] +allEnemys : ['IdString_0'] +colour : this.dataColor +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setEnemy", "id": "'+IdString_0+'", "name": "'+EnemyId_List_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"'+Bool_0+'},\n'; +return code; +*/; + + +setEquip_s + : '设置装备属性' ':' '装备ID' IdString EquipValueType_List '的' EvalString AssignOperator_List expression Newline + + +/* setEquip_s +tooltip : setEquip:设置某个怪物的属性 +helpUrl : /_docs/#/instruction +default : ["sword1", "value", "atk", "="] +allEquips : ['IdString_0'] +colour : this.dataColor +EquipValueType_List_0 = EquipValueType_List_0 == 'percentage' ? ', "valueType": "percentage"' : ', "valueType": "value"'; +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +var code = '{"type": "setEquip", "id": "'+IdString_0+'"'+EquipValueType_List_0+', "name": "'+EvalString_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"},\n'; +return code; +*/; + + +setEnemyOnPoint_s + : '设置某点怪物属性' ':' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '的' EnemyPoint_List AssignOperator_List expression '不刷新显伤' Bool Newline + + +/* setEnemyOnPoint_s +tooltip : setEnemyOnPoint:设置某个点上怪物的属性 +helpUrl : /_docs/#/instruction +default : ["", "", "", "atk", "=", "", false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (AssignOperator_List_0 && AssignOperator_List_0 != '=') { + AssignOperator_List_0 = ', "operator": "' + AssignOperator_List_0 + '"'; +} else AssignOperator_List_0 = ''; +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "setEnemyOnPoint"'+floorstr+IdString_0+', "name": "'+EnemyPoint_List_0+'"'+AssignOperator_List_0+', "value": "'+expression_0+'"'+Bool_0+'},\n'; +return code; +*/; + +resetEnemyOnPoint_s + : '重置某点怪物属性' ':' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* resetEnemyOnPoint_s +tooltip : resetEnemyOnPoint:重置某个点上怪物的属性 +helpUrl : /_docs/#/instruction +default : ["", "", "", false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "resetEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +moveEnemyOnPoint_s + : '移动某点怪物属性' ':' '起点' 'x' PosString? ',' 'y' PosString? '终点' 'x' PosString? 'y' PosString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* moveEnemyOnPoint_s +tooltip : moveEnemyOnPoint:移动某个点上怪物的属性到其他点 +helpUrl : /_docs/#/instruction +default : ["", "", "", "", "", false] +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_2", "PosString_3"] +menu : [['选择起点位置','editor_blockly.selectPoint(block,["PosString_0", "PosString_1"])']] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = PosString_0 && PosString_1 ? ', "from": ['+PosString_0+','+PosString_1+']' : ''; +if (PosString_2 && PosString_3) floorstr += ', "to": ['+PosString_2+','+PosString_3+']' +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "moveEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +moveEnemyOnPoint_1_s + : '移动某点怪物属性' ':' '起点' 'x' PosString? ',' 'y' PosString? '增量' 'dx' PosString? 'dy' PosString? '楼层' IdString? '不刷新显伤' Bool Newline + + +/* moveEnemyOnPoint_1_s +tooltip : moveEnemyOnPoint:移动某个点上怪物的属性到其他点 +helpUrl : /_docs/#/instruction +default : ["", "", "", "", "", false] +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = PosString_0 && PosString_1 ? ', "from": ['+PosString_0+','+PosString_1+']' : ''; +if (PosString_2 && PosString_3) floorstr += ', "dxy": ['+PosString_2+','+PosString_3+']' +Bool_0 = Bool_0 ? ', "norefresh": true' : ''; +var code = '{"type": "moveEnemyOnPoint"'+floorstr+IdString_0+Bool_0+'},\n'; +return code; +*/; + +setFloor_s + : '设置楼层属性' ':' Floor_Meta_List '楼层名' IdString? '为' JsonEvalString Newline + + +/* setFloor_s +tooltip : setFloor:设置楼层属性;该楼层属性和编辑器中的楼层属性一一对应 +helpUrl : /_docs/#/instruction +default : ["title","","\"新楼层名\""] +allFloorIds : ['IdString_0'] +colour : this.dataColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "setFloor", "name": "'+Floor_Meta_List_0+'"'+IdString_0+', "value": '+JsonEvalString_0+'},\n'; +return code; +*/; + + +setGlobalAttribute_s + : '设置全局属性' ':' Global_Attribute_List '为' EvalString Newline + + +/* setGlobalAttribute_s +tooltip : setGlobalAttribute:设置全局属性 +helpUrl : /_docs/#/instruction +default : ["font","Verdana"] +colour : this.dataColor +var code = '{"type": "setGlobalAttribute", "name": "'+Global_Attribute_List_0+'", "value": "'+EvalString_0+'"},\n'; +return code; +*/; + + +setGlobalValue_s + : '设置全局数值' ':' Global_Value_List '为' EvalString Newline + + +/* setGlobalValue_s +tooltip : setGlobalValue:设置全局属性 +helpUrl : /_docs/#/instruction +default : ["lavaDamage","100"] +colour : this.dataColor +var code = '{"type": "setGlobalValue", "name": "'+Global_Value_List_0+'", "value": '+EvalString_0+'},\n'; +return code; +*/; + + +setGlobalFlag_s + : '设置系统开关' ':' Global_Flag_List Bool Newline + + +/* setGlobalFlag_s +tooltip : setGlobalFlag:设置系统开关 +helpUrl : /_docs/#/instruction +default : ["s:enableFloor","true"] +colour : this.dataColor +var code = '{"type": "setGlobalFlag", "name": "'+Global_Flag_List_0+'", "value": '+Bool_0+'},\n'; +return code; +*/; + + +setNameMap_s + : '设置文件别名' ':' EvalString '为' EvalString? Newline + + +/* setNameMap_s +tooltip : setNameMap:设置文件别名 +helpUrl : /_docs/#/instruction +default : ["背景音乐",""] +colour : this.dataColor +EvalString_1 = EvalString_1 ? (', "value": "' + EvalString_1 + '"') : ''; +var code = '{"type": "setNameMap", "name": "'+EvalString_0+'"'+EvalString_1+'},\n'; +return code; +*/; + +show_s + : '显示事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* show_s +tooltip : show: 将禁用事件启用,楼层和动画时间可不填,xy可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","","","",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "show"'+floorstr+IdString_0+''+IntString_0+Bool_0+'},\n'; +return code; +*/; + +hide_s + : '隐藏事件' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '同时删除' Bool '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* hide_s +tooltip : hide: 隐藏事件,同时可删除 +helpUrl : /_docs/#/instruction +default : ["","","",true,"",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "remove": true':''; +Bool_1 = Bool_1 ?', "async": true':''; +var code = '{"type": "hide"'+floorstr+IdString_0+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +setBlockOpacity_s + : '设置图块不透明度' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '不透明度' Number '动画时间' IntString? '不等待执行完毕' Bool? Newline + + +/* setBlockOpacity_s +tooltip : setBlockOpacity: 设置图块不透明度 +helpUrl : /_docs/#/instruction +default : ["","","",1.0,"",false] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (Number_0 < 0 || Number_0 > 1) throw new Error('不透明度需要在0~1之间'); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0 ?', "async": true':''; +var code = '{"type": "setBlockOpacity"'+floorstr+IdString_0+', "opacity": '+Number_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +setBlockFilter_s + : '设置图块特效' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number Newline + + +/* setBlockFilter_s +tooltip : setBlockFilter: 设置图块特效 +helpUrl : /_docs/#/instruction +default : ["","","",0,0,0,false,0] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (Number_0 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_1 < 0 || Number_1 > 1) throw '灰度需要在0~1之间'; +if (Number_2 < 0) throw '阴影不得小于0;0为完全没有阴影'; + +var code = '{"type": "setBlockFilter"'+floorstr+IdString_0+', "blur": '+Number_0+', "hue": '+Int_0+', "grayscale": '+Number_1+', "invert": '+Bool_0+', "shadow": '+Number_2+'},\n'; +return code; +*/; + + +trigger_s + : '触发系统事件' 'x' PosString? ',' 'y' PosString? Newline + + +/* trigger_s +tooltip : trigger: 立即触发另一个地点的事件 +helpUrl : /_docs/#/instruction +default : ["",""] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.eventColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "trigger"'+floorstr+'},\n'; +return code; +*/; + +insert_1_s + : '插入公共事件' EvalString '参数列表' JsonEvalString? Newline + + +/* insert_1_s +tooltip : insert: 插入公共事件并执行 +helpUrl : /_docs/#/instruction +allEvents : ['EvalString_0'] +default : ["加点事件", ""] +colour : this.eventColor +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); + JsonEvalString_0 = ', "args": ' +JsonEvalString_0; +} +var code = '{"type": "insert", "name": "'+EvalString_0+'"'+JsonEvalString_0+'},\n'; +return code; +*/; + +insert_2_s + : '插入事件' 'x' PosString? ',' 'y' PosString? Event_List? '楼层' IdString? '参数列表' JsonEvalString? Newline + + +/* insert_2_s +tooltip : insert: 立即插入另一个地点的事件执行,当前事件不会中断,事件坐标不会改变 +helpUrl : /_docs/#/instruction +default : ["0","0",null,"",""] +colour : this.eventColor +allFloorIds : ['IdString_0'] +selectPoint : ["PosString_0", "PosString_1", "IdString_0"] +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +if (JsonEvalString_0) { + if (!(JSON.parse(JsonEvalString_0) instanceof Array)) + throw new Error('参数列表必须是个有效的数组!'); + JsonEvalString_0 = ', "args": ' +JsonEvalString_0; +} +if (Event_List_0 && Event_List_0 !=='null') + Event_List_0 = ', "which": "'+Event_List_0+'"'; +else Event_List_0 = ''; +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "insert"'+floorstr+Event_List_0+IdString_0+JsonEvalString_0+'},\n'; +return code; +*/; + +exit_s + : '立刻结束当前事件' Newline + + +/* exit_s +tooltip : exit: 立刻结束当前事件 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = '{"type": "exit"},\n'; +return code; +*/; + +setBlock_s + : '转变图块为' EvalString 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* setBlock_s +tooltip : setBlock:设置某个图块,忽略坐标楼层则为当前事件 +helpUrl : /_docs/#/instruction +colour : this.mapColor +allFloorIds : ['IdString_0'] +allIds : ['EvalString_0'] +default : ["yellowDoor","","","","",false] +selectPoint : ["EvalString_1", "EvalString_2", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_1, EvalString_2); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +IntString_0 = IntString_0 && (', "time": ' + IntString_0); +Bool_0 = Bool_0 ? (', "async": true') : ''; +var code = '{"type": "setBlock", "number": "'+EvalString_0+'"'+floorstr+IdString_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +turnBlock_s + : '事件转向' DirectionEx_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* turnBlock_s +tooltip : turnBlock:事件转向;自动检索faceIds +helpUrl : /_docs/#/instruction +colour : this.mapColor +allFloorIds : ['IdString_0'] +default : [null,"","",""] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "turnBlock"'+DirectionEx_List_0+floorstr+IdString_0+'},\n'; +return code; +*/; + +showFloorImg_s + : '显示贴图' '像素坐标' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* showFloorImg_s +tooltip : showFloorImg: 显示一个贴图,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "showFloorImg"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +hideFloorImg_s + : '隐藏贴图' '像素坐标' 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* hideFloorImg_s +tooltip : hideFloorImg: 隐藏一个贴图,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "hideFloorImg"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +showBgFgMap_s + : '显示图层块' Bg_Fg_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* showBgFgMap_s +tooltip : showBgFgMap: 显示图层块,即背景图层/前景图层的某些图块,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["bg","","",""] +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "showBgFgMap", "name": "' + Bg_Fg_List_0 + '"' +floorstr+IdString_0+'},\n'; +return code; +*/; + +hideBgFgMap_s + : '隐藏图层块' Bg_Fg_List 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* hideBgFgMap_s +tooltip : hideBgFgMap: 隐藏图层块,即背景图层/前景图层的某些图块,xy为左上角坐标,可用逗号分隔表示多个点 +helpUrl : /_docs/#/instruction +default : ["bg","","",""] +allFloorIds : ['IdString_0'] +colour : this.mapColor +selectPoint : ["EvalString_0", "EvalString_1", "IdString_0"] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_0, EvalString_1); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "hideBgFgMap", "name": "' + Bg_Fg_List_0 + '"' +floorstr+IdString_0+'},\n'; +return code; +*/; + +setBgFgBlock_s + : '转变图层块' Bg_Fg_List '为' EvalString 'x' EvalString? ',' 'y' EvalString? '楼层' IdString? Newline + + +/* setBgFgBlock_s +tooltip : setBgFgBlock:设置某个图层块,忽略坐标楼层则为当前点 +helpUrl : /_docs/#/instruction +colour : this.mapColor +selectPoint : ["EvalString_1", "EvalString_2", "IdString_0"] +allIds : ['EvalString_0'] +allFloorIds : ['IdString_0'] +default : ["bg","yellowDoor","","",""] +var floorstr = MotaActionFunctions.processMultiLoc(EvalString_1, EvalString_2); +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var code = '{"type": "setBgFgBlock", "name": "' + Bg_Fg_List_0 + '", "number": "'+EvalString_0+'"'+floorstr+IdString_0+'},\n'; +return code; +*/; + +setHeroIcon_s + : '更改角色行走图' EvalString? '不重绘' Bool Newline + + +/* setHeroIcon_s +tooltip : setHeroIcon:更改角色行走图 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["hero.png", false] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +EvalString_0 = EvalString_0 && (', "name": "'+EvalString_0+'"'); +Bool_0 = Bool_0 ? (', "noDraw": true') : ''; +var code = '{"type": "setHeroIcon"'+EvalString_0+Bool_0+'},\n'; +return code; +*/; + +update_s + : '更新状态栏和地图显伤' '不检查自动事件' Bool Newline + + +/* update_s +tooltip : update: 立刻更新状态栏和地图显伤 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.dataColor +Bool_0 = Bool_0 ? (', "doNotCheckAutoEvents": true') : '' +var code = '{"type": "update"'+Bool_0+'},\n'; +return code; +*/; + +showStatusBar_s + : '显示状态栏' Newline + + +/* showStatusBar_s +tooltip : showStatusBar: 显示状态栏 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "showStatusBar"},\n'; +return code; +*/; + +hideStatusBar_s + : '隐藏状态栏' '不隐藏竖屏工具栏' Bool Newline + + +/* hideStatusBar_s +tooltip : hideStatusBar: 隐藏状态栏 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0?', "toolbox": true':''; +var code = '{"type": "hideStatusBar"'+Bool_0+'},\n'; +return code; +*/; + +setHeroOpacity_s + : '设置勇士不透明度' Number '渐变方式' MoveMode_List '动画时间' IntString? '不等待执行完毕' Bool Newline + +/* setHeroOpacity_s +tooltip : setHeroOpacity: 设置勇士不透明度 +helpUrl : /_docs/#/instruction +default : [1,'','',false] +colour : this.soundColor +if (Number_0 < 0 || Number_0 > 1) throw new Error('不透明度需要在0~1之间'); +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +IntString_0 = IntString_0 && (', "time": ' + IntString_0); +Bool_0 = Bool_0 ? (', "async": true') : ''; +var code = '{"type": "setHeroOpacity", "opacity": '+Number_0+MoveMode_List_0+IntString_0+Bool_0+'},\n'; +return code; +*/; + +sleep_s + : '等待' Int '毫秒' '不可被Ctrl跳过' Bool Newline + + +/* sleep_s +tooltip : sleep: 等待多少毫秒 +helpUrl : /_docs/#/instruction +default : [500, false] +colour : this.soundColor +Bool_0 = Bool_0?', "noSkip": true':''; +var code = '{"type": "sleep", "time": '+Int_0+Bool_0+'},\n'; +return code; +*/; + + +battle_s + : '强制战斗' IdString Newline + + +/* battle_s +tooltip : battle: 强制战斗 +helpUrl : /_docs/#/instruction +default : ["greenSlime"] +allEnemys : ['IdString_0'] +colour : this.dataColor +var code = '{"type": "battle", "id": "'+IdString_0+'"},\n'; +return code; +*/; + + +battle_1_s + : '强制战斗' 'x' PosString? ',' 'y' PosString? Newline + + +/* battle_1_s +tooltip : battle: 强制战斗 +helpUrl : /_docs/#/instruction +default : ["","",""] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +var code = '{"type": "battle"'+floorstr+'},\n'; +return code; +*/; + +openDoor_s + : '开门' 'x' PosString? ',' 'y' PosString? '楼层' IdString? '需要钥匙' Bool? '不等待执行完毕' Bool Newline + + +/* openDoor_s +tooltip : openDoor: 开门,楼层可不填表示当前层 +helpUrl : /_docs/#/instruction +default : ["","","",false,false] +selectPoint : ["PosString_0", "PosString_1", "IdString_0"] +allFloorIds : ['IdString_0'] +colour : this.mapColor +IdString_0 = IdString_0 && (', "floorId": "'+IdString_0+'"'); +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Bool_0 = Bool_0 ? ', "needKey": true' : ''; +Bool_1 = Bool_1 ? ', "async": true' : ''; +var code = '{"type": "openDoor"'+floorstr+IdString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +closeDoor_s + : '关门' 'x' PosString? ',' 'y' PosString? 'ID' IdString '不等待执行完毕' Bool Newline + + +/* closeDoor_s +tooltip : closeDoor: 关门事件,需要该点本身无事件 +helpUrl : /_docs/#/instruction +default : ["","","yellowDoor",false] +selectPoint : ["PosString_0", "PosString_1"] +allDoors : ['IdString_0'] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Bool_0 = Bool_0 ? ', "async": true' : ''; +var code = '{"type": "closeDoor", "id": "'+IdString_0+'"'+floorstr+Bool_0+'},\n'; +return code; +*/; + +changeFloor_s + : '楼层切换' Floor_List IdString? Stair_List 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List '动画时间' IntString? Newline + + +/* changeFloor_s +tooltip : changeFloor: 楼层切换,动画时间可不填 +helpUrl : /_docs/#/instruction +default : [null,"",null,"","",null,"",null] +selectPoint : ["PosString_0", "PosString_1", "IdString_0", true] +allFloorIds : ['IdString_0'] +colour : this.dataColor +var toFloorId = IdString_0; +if (Floor_List_0!='floorId') toFloorId = Floor_List_0; +toFloorId = toFloorId ? (', "floorId": "' + toFloorId +'"') : ''; +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+', '+PosString_1+']'; +} +if (Stair_List_0===':now') loc = ''; +else if (Stair_List_0!=='loc')loc = ', "stair": "'+Stair_List_0+'"'; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +var code = '{"type": "changeFloor"'+toFloorId+loc+DirectionEx_List_0+IntString_0+' },\n'; +return code; +*/; + +changePos_s + : '位置朝向切换' 'x' PosString? ',' 'y' PosString? '朝向' DirectionEx_List Newline + + +/* changePos_s +tooltip : changePos: 当前位置切换 +helpUrl : /_docs/#/instruction +default : ["","",null] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.dataColor +var loc = (PosString_0 && PosString_1) ? (', "loc": ['+PosString_0+','+PosString_1+']') : ''; +if (DirectionEx_List_0 == 'null') DirectionEx_List_0 = ''; +DirectionEx_List_0 = DirectionEx_List_0 && (', "direction": "'+DirectionEx_List_0+'"'); +var code = '{"type": "changePos"'+loc+DirectionEx_List_0+'},\n'; +return code; +*/; + +useItem_s + : '使用道具' IdString Newline + + +/* useItem_s +tooltip : useItem: 使用道具 +helpUrl : /_docs/#/instruction +colour : this.dataColor +allItems : ['IdString_0'] +default : ["pickaxe"] +var code = '{"type": "useItem", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +loadEquip_s + : '装上装备' IdString Newline + + +/* loadEquip_s +tooltip : loadEquip: 装上装备 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["sword1"] +allEquips : ['IdString_0'] +var code = '{"type": "loadEquip", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +unloadEquip_s + : '卸下第' Int '格装备孔的装备' Newline + + +/* unloadEquip_s +tooltip : unloadEquip: 卸下装备 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : [0] +var code = '{"type": "unloadEquip", "pos": '+Int_0+'},\n'; +return code; +*/; + +openShop_s + : '启用全局商店' IdString '同时打开' Bool Newline + + +/* openShop_s +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +colour : this.dataColor +default : ["shop1", true] +allShops : ['IdString_0'] +Bool_0 = Bool_0 ? (', "open": true') : ''; +var code = '{"type": "openShop", "id": "'+IdString_0+'"'+Bool_0+'},\n'; +return code; +*/; + +disableShop_s + : '禁用全局商店' IdString Newline + + +/* disableShop_s +tooltip : 全局商店 +helpUrl : /_docs/#/instruction +default : ["shop1"] +allShops : ['IdString_0'] +colour : this.dataColor +var code = '{"type": "disableShop", "id": "'+IdString_0+'"},\n'; +return code; +*/; + +follow_s + : '跟随勇士' '行走图' EvalString Newline + + +/* follow_s +tooltip : follow: 跟随勇士 +helpUrl : /_docs/#/instruction +default : ["npc.png"] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +colour : this.dataColor +var code = '{"type": "follow", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +unfollow_s + : '取消跟随' '行走图' EvalString? Newline + + +/* unfollow_s +tooltip : unfollow: 取消跟随 +helpUrl : /_docs/#/instruction +default : [""] +allImages : ['EvalString_0'] +material : ["./project/images/:images", "EvalString_0"] +colour : this.dataColor +EvalString_0 = EvalString_0 ? (', "name": "' + EvalString_0 + '"') : ""; +var code = '{"type": "unfollow"' + EvalString_0 + '},\n'; +return code; +*/; + +vibrate_s + : '画面震动' '方向' Vibrate_List '时间' Int '速度' Int '振幅' Int '不等待执行完毕' Bool Newline + + +/* vibrate_s +tooltip : vibrate: 画面震动 +helpUrl : /_docs/#/instruction +default : ["horizontal",2000,10,10,false] +colour : this.soundColor +var async = Bool_0?', "async": true':'' +var code = '{"type": "vibrate", "direction": "'+Vibrate_List_0+'", "time": '+Int_0+', "speed": '+Int_1+', "power": '+Int_2+async+'},\n'; +return code; +*/; + +animate_s + : '显示动画' EvalString '位置' 'x' PosString? 'y' PosString? '相对窗口坐标' Bool '不等待执行完毕' Bool Newline + + +/* animate_s +tooltip : animate:显示动画,位置填hero或者1,2形式的位置,或者不填代表当前事件点 +helpUrl : /_docs/#/instruction +default : ["zone","","",false,false] +allAnimates : ['EvalString_0'] +material : ["./project/animates/", "EvalString_0"] +menu : [['选择位置', 'editor_blockly.selectPoint(block, ["PosString_0", "PosString_1"])']] +colour : this.soundColor +var loc = PosString_0&&PosString_1?(', "loc": ['+PosString_0+','+PosString_1+']'):''; +Bool_0 = Bool_0?', "alignWindow": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "animate", "name": "'+EvalString_0+'"'+loc+Bool_0+Bool_1+'},\n'; +return code; +*/; + +animate_1_s + : '显示动画并跟随角色' EvalString '不等待执行完毕' Bool Newline + + +/* animate_1_s +tooltip : animate:显示动画并跟随角色 +helpUrl : /_docs/#/instruction +default : ["zone",false] +allAnimates : ['EvalString_0'] +material : ["./project/animates/", "EvalString_0"] +colour : this.soundColor +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "animate", "name": "'+EvalString_0+'", "loc": "hero"'+Bool_0+'},\n'; +return code; +*/; + +stopAnimate_s + : '停止所有动画' '执行动画回调' Bool Newline + +/* stopAnimate_s +tooltip : stopAnimate:停止所有动画 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.soundColor +Bool_0 = Bool_0?', "doCallback": true':''; +var code = '{"type": "stopAnimate"'+Bool_0+'},\n'; +return code; +*/; + +setViewport_s + : '设置视角' '左上角坐标' 'x' PosString? ',' 'y' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + + +/* setViewport_s +tooltip : setViewport: 设置视角 +helpUrl : /_docs/#/instruction +default : ["","","",0,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.soundColor +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +Bool_0 = Bool_0?', "async": true':''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; +return code; +*/; + +setViewport_1_s + : '设置视角' '增量坐标' 'dx' PosString? ',' 'dy' PosString? '移动方式' MoveMode_List '动画时间' Int '不等待执行完毕' Bool Newline + + +/* setViewport_1_s +tooltip : setViewport: 设置视角 +helpUrl : /_docs/#/instruction +default : ["0","0","",0,false] +colour : this.soundColor +var loc = ''; +if (PosString_0 && PosString_1) { + loc = ', "dxy": ['+PosString_0+','+PosString_1+']'; +} +Int_0 = Int_0 ?(', "time": '+Int_0):''; +Bool_0 = Bool_0?', "async": true':''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var code = '{"type": "setViewport"'+loc+MoveMode_List_0+Int_0+Bool_0+'},\n'; +return code; +*/; + +lockViewport_s + : '是否锁定视角' Bool Newline + +/* lockViewport_s +tooltip : lockViewport: 是否锁定视角 +helpUrl : /_docs/#/instruction +default : [false] +colour : this.soundColor +Bool_0 = Bool_0 ? (', "lock": true') : ''; +var code = '{"type": "lockViewport"'+Bool_0+'},\n'; +return code; +*/; + +showImage_s + : '显示图片' '图片编号' NInt '图片' EvalString '翻转' Reverse_List BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + + +/* showImage_s +tooltip : showImage:显示图片 +helpUrl : /_docs/#/instruction +default : [1,"bg.jpg","null","0","0",1,0,false] +allImages : ['EvalString_0'] +menu : [['选择图片','editor_blockly.selectMaterial(block, ["./project/images/:images", "EvalString_0"])']] +previewBlock : true +colour : this.imageColor +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showImage", "code": '+NInt_0+', "image": "'+EvalString_0+'"'+Reverse_List_0+', "loc": ['+PosString_0+','+PosString_1+'], "opacity": '+Number_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showImage_1_s + : '显示图片' '图片编号' NInt '图片' EvalString '翻转' Reverse_List BGNL? + '裁剪的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '不透明度' Number BGNL? + '绘制的起点像素' 'x' PosString 'y' PosString '宽' PosString? '高' PosString? '时间' Int '不等待执行完毕' Bool Newline + + +/* showImage_1_s +tooltip : showImage_1:显示图片 +helpUrl : /_docs/#/instruction +default : [1,"bg.jpg","null","0","0","","",1,"0","0","","",0,false] +allImages : ['EvalString_0'] +menu : [['选择图片','editor_blockly.selectMaterial(block, ["./project/images/:images", "EvalString_0"])']] +previewBlock : true +colour : this.imageColor +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showImage", "code": '+NInt_0+', "image": "'+EvalString_0+'"'+Reverse_List_0+', '+ + '"sloc": ['+PosString_0+','+PosString_1+','+PosString_2+','+PosString_3+'], '+ + '"loc": ['+PosString_4+','+PosString_5+','+PosString_6+','+PosString_7+'], '+ + '"opacity": '+Number_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showTextImage_s + : '显示图片化文本' EvalString_Multi BGNL? + '图片编号' NInt '起点像素' 'x' PosString 'y' PosString '行距' Number '翻转' Reverse_List '不透明度' Number '时间' Int '不等待执行完毕' Bool Newline + + +/* showTextImage_s +tooltip : showTextImage:显示图片化文本 +helpUrl : /_docs/#/instruction +colour : this.imageColor +doubleclicktext : EvalString_Multi_0 +default : ["可以使用setText事件来控制字体、颜色、大小、偏移量等",1,"0","0",1.4,"null",1,0,false] +if (Reverse_List_0 && Reverse_List_0 != 'null') { + Reverse_List_0 = ', "reverse": "' + Reverse_List_0 + '"'; +} else Reverse_List_0 = ''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "showTextImage", "code": '+NInt_0+', "text": "'+EvalString_Multi_0+'", "loc": ['+PosString_0+','+PosString_1+'], "lineHeight": '+Number_0+Reverse_List_0+', "opacity": '+Number_1+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +hideImage_s + : '清除图片' '图片编号' NInt '时间' Int '不等待执行完毕' Bool Newline + + +/* hideImage_s +tooltip : hideImage:清除图片 +helpUrl : /_docs/#/instruction +default : [1,0,false] +colour : this.imageColor +var async = Bool_0?', "async": true':''; +var code = '{"type": "hideImage", "code": '+NInt_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +showGif_s + : '显示或清除动图' EvalString? '起点像素位置' 'x' PosString? 'y' PosString? Newline + + +/* showGif_s +tooltip : showGif:显示动图 +helpUrl : /_docs/#/instruction +default : ["","",""] +allImages : ['EvalString_0'] +previewBlock : true +colour : this.imageColor +EvalString_0 = EvalString_0 ? (', "name": "'+EvalString_0+'"') : ''; +var loc = (PosString_0 && PosString_1) ? (', "loc": ['+PosString_0+','+PosString_1+']') : ''; +var code = '{"type": "showGif"'+EvalString_0+loc+'},\n'; +return code; +*/; + +moveImage_s + : '图片移动' '图片编号' NInt '终点像素位置' 'x' PosString? 'y' PosString? BGNL? + '不透明度' EvalString? '移动方式' MoveMode_List '移动时间' Int '不等待执行完毕' Bool Newline + + +/* moveImage_s +tooltip : moveImage:图片移动 +helpUrl : /_docs/#/instruction +default : [1,'','','','',500,false] +colour : this.imageColor +var toloc = ''; +if (PosString_0 && PosString_1) + toloc = ', "to": ['+PosString_0+','+PosString_1+']'; +EvalString_0 = (EvalString_0!=='') ? (', "opacity": '+EvalString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "moveImage", "code": '+NInt_0+toloc+MoveMode_List_0+EvalString_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +rotateImage_s + : '图片旋转' '图片编号' NInt '中心点像素' 'x' PosString? 'y' PosString? '移动方式' MoveMode_List BGNL? '旋转度数(正数顺时针,负数逆时针)' NInt '旋转时间' Int '不等待执行完毕' Bool Newline + + +/* rotateImage_s +tooltip : rotateImage:图片旋转 +helpUrl : /_docs/#/instruction +default : [1,'','','',90,500,false] +colour : this.imageColor +var loc = ''; +if (PosString_0 && PosString_1) + loc = ', "center": ['+PosString_0+','+PosString_1+']'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "rotateImage", "code": '+NInt_0+loc+', "angle": '+NInt_1+MoveMode_List_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +scaleImage_s + : '图片放缩' '图片编号' NInt '中心点像素' 'x' PosString? 'y' PosString? '移动方式' MoveMode_List BGNL? '放缩比例' Number '动画时间' Int '不等待执行完毕' Bool Newline + + +/* scaleImage_s +tooltip : scaleImage:图片放缩 +helpUrl : /_docs/#/instruction +default : [1,'','','',0.8,0,false] +colour : this.imageColor +if (Number_0 <= 0) throw new Error('放缩比例需要大于0'); +var loc = ''; +if (PosString_0 && PosString_1) + loc = ', "center": ['+PosString_0+','+PosString_1+']'; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "scaleImage", "code": '+NInt_0+loc+', "scale": '+Number_0+MoveMode_List_0+', "time": '+Int_0+async+'},\n'; +return code; +*/; + +setCurtain_0_s + : '更改画面色调' ColorString Colour '动画时间' IntString? BGNL? Newline '渐变方式' MoveMode_List '持续到下一个本事件' Bool '不等待执行完毕' Bool Newline + + +/* setCurtain_0_s +tooltip : setCurtain: 更改画面色调,动画时间可不填 +helpUrl : /_docs/#/instruction +default : ["255,255,255,1",'rgba(255,255,255,1)',500,'',true,false] +colour : this.soundColor +previewBlock : true +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +Bool_0 = Bool_0 ? ', "keep": true' : ''; +var async = Bool_1?', "async": true':''; +var code = '{"type": "setCurtain", "color": ['+ColorString_0+']'+IntString_0+MoveMode_List_0+Bool_0+async+'},\n'; +return code; +*/; + +setCurtain_1_s + : '恢复画面色调' '动画时间' IntString? '渐变方式' MoveMode_List '不等待执行完毕' Bool Newline + + +/* setCurtain_1_s +tooltip : setCurtain: 恢复画面色调,动画时间可不填 +helpUrl : /_docs/#/instruction +default : [500,'',false] +colour : this.soundColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "setCurtain"'+IntString_0+MoveMode_List_0 +async+'},\n'; +return code; +*/; + +screenFlash_s + : '画面闪烁' ColorString Colour '单次时间' Int '执行次数' IntString? '渐变方式' MoveMode_List '不等待执行完毕' Bool Newline + +/* screenFlash_s +tooltip : screenFlash: 画面闪烁,动画时间可不填 +helpUrl : /_docs/#/instruction +default : ["255,255,255,1",'rgba(255,255,255,1)',500,1,'',false] +colour : this.soundColor +if (ColorString_0 == '') throw new Error('颜色格式错误,形如:0~255,0~255,0~255,0~1'); +IntString_0 = IntString_0 ? (', "times": '+IntString_0):''; +MoveMode_List_0 = (MoveMode_List_0!=='') ? (', "moveMode": "'+MoveMode_List_0+'"'):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "screenFlash", "color": ['+ColorString_0+'], "time": '+Int_0 +IntString_0+MoveMode_List_0+async+'},\n'; +return code; +*/; + +setWeather_s + : '更改天气' Weather_List '强度' Int '持续到下个本事件' Bool Newline + + +/* setWeather_s +tooltip : setWeather:更改天气 +helpUrl : /_docs/#/instruction +default : [null,1,true] +colour : this.soundColor +if(Int_0<1 || Int_0>10) throw new Error('天气的强度等级, 在1-10之间'); +Bool_0 = Bool_0 ? ', "keep": true' : '' +var code = '{"type": "setWeather", "name": "'+Weather_List_0+'", "level": '+Int_0+Bool_0+'},\n'; +if(Weather_List_0===''||Weather_List_0==='null'||Weather_List_0==null)code = '{"type": "setWeather"},\n'; +return code; +*/; + +move_s + : '移动事件' 'x' PosString? ',' 'y' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool BGNL? moveDirection+ Newline + + +/* move_s +tooltip : move: 让某个NPC/怪物移动,位置可不填代表当前事件 +helpUrl : /_docs/#/instruction +default : ["","",500,true,false,null] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "move"'+floorstr+IntString_0+Bool_0+Bool_1+', "steps": ['+moveDirection_0.trim().substring(2)+']},\n'; +return code; +*/; + +moveDirection + : '移动方向' Move_List '格数' Int Newline + +/* moveDirection +tooltip : 移动方向 +helpUrl : /_docs/#/instruction +default : ["up", 0] +colour : this.subColor +if (Move_List_0 == 'speed' && Int_0 < 16) throw '设置的移动速度值不得小于16'; +return ', "' + Move_List_0 + ':' + Int_0 + '"'; +*/; + +moveAction_s + : '勇士前进一格或撞击' Newline + + +/* moveAction_s +tooltip : moveAction: 前进一格或撞击 +helpUrl : /_docs/#/instruction +colour : this.dataColor +return '{"type": "moveAction"},\n'; +*/; + + + +moveHero_s + : '无视地形移动勇士' '动画时间' IntString? '不等待执行完毕' Bool BGNL? moveDirection+ Newline + + +/* moveHero_s +tooltip : moveHero:移动勇士,用这种方式移动勇士的过程中将无视一切地形, 无视一切事件, 中毒状态也不会扣血 +helpUrl : /_docs/#/instruction +default : ["",false,"上右3下2后4左前2"] +colour : this.mapColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "moveHero"'+IntString_0+Bool_0+', "steps": ['+moveDirection_0.trim().substring(2)+']},\n'; +return code; +*/; + +jump_s + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '终止 x' PosString? ',' 'y' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool Newline + + +/* jump_s +tooltip : jump: 让某个NPC/怪物跳跃 +helpUrl : /_docs/#/instruction +default : ["","","","",500,true,false] +selectPoint : ["PosString_2", "PosString_3"] +menu : [['选择起点位置','editor_blockly.selectPoint(block,["PosString_0", "PosString_1"])']] +colour : this.mapColor + +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr += ', "from": ['+PosString_0+','+PosString_1+']'; +} +if (PosString_2 && PosString_3) { + floorstr += ', "to": ['+PosString_2+','+PosString_3+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "jump"'+floorstr+''+IntString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +jump_1_s + : '跳跃事件' '起始 x' PosString? ',' 'y' PosString? '增量 dx' PosString? ',' 'dy' PosString? '动画时间' IntString? '不消失' Bool '不等待执行完毕' Bool Newline + + +/* jump_1_s +tooltip : jump: 让某个NPC/怪物跳跃,给定增量 +helpUrl : /_docs/#/instruction +default : ["","","0","0",500,true,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor + +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr += ', "from": ['+PosString_0+','+PosString_1+']'; +} +if (PosString_2 && PosString_3) { + floorstr += ', "dxy": ['+PosString_2+','+PosString_3+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "keep": true':''; +Bool_1 = Bool_1?', "async": true':''; +var code = '{"type": "jump"'+floorstr+''+IntString_0+Bool_0+Bool_1+'},\n'; +return code; +*/; + +jumpHero_s + : '跳跃勇士' 'x' PosString? ',' 'y' PosString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* jumpHero_s +tooltip : jumpHero: 跳跃勇士 +helpUrl : /_docs/#/instruction +default : ["","",500,false] +selectPoint : ["PosString_0", "PosString_1"] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "loc": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "jumpHero"'+floorstr+IntString_0+Bool_0+'},\n'; +return code; +*/; + +jumpHero_1_s + : '跳跃勇士' '增量 dx' PosString? ',' 'dy' PosString? '动画时间' IntString? '不等待执行完毕' Bool Newline + + +/* jumpHero_1_s +tooltip : jumpHero: 跳跃勇士,给定增量 +helpUrl : /_docs/#/instruction +default : ["0","0",500,false] +colour : this.mapColor +var floorstr = ''; +if (PosString_0 && PosString_1) { + floorstr = ', "dxy": ['+PosString_0+','+PosString_1+']'; +} +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +Bool_0 = Bool_0?', "async": true':''; +var code = '{"type": "jumpHero"'+floorstr+IntString_0+Bool_0+'},\n'; +return code; +*/; + +playBgm_s + : '播放背景音乐' EvalString '开始播放秒数' Int '持续到下个本事件' Bool Newline + + +/* playBgm_s +tooltip : playBgm: 播放背景音乐 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3", 0, true] +allBgms : ['EvalString_0'] +material : ["./project/bgms/", "EvalString_0"] +colour : this.imageColor +Int_0 = Int_0 ? (', "startTime": '+Int_0) : ''; +Bool_0 = Bool_0 ? ', "keep": true' : ''; +var code = '{"type": "playBgm", "name": "'+EvalString_0+'"'+Int_0+Bool_0+'},\n'; +return code; +*/; + +pauseBgm_s + : '暂停背景音乐' Newline + + +/* pauseBgm_s +tooltip : pauseBgm: 暂停背景音乐 +helpUrl : /_docs/#/instruction +colour : this.imageColor +var code = '{"type": "pauseBgm"},\n'; +return code; +*/; + +resumeBgm_s + : '恢复背景音乐' '从暂停位置继续播放' Bool Newline + + +/* resumeBgm_s +tooltip : resumeBgm: 恢复背景音乐 +helpUrl : /_docs/#/instruction +colour : this.imageColor +Bool_0 = Bool_0 ? ', "resume": true' : ''; +var code = '{"type": "resumeBgm"' + Bool_0 + '},\n'; +return code; +*/; + +loadBgm_s + : '预加载背景音乐' EvalString Newline + + +/* loadBgm_s +tooltip : loadBgm: 预加载某个背景音乐,之后可以直接播放 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3"] +allBgms : ['EvalString_0'] +material : ["./project/bgms/", "EvalString_0"] +colour : this.imageColor +var code = '{"type": "loadBgm", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +freeBgm_s + : '释放背景音乐的缓存' EvalString Newline + + +/* freeBgm_s +tooltip : freeBgm: 释放背景音乐的缓存 +helpUrl : /_docs/#/instruction +default : ["bgm.mp3"] +allBgms : ['EvalString_0'] +colour : this.imageColor +var code = '{"type": "freeBgm", "name": "'+EvalString_0+'"},\n'; +return code; +*/; + +playSound_s + : '播放音效' EvalString '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline + + +/* playSound_s +tooltip : playSound: 播放音效 +helpUrl : /_docs/#/instruction +default : ["item.mp3",false,"",false] +colour : this.imageColor +allSounds : ['EvalString_0'] +material : ["./project/sounds/", "EvalString_0"] +if (IntString_0) { + if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。'; + IntString_0 = ', "pitch": ' + IntString_0; +} else IntString_0 = ''; +Bool_0 = Bool_0 ? ', "stop": true' : ''; +Bool_1 = Bool_1 ? ', "sync": true' : ''; +var code = '{"type": "playSound", "name": "'+EvalString_0+'"'+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +playSound_1_s + : '播放系统音效' NameMap_List '停止之前音效' Bool? '音调' IntString? '等待播放完毕' Bool? Newline + + +/* playSound_1_s +tooltip : playSound: 播放系统音效 +helpUrl : /_docs/#/instruction +default : ["确定",false,"",false] +colour : this.imageColor +if (IntString_0) { + if (parseInt(IntString_0) < 30 || parseInt(IntString_0) > 300) throw '音调设置只能在30-300之间;100为正常音调。'; + IntString_0 = ', "pitch": ' + IntString_0; +} else IntString_0 = ''; +Bool_0 = Bool_0 ? ', "stop": true' : ''; +Bool_1 = Bool_1 ? ', "sync": true' : ''; +var code = '{"type": "playSound", "name": "'+NameMap_List_0+'"'+Bool_0+IntString_0+Bool_1+'},\n'; +return code; +*/; + +stopSound_s + : '停止所有音效' Newline + + +/* stopSound_s +tooltip : stopSound: 停止所有音效 +helpUrl : /_docs/#/instruction +colour : this.imageColor +var code = '{"type": "stopSound"},\n'; +return code; +*/; + +setVolume_s + : '设置音量' Int '渐变时间' IntString? '不等待执行完毕' Bool Newline + + +/* setVolume_s +tooltip : setVolume: 设置音量 +helpUrl : /_docs/#/instruction +default : [90, 500, false] +colour : this.imageColor +IntString_0 = IntString_0 ?(', "time": '+IntString_0):''; +var async = Bool_0?', "async": true':''; +var code = '{"type": "setVolume", "value": '+Int_0+IntString_0+async+'},\n'; +return code; +*/; + +setBgmSpeed_s + : '设置背景音乐播放速度' Int '同时改变音调' Bool Newline + + +/* setBgmSpeed_s +tooltip : setSpeed: 设置背景音乐播放速度 +helpUrl : /_docs/#/instruction +default : [100, true] +colour : this.imageColor +if (Int_0 < 30 || Int_0 > 300) throw '速度只能设置只能在30-300之间;100为正常速度。'; +Bool_0 = Bool_0?', "pitch": true':''; +var code = '{"type": "setBgmSpeed", "value": '+Int_0+Bool_0+'},\n'; +return code; +*/; + +win_s + : '游戏胜利,结局' ':' EvalString? '不计入榜单' Bool '不结束游戏' Bool Newline + + +/* win_s +tooltip : win: 获得胜利, 该事件会显示获胜页面, 并重新游戏 +helpUrl : /_docs/#/instruction +default : ["",false, false] +Bool_0 = Bool_0?', "norank": 1':''; +Bool_1 = Bool_1?', "noexit": 1':''; +var code = '{"type": "win", "reason": "'+EvalString_0+'"'+Bool_0+Bool_1+'},\n'; +return code; +*/; + +lose_s + : '游戏失败,结局' ':' EvalString? Newline + + +/* lose_s +tooltip : lose: 游戏失败, 该事件会显示失败页面, 并重新开始游戏 +helpUrl : /_docs/#/instruction +default : [""] +var code = '{"type": "lose", "reason": "'+EvalString_0+'"},\n'; +return code; +*/; + +restart_s + : '直接回到标题界面' Newline + + +/* restart_s +tooltip : restart: 直接回到标题界面 +helpUrl : /_docs/#/instruction +var code = '{"type": "restart"},\n'; +return code; +*/; + +input_s + : '接受用户输入数字,提示' ':' EvalString Newline + + +/* input_s +tooltip : input:接受用户输入数字, 事件只能接受非负整数输入, 所有非法的输入将全部变成0 +helpUrl : /_docs/#/instruction +default : ["请输入一个数"] +colour : this.dataColor +var code = '{"type": "input", "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +input2_s + : '接受用户输入文本,提示' ':' EvalString Newline + + +/* input2_s +tooltip : input2:接受用户输入文本, 允许用户输入任何形式的文本 +helpUrl : /_docs/#/instruction +default : ["请输入文本"] +colour : this.dataColor +var code = '{"type": "input2", "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +if_s + : '如果' ':' expression BGNL? Newline action+ '否则' ':' BGNL? Newline action+ BEND Newline + + +/* if_s +tooltip : if: 条件判断 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "if", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"true": [\n',action_0,'],\n', + '"false": [\n',action_1,']', +'},\n'].join(''); +return code; +*/; + +if_1_s + : '如果' ':' expression BGNL? Newline action+ BEND Newline + + +/* if_1_s +tooltip : if: 条件判断 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "if", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"true": [\n',action_0,']', +'},\n'].join(''); +return code; +*/; + +switch_s + : '多重分歧 条件判定' ':' expression BGNL? Newline switchCase+ BEND Newline + + +/* switch_s +tooltip : switch: 多重条件分歧 +helpUrl : /_docs/#/instruction +default : ["判别值"] +colour : this.eventColor +var code = ['{"type": "switch", "condition": "',expression_0,'", ', + block.isCollapsed()?'"_collapsed": true, ':'', + block.isEnabled()?'':'"_disabled": true, ', + '"caseList": [\n', + switchCase_0, +'], },\n'].join(''); +return code; +*/; + +switchCase + : '如果是' expression '的场合' '不跳出' Bool BGNL? Newline action+ + + +/* switchCase +tooltip : 选项的选择 +helpUrl : /_docs/#/instruction +default : ["", false] +colour : this.subColor +Bool_0 = Bool_0?', "nobreak": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "'+expression_0+'"'+Bool_0+collapsed+disabled+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +choices_s + : '选项' ':' EvalString_Multi? BGNL? '标题' EvalString? '图像' IdString? '超时毫秒数' Int '宽度' IntString? BGNL? Newline choicesContext+ BEND Newline + + +/* choices_s +tooltip : choices: 给用户提供选项 +helpUrl : /_docs/#/instruction +previewBlock : true +default : ["","流浪者","trader",0,''] +allIds : ['IdString_0'] +var title=''; +if (EvalString_0==''){ + if (IdString_0=='')title=''; + else title='\\t['+IdString_0+']'; +} else { + if (IdString_0=='')title='\\t['+EvalString_0+']'; + else title='\\t['+EvalString_0+','+IdString_0+']'; +} +EvalString_Multi_0 = title+EvalString_Multi_0; +EvalString_Multi_0 = EvalString_Multi_0 ?(', "text": "'+EvalString_Multi_0+'"'):''; +Int_0 = Int_0 ? (', "timeout": '+Int_0) : ''; +IntString_0 = IntString_0 ? (', "width": ' + IntString_0) : ''; +var code = ['{"type": "choices"',EvalString_Multi_0,Int_0,IntString_0, + block.isCollapsed()?', "_collapsed": true':'', + block.isEnabled()?'':', "_disabled": true', + ', "choices": [\n', + choicesContext_0, +']},\n'].join(''); +return code; +*/; + +choicesContext + : '子选项' EvalString '图标' IdString? '颜色' ColorString? Colour '启用条件' EvalString? '出现条件' EvalString? BGNL? Newline action+ + + +/* choicesContext +tooltip : 选项的选择 +helpUrl : /_docs/#/instruction +default : ["提示文字:红钥匙","","","",""] +allIds : ['IdString_0'] +colour : this.subColor +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +EvalString_1 = EvalString_1 && (', "need": "'+EvalString_1+'"'); +EvalString_2 = EvalString_2 && (', "condition": "'+EvalString_2+'"'); +IdString_0 = IdString_0?(', "icon": "'+IdString_0+'"'):''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"text": "'+EvalString_0+'"'+IdString_0+ColorString_0+EvalString_1+EvalString_2+collapsed+disabled+', "action": [\n'+action_0+']},\n'; +return code; +*/; + +confirm_s + : '显示确认框' ':' EvalString_Multi '超时毫秒数' Int BGNL? '确定的场合' ':' '(默认选中' Bool ')' BGNL? Newline action+ '取消的场合' ':' BGNL? Newline action+ BEND Newline + +/* confirm_s +tooltip : 弹出确认框 +helpUrl : /_docs/#/instruction +default : ["确认要xxx吗?",0,false] +previewBlock : true +Bool_0 = Bool_0?', "default": true':'' +Int_0 = Int_0 ? (', "timeout": '+Int_0) : ''; +var code = ['{"type": "confirm"'+Int_0+Bool_0+', "text": "',EvalString_Multi_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"yes": [\n',action_0,'],\n', + '"no": [\n',action_1,']\n', +'},\n'].join(''); +return code; +*/; + +for_s + : '循环遍历' ': ' expression '从' EvalString '到' EvalString '步增' EvalString BGNL? Newline action+ BEND Newline + +/* for_s +tooltip : for:循环遍历 +helpUrl : /_docs/#/instruction +colour : this.eventColor +if (!/^temp:[A-Z]$/.test(expression_0)) { + throw new Error('循环遍历仅允许使用临时变量!'); +} +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +return '{"type": "for", "name": "'+expression_0+'", "from": "'+EvalString_0+'", "to": "'+EvalString_1+'", "step": "'+EvalString_2+'"'+collapsed+disabled+',\n"data": [\n'+action_0+']},\n'; +*/; + +forEach_s + : '循环遍历' ': 以' expression '逐项读取列表' JsonEvalString BGNL? Newline action+ BEND Newline + +/* forEach_s +tooltip : forEach:循环遍历列表 +helpUrl : /_docs/#/instruction +colour : this.eventColor +if (!/^temp:[A-Z]$/.test(expression_0)) { + throw new Error('循环遍历仅允许使用临时变量!'); +} +if (JsonEvalString_0 == '' || !(JSON.parse(JsonEvalString_0) instanceof Array)) { + throw new Error('参数列表必须是个有效的数组!'); +} +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +return '{"type": "forEach", "name": "'+expression_0+'", "list": '+JsonEvalString_0 + collapsed+disabled+',\n"data": [\n'+action_0+']},\n'; +*/; + +while_s + : '前置条件循环' ':' '当' expression '时' BGNL? Newline action+ BEND Newline + +/* while_s +tooltip : while:前置条件循环 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "while", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"data": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + +dowhile_s + : '后置条件循环' ':' BGNL? Newline action+ BEND '当' expression '时' Newline + +/* dowhile_s +tooltip : dowhile:后置条件循环 +helpUrl : /_docs/#/instruction +colour : this.eventColor +var code = ['{"type": "dowhile", "condition": "',expression_0,'",', + block.isCollapsed()?' "_collapsed": true,':'', + block.isEnabled()?'':' "_disabled": true,', + '\n"data": [\n',action_0,'],\n', +'},\n'].join(''); +return code; +*/; + +break_s + : '跳出循环或公共事件' '层数' Int Newline + +/* break_s +tooltip : break:跳出循环或公共事件! +helpUrl : /_docs/#/instruction +colour : this.eventColor +default : [1] +if (Int_0 <= 0) throw "层数至少为1!"; +var code = '{"type": "break", "n": '+Int_0+'},\n'; +return code; +*/; + +continue_s + : '提前结束循环或跳出公共事件' '层数' Int Newline + +/* continue_s +tooltip : continue:提前结束循环或跳出公共事件,或跳出公共事件! +helpUrl : /_docs/#/instruction +colour : this.eventColor +default : [1] +if (Int_0 <= 0) throw "层数至少为1!"; +var code = '{"type": "continue", "n": '+Int_0+'},\n'; +return code; +*/; + + +wait_s + : '等待用户操作并获得按键或点击信息' '仅检测子块' Bool '超时毫秒数' Int BGNL? Newline waitContext* BEND Newline + + +/* wait_s +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : [true,0] +colour : this.soundColor +Bool_0 = Bool_0?(', "forceChild": true'):''; +Int_0 = Int_0?(', "timeout": ' + Int_0):''; +waitContext_0 = waitContext_0 ? (', "data": [\n' + waitContext_0 + ']') : ''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"type": "wait"' + Bool_0 + Int_0 + collapsed + disabled + waitContext_0 + '},\n'; +return code; +*/; + + +waitContext + : waitContext_1 + | waitContext_2 + | waitContext_3 + | waitContext_4 + | waitContext_empty; + + +waitContext_1 + : '按键的场合:' '键值(右键查表)' EvalString '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_1 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +colour : this.subColor +default : ["",false] +menu : [["查询键值表", "editor_blockly.showKeyCodes()"]] +if (!/^\d+(,\d+)*$/.test(EvalString_0)) { + throw new Error('键值必须是正整数,可以以逗号分隔'); +} +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "keyboard", "keycode": "' + EvalString_0 + '"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + + +waitContext_2 + : '点击的场合:' '像素x范围' PosString '~' PosString '; y范围' PosString '~' PosString '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_2 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : [0,32,0,32,false] +previewBlock : true +colour : this.subColor +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "mouse", "px": [' + PosString_0 + ',' + PosString_1 + '], "py": [' + PosString_2 + ',' + PosString_3 + ']'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_3 + : '自定义条件的场合:' expression '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_3 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +default : ["true",false] +colour : this.subColor +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "condition", "condition": "'+expression_0+'"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_4 + : '超时的场合:' '不进行剩余判定' Bool BGNL? Newline action+ BEND Newline + +/* waitContext_4 +tooltip : wait: 等待用户操作并获得按键或点击信息 +helpUrl : /_docs/#/instruction +colour : this.subColor +default : [false] +Bool_0 = Bool_0?', "break": true':''; +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = '{"case": "timeout"'+Bool_0+collapsed+disabled+', "action": [\n' + action_0 + ']},\n'; +return code; +*/; + +waitContext_empty : Newline + +/* waitContext_empty +return ''; +*/; + + +waitAsync_s + : '等待所有异步事件执行完毕' '不等待动画' Bool '等待音效' Bool + + +/* waitAsync_s +tooltip : waitAsync: 等待所有异步事件执行完毕 +helpUrl : /_docs/#/instruction +default : [false, false] +colour : this.soundColor +Bool_0 = Bool_0 ? ', "excludeAnimates": true' : ''; +Bool_1 = Bool_1 ? ', "includeSounds": true' : ''; +var code = '{"type": "waitAsync"'+Bool_0+Bool_1+'},\n'; +return code; +*/; + + +stopAsync_s + : '立刻结束所有异步事件' BGNL Newline + + +/* stopAsync_s +tooltip : stopAsync: 立刻结束所有异步事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "stopAsync"},\n'; +return code; +*/; + + +callBook_s + : '呼出怪物手册' + + +/* callBook_s +tooltip : callBook: 呼出怪物手册;返回游戏后将继续执行后面的事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callBook"},\n'; +return code; +*/; + + +callSave_s + : '呼出存档页面' + + +/* callSave_s +tooltip : callSave: 呼出存档页面 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callSave"},\n'; +return code; +*/; + + +autoSave_s + : '自动存档' '读档到触发前' Bool Newline + + +/* autoSave_s +tooltip : autoSave: 自动存档 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0 ? (', "removeLast": true') : ''; +var code = '{"type": "autoSave"'+Bool_0+'},\n'; +return code; +*/; + + +forbidSave_s + : '是否禁止存档' Bool Newline + + +/* forbidSave_s +tooltip : forbidSave: 禁止存档 +helpUrl : /_docs/#/instruction +colour : this.soundColor +default : [false] +Bool_0 = Bool_0 ? (', "forbid": true') : ''; +var code = '{"type": "forbidSave"'+Bool_0+'},\n'; +return code; +*/; + + +callLoad_s + : '呼出读档页面' Newline + + +/* callLoad_s +tooltip : callLoad: 呼出存档页面;返回游戏后将继续执行后面的事件 +helpUrl : /_docs/#/instruction +colour : this.soundColor +var code = '{"type": "callLoad"},\n'; +return code; +*/; + + +previewUI_s + : 'ui绘制并预览' '(双击此项可进行预览)' BGNL? Newline action+ BEND Newline + + +/* previewUI_s +tooltip : previewUI: ui绘制并预览 +helpUrl : /_docs/#/instruction +previewBlock : true +var collapsed=block.isCollapsed()?', "_collapsed": true':''; +var disabled=block.isEnabled()?'':', "_disabled": true'; +var code = ['{"type": "previewUI"'+collapsed+disabled+', "action": [\n', action_0,']},\n'].join(''); +return code; +*/; + + +clearMap_s + : '清除画布' '起点像素' 'x' PosString? 'y' PosString? '宽' PosString? '高' PosString? Newline + +/* clearMap_s +tooltip : clearMap: 清除画布 +helpUrl : /_docs/#/instruction +colour : this.uiColor +default : ["", "", "", ""] +previewBlock : true +PosString_0 = PosString_0 && (', "x": ' + PosString_0); +PosString_1 = PosString_1 && (', "y": ' + PosString_1); +PosString_2 = PosString_2 && (', "width": ' + PosString_2); +PosString_3 = PosString_3 && (', "height": ' + PosString_3); +var code = '{"type": "clearMap"'+PosString_0+PosString_1+PosString_2+PosString_3+'},\n'; +return code; +*/; + + +setAttribute_s + : '设置画布属性' '字体' FontString? '填充样式' ColorString? Colour '边框样式' ColorString? Colour BGNL? '线宽度' IntString? '不透明度' EvalString? '对齐' TextAlign_List '基准线' TextBaseline_List 'z值' IntString? Newline + +/* setAttribute_s +tooltip : setAttribute:设置画布属性 +helpUrl : /_docs/#/instruction +previewBlock : true +colour : this.uiColor +default : ["","",'rgba(255,255,255,1)',"",'rgba(255,255,255,1)',"","",null,null,""] +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +TextBaseline_List_0 = TextBaseline_List_0==='null'?'': ', "baseline": "'+TextBaseline_List_0+'"'; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +ColorString_0 = ColorString_0 ? (', "fillStyle": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "strokeStyle": ['+ColorString_1+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +if (EvalString_0) { + var f = parseFloat(EvalString_0); + if (isNaN(f) || f<0 || f>1) throw new Error('不透明度必须是0到1的浮点数或不填'); + EvalString_0 = ', "alpha": '+EvalString_0; +} +IntString_1 = IntString_1 ? (', "z": '+IntString_1) : ''; +var code = '{"type": "setAttribute"'+FontString_0+ColorString_0+ColorString_1+IntString_0+ + EvalString_0+TextAlign_List_0+TextBaseline_List_0+IntString_1+'},\n'; +return code; +*/; + + +setFilter_s + : '设置画布特效' '虚化' Number '色相' Int '灰度' Number '反色' Bool '阴影' Number Newline + + +/* setFilter_s +tooltip : setFilter: 设置画布特效 +helpUrl : /_docs/#/instruction +default : [0,0,0,false,0] +previewBlock : true +colour : this.uiColor +if (Number_0 < 0) throw '虚化不得小于0;0为完全没有虚化'; +if (Int_0 < 0 || Int_0 >= 360) throw '色相需要在0~359之间'; +if (Number_1 < 0 || Number_1 > 1) throw '灰度需要在0~1之间'; +if (Number_2 < 0) throw '阴影不得小于0;0为完全没有阴影'; +var code = '{"type": "setFilter", "blur": '+Number_0+', "hue": '+Int_0+', "grayscale": '+Number_1+', "invert": '+Bool_0+', "shadow": '+Number_2+'},\n'; +return code; +*/; + + +fillText_s + : '绘制文本' 'x' PosString 'y' PosString '样式' ColorString? Colour '字体' FontString? '最大宽度' IntString? BGNL? EvalString Newline + +/* fillText_s +tooltip : fillText:绘制一行文本;可以设置最大宽度进行放缩 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","",'rgba(255,255,255,1)',"","","绘制一行文本"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +IntString_0 = IntString_0 ? (', "maxWidth": '+IntString_0) : ''; +var code = '{"type": "fillText", "x": '+PosString_0+', "y": '+PosString_1+ColorString_0+FontString_0+IntString_0+', "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +fillBoldText_s + : '绘制描边文本' 'x' PosString 'y' PosString '样式' ColorString? Colour '描边颜色' ColorString? Colour '字体' FontString? BGNL? EvalString Newline + +/* fillBoldText_s +tooltip : fillBoldText:绘制一行描边文本 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","",'rgba(255,255,255,1)',"",'rgba(0,0,0,1)',"","绘制一行描边文本"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +ColorString_1 = ColorString_1 ? (', "strokeStyle": ['+ColorString_1+']') : ''; +FontString_0 = FontString_0 ? (', "font": "' + FontString_0 + '"') : ''; +var code = '{"type": "fillBoldText", "x": '+PosString_0+', "y": '+PosString_1+ColorString_0+ColorString_1+FontString_0+', "text": "'+EvalString_0+'"},\n'; +return code; +*/; + +drawTextContent_s + : '绘制多行文本' EvalString_Multi BGNL? '起点像素' 'x' PosString 'y' PosString '最大宽度' IntString? '颜色' ColorString? Colour BGNL? '对齐' TextAlign_List '字体大小' IntString? '行距' IntString? '粗体' Bool Newline + +/* drawTextContent_s +tooltip : drawTextContent:绘制多行文本 +helpUrl : /_docs/#/instruction +doubleclicktext : EvalString_Multi_0 +menu : [['预览多行文本','editor_blockly.previewBlock(block)']] +colour : this.uiColor +default : ["绘制多行文本\\n可双击编辑","0","0","","",'rgba(255,255,255,1)',null,"","",false] +TextAlign_List_0 = TextAlign_List_0==='null'?'': ', "align": "'+TextAlign_List_0+'"'; +Bool_0 = Bool_0 ? (', "bold": true') : ''; +IntString_0 = IntString_0 ? (', "maxWidth": '+IntString_0) : ''; +IntString_1 = IntString_1 ? (', "fontSize": '+IntString_1) : ''; +IntString_2 = IntString_2 ? (', "lineHeight": '+IntString_2) : ''; +ColorString_0 = ColorString_0 ? (', "color": ['+ColorString_0+']') : ''; +var code = '{"type": "drawTextContent", "text": "'+EvalString_Multi_0+'", "left": '+PosString_0+', "top": '+PosString_1+TextAlign_List_0+IntString_0+IntString_1+IntString_2+ColorString_0+Bool_0+'},\n'; +return code; +*/; + +fillRect_s + : '绘制矩形' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '圆角半径' PosString? '旋转度数' PosString? '颜色' ColorString? Colour Newline + +/* fillRect_s +tooltip : fillRect:绘制矩形 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","","","rgba(255,255,255,1)"] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +PosString_4 = PosString_4 ? (', "radius": '+PosString_4) : ''; +PosString_5 = PosString_5 ? (', "angle": ' + PosString_5) : ''; +var code = '{"type": "fillRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+PosString_4+PosString_5+ColorString_0+'},\n'; +return code; +*/; + +strokeRect_s + : '绘制矩形边框' '起点像素' 'x' PosString 'y' PosString '宽' PosString '高' PosString '圆角半径' PosString? '旋转度数' PosString? '颜色' ColorString? Colour '线宽' IntString? Newline + +/* strokeRect_s +tooltip : strokeRect:绘制矩形边框 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +PosString_4 = PosString_4 ? (', "radius": '+PosString_4) : ''; +PosString_5 = PosString_5 ? (', "angle": ' + PosString_5) : ''; +var code = '{"type": "strokeRect", "x": '+PosString_0+', "y": '+PosString_1+', "width": '+PosString_2+', "height": '+PosString_3+PosString_4+PosString_5+ColorString_0+IntString_0+'},\n'; +return code; +*/; + +drawLine_s + : '绘制线段' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' ColorString? Colour '线宽' IntString? Newline + +/* drawLine_s +tooltip : drawLine:绘制线段 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +var code = '{"type": "drawLine", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+ColorString_0+IntString_0+'},\n'; +return code; +*/; + +drawArrow_s + : '绘制箭头' '起点像素' 'x' PosString 'y' PosString '终点像素' 'x' PosString 'y' PosString '颜色' ColorString? Colour '线宽' IntString? Newline + +/* drawArrow_s +tooltip : drawArrow:绘制箭头 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0","0","flag:x","300","","rgba(255,255,255,1)",""] +ColorString_0 = ColorString_0 ? (', "style": ['+ColorString_0+']') : ''; +IntString_0 = IntString_0 ? (', "lineWidth": '+IntString_0) : ''; +var code = '{"type": "drawArrow", "x1": '+PosString_0+', "y1": '+PosString_1+', "x2": '+PosString_2+', "y2": '+PosString_3+ColorString_0+IntString_0+'},\n'; +return code; +*/; + + +fillPolygon_s + : '绘制多边形' '顶点像素列表' 'x' EvalString 'y' EvalString '颜色' ColorString? Colour Newline + +/* fillPolygon_s +tooltip : fillPolygon:绘制多边形 +helpUrl : /_docs/#/instruction +colour : this.uiColor +previewBlock : true +default : ["0,0,100","0,100,0","","rgba(255,255,255,1)"] +var pattern2 = /^([+-]?\d+)(,[+-]?\d+)*$/; +if(!pattern2.test(EvalString_0) || !pattern2.test(EvalString_1))throw new Error('坐标格式错误,请右键点击帮助查看格式'); +EvalString_0=EvalString_0.split(','); +EvalString_1=EvalString_1.split(','); +if(EvalString_0.length!==EvalString_1.length)throw new Error('坐标格式错误,请右键点击帮助查看格式'); +for(var ii=0;ii': Blockly.JavaScript.ORDER_RELATIONAL, + '<': Blockly.JavaScript.ORDER_RELATIONAL, + '>=': Blockly.JavaScript.ORDER_RELATIONAL, + '<=': Blockly.JavaScript.ORDER_RELATIONAL, + '&&': Blockly.JavaScript.ORDER_LOGICAL_AND, + '||': Blockly.JavaScript.ORDER_LOGICAL_OR, + '^': Blockly.JavaScript.ORDER_BITWISE_XOR, + 'min': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'max': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'startsWith': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'endsWith': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA + 'includes': Blockly.JavaScript.ORDER_MEMBER, //recieveOrder : ORDER_COMMA +} +return [code, orders[Arithmetic_List_0]]; +*/; + +negate_e + : '非' expression + + +/* negate_e +//todo 修改recieveOrder : ORDER_LOGICAL_NOT 修改 inputsInline +var code = '!'+expression_0; +return [code, Blockly.JavaScript.ORDER_LOGICAL_NOT]; +*/; + +unaryOperation_e + : UnaryOperator_List expression + + +/* unaryOperation_e +var code = UnaryOperator_List_0 + '(' + expression_0 + ')'; +return [code, Blockly.JavaScript.ORDER_MEMBER]; +*/; + + +bool_e + : ':' Bool + + +/* bool_e +tooltip : 逻辑是否 +var code = Bool_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idString_e + : IdString + + +/* idString_e +colour : this.idstring_eColor +default : ["变量:生命"] +var code = IdString_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +idIdList_e + : Id_List ':' IdText + + +/* idIdList_e +colour : this.idstring_eColor +default : [null,"自定义flag"] +var code = MotaActionFunctions.replaceFromName(MotaActionFunctions.replaceToName(Id_List_0+':'+IdText_0)); +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +idFixedList_e + : FixedId_List + + +/* idFixedList_e +colour : this.idstring_eColor +var code = FixedId_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +enemyattr_e + : '怪物' IdString '的' EnemyId_List + + +/* enemyattr_e +default : ['greenSlime',"hp"] +allEnemys : ['IdString_0'] +var code = 'enemy:'+IdString_0+':'+EnemyId_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockId_e + : '图块ID:' PosString ',' PosString + + +/* blockId_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockId:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockId('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockNumber_e + : '图块数字:' PosString ',' PosString + + +/* blockNumber_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockNumber:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockNumber('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +blockCls_e + : '图块类别:' PosString ',' PosString + + +/* blockCls_e +default : [0,0] +if (/^\d+$/.test(PosString_0) && /^\d+$/.test(PosString_1)) { + return ['blockCls:'+PosString_0+','+PosString_1, Blockly.JavaScript.ORDER_ATOMIC]; +} +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +return ['core.getBlockCls('+PosString_0+','+PosString_1+')', Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +nextXY_e + : '前方' NInt '格的' NextXY_List + +/* nextXY_e +default : [1, 'nextX'] +var code = NextXY_List_0 == 'nextY' ? ('core.nextY('+NInt_0+')') : ('core.nextX('+NInt_0+')'); +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +isReplaying_e + : '录像播放中' + +/* isReplaying_e +var code = 'core.isReplaying()'; +return [code, Blockly.JavaScript.ORDER_ATOMIC];; +*/; + + +hasVisitedFloor_e + : '访问过楼层' IdString + +/* hasVisitedFloor_e +default : ['MT0'] +allFloorIds : ['IdString_0'] +var code = 'core.hasVisitedFloor(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +isShopVisited_e + : '开启过商店' IdString + +/* isShopVisited_e +default : ['shop1'] +allShops : ['IdString_0'] +var code = 'core.isShopVisited(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +hasEquip_e + : '当前正在装备' IdString + +/* hasEquip_e +default : ['sword1'] +allEquips : ['IdString_0'] +var code = 'core.hasEquip(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +canBattle_e + : '当前能否战斗' IdString + +/* canBattle_e +default : ['greenSlime'] +allEnemys : ['IdString_0'] +var code = 'core.canBattle(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +damage_e + : '战斗伤害' '怪物ID' IdString + +/* damage_e +default : ['greenSlime'] +allEnemys : ['IdString_0'] +var code = 'core.getDamage(\'' + IdString_0 + '\')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +damage_1_e + : '战斗伤害' '点坐标' 'x' PosString 'y' PosString + +/* damage_1_e +default : [0, 0] +if (PosString_0.startsWith('"')) { + PosString_0 = PosString_0.substring(1, PosString_0.length - 1); +} +if (PosString_1.startsWith('"')) { + PosString_1 = PosString_1.substring(1, PosString_1.length - 1); +} +var code = 'core.getDamage(null, ' + PosString_0 + ',' + PosString_1 + ')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +rand_e + : '随机数 [0, ' Int ')' + +/* rand_e +default : ['10'] +var code = 'core.rand(' + Int_0 + ')'; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +equip_e + : '第' Int '格装备孔' + + +/* equip_e +default : [0] +var code = 'equip:'+Int_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idFlag_e + : '独立开关' Letter_List + + +/* idFlag_e +colour : this.idstring_eColor +default : ["A"] +var code = "switch:"+Letter_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +idTemp_e + : '临时变量' Letter_List + + +/* idTemp_e +colour : this.idstring_eColor +default : ["A"] +var code = "temp:"+Letter_List_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + + +evalString_e + : EvalString_Multi + + +/* evalString_e +default : ["值"] +var code = EvalString_Multi_0; +return [code, Blockly.JavaScript.ORDER_ATOMIC]; +*/; + +//===============lexer=============== + +IdText + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +RawEvalString + : 'sdeirughvuiyasdbe'+ //为了被识别为复杂词法规则 + ; + +JsonEvalString + : 'sdeirughvuiyasdbe'+ //为了被识别为复杂词法规则 + ; + +PosString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +IntString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +ColorString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +FontString + : 'sdeirughvuiyasbde'+ //为了被识别为复杂词法规则 + ; + +Floor_List + : '楼层ID'|'前一楼'|'后一楼'|'当前楼' + /*Floor_List ['floorId',':before',':next',':now']*/; + +Stair_List + : '坐标'|'上楼梯'|'下楼梯'|'保持不变'|'中心对称点'|'x对称点'|'y对称点'|'楼传落点' + /*Stair_List ['loc','upFloor','downFloor',':now',':symmetry',':symmetry_x',':symmetry_y','flyPoint']*/; + +SetTextPosition_List + : '不改变'|'距离顶部'|'居中'|'距离底部' + /*SetTextPosition_List ['null','up','center','down']*/; + +TextAlign_List + : '不改变'|'左对齐'|'左右居中'|'右对齐' + /*TextAlign_List ['null','left','center','right']*/; + +TextBaseline_List + : '不改变'|'顶部'|'悬挂'|'居中'|'标准值'|'ideographic'|'底部' + /*TextBaseline_List ['null','top','hanging','middle','alphabetic','ideographic','bottom']*/; + +Reverse_List + : '不改变'|'左右翻转'|'上下翻转'|'中心翻转' + /*Reverse_List ['null',':x',':y',':o']*/; + +ShopUse_List + : '金币' | '经验' + /*ShopUse_List ['money','exp']*/; + +Arithmetic_List + : '加'|'减'|'乘'|'除'|'取余'|'乘方'|'等于'|'不等于'|'大于'|'小于'|'大于等于'|'小于等于'|'且'|'或'|'异或'|'取较大'|'取较小'|'弱相等'|'弱不相等'|'开始于'|'结束于'|'包含' + /*Arithmetic_List ['+','-','*','/','%','**','===','!==','>','<','>=','<=','&&','||','^','max','min','==','!=','startsWith','endsWith','includes']*/; + +AssignOperator_List + : '设为'|'增加'|'减少'|'乘以'|'除以'|'乘方'|'除以并取商'|'除以并取余'|'设为不小于'|'设为不大于' + /*AssignOperator_List ['=','+=','-=','*=','/=','**=','//=','%=','max=','min=']*/; + +UnaryOperator_List + : '向下取整'|'向上取整'|'四舍五入'|'整数截断'|'绝对值'|'开方'|'变量类型' + /*UnaryOperator_List ['Math.floor', 'Math.ceil', 'Math.round', 'Math.trunc', 'Math.abs', 'Math.sqrt', 'typeof']*/; + +Weather_List + : '无'|'雨'|'雪'|'晴'|'雾'|'云' + /*Weather_List ['null','rain','snow','sun','fog','cloud']*/; + +B_0_List + : '不改变'|'不可通行'|'可以通行' + /*B_0_List ['null','true','false']*/; + +B_1_List + : '不改变'|'设为粗体'|'取消粗体' + /*B_1_List ['null','true','false']*/; + +Bg_Fg_List + : '背景层'|'前景层' + /*Bg_Fg_List ['bg','fg']*/; + +Bg_Fg2_List + : '背景层'|'前景层'|'自适配' + /*Bg_Fg2_List ['bg','fg','auto']*/; + +IgnoreChangeFloor_List + : '全局默认值' | '可穿透' | '不可穿透' + /*IgnoreChangeFloor_List ['null','true','false']*/; + +Event_List + : '普通事件'|'战前事件'|'战后事件'|'道具后事件'|'开门后事件' + /*Event_List ['null','beforeBattle','afterBattle','afterGetItem','afterOpenDoor']*/; + +Floor_Meta_List + : '楼层中文名'|'状态栏名称'|'能否楼传飞到'|'能否楼传飞出'|'能否打开快捷商店'|'是否不可浏览地图'|'是否不可瞬间移动'|'默认地面ID'|'宝石血瓶效果'|'上楼点坐标'|'下楼点坐标'|'楼传落点坐标'|'背景音乐'|'画面色调'|'天气和强度'|'是否地下层' + /*Floor_Meta_List ['title','name','canFlyTo', 'canFlyFrom', 'canUseQuickShop', 'cannotViewMap', 'cannotMoveDirectly', 'defaultGround', 'ratio', 'upFloor', 'downFloor', 'flyPoint', 'bgm', 'color', 'weather', 'underGround']*/; + +Global_Attribute_List + : '全局字体'|'横屏左侧状态栏背景'|'竖屏上方状态栏背景'|'竖屏下方道具栏背景'|'边框颜色'|'状态栏文字色'|'选中框颜色'|'楼层转换样式'|'装备列表' + /*Global_Attribute_List ['font','statusLeftBackground','statusTopBackground', 'toolsBackground', 'borderColor', 'statusBarColor', 'selectColor', 'floorChangingStyle', 'equipName']*/; + +Global_Value_List + : '血网伤害'|'中毒伤害'|'衰弱效果'|'红宝石效果'|'蓝宝石效果'|'绿宝石效果'|'红血瓶效果'|'蓝血瓶效果'|'黄血瓶效果'|'绿血瓶效果'|'破甲比例'|'反击比例'|'净化比例'|'仇恨增加值'|'图块每帧时间'|'上下楼时间' + /*Global_Value_List ['lavaDamage','poisonDamage','weakValue', 'redGem', 'blueGem', 'greenGem', 'redPotion', 'bluePotion', 'yellowPotion', 'greenPotion', 'breakArmor', 'counterAttack', 'purify', 'hatred', 'animateSpeed', 'floorChangeTime']*/; + + +Global_Flag_List + : '显示当前楼层'|'显示勇士图标'|'显示当前等级'|'启用生命上限'|'显示生命值'|'显示魔力值'|'显示攻击力'|'显示防御力'|'显示护盾值'|'显示金币值'|'显示经验值'|'允许等级提升'|'升级扣除模式'|'显示钥匙数量'|'显示绿钥匙'|'显示破炸飞'|'显示毒衰咒'|'显示当前技能'|'横屏底部工具栏'|'楼梯边才能楼传'|'楼传平面塔模式'|'开启加点'|'开启负伤'|'夹击不超伤害值'|'二分计算临界'|'允许轻按'|'允许走到将死领域'|'允许瞬间移动'|'阻激夹域后禁用快捷商店'|'虚化前景层' + /*Global_Flag_List ['s:enableFloor','s:enableName','s:enableLv', 's:enableHPMax', 's:enableHP', 's:enableMana', 's:enableAtk', 's:enableDef', 's:enableMDef', 's:enableMoney', 's:enableExp', 's:enableLevelUp', 's:levelUpLeftMode', 's:enableKeys', 's:enableGreenKey', 's:enablePZF', 's:enableDebuff', 's:enableSkill', 'extendToolbar', 'flyNearStair', 'flyRecordPosition', 'enableAddPoint', 'enableNegativeDamage', 'betweenAttackMax', 'useLoop', 'enableGentleClick', 'canGoDeadZone', 'enableMoveDirectly', 'disableShopOnDamage', 'blurFg']*/; + +NextXY_List + : '横坐标'|'纵坐标' + /*NextXY_List ['nextX','nextY']*/; + +EquipValueType_List + : '数值项'|'百分比项' + /*EquipValueType_List ['value','percentage']*/; + +Vibrate_List + : '左右'|'上下'|'左上-右下'|'左下-右上'|'随机' + /*Vibrate_List ['horizontal','vertical','diagonal1','diagonal2','random']*/; + +Colour + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +Angle + : 'sdeirughvuiyasdeb'+ //为了被识别为复杂词法规则 + ; + +Bool: 'TRUE' + | 'FALSE' + ; + +Int : '0' | [1-9][0-9]* ; // no leading zeros + +NInt : '0' | '-'? [1-9][0-9]* ; + +Letter_List + : 'A'|'B'|'C'|'D'|'E'|'F'|'G'|'H'|'I'|'J'|'K'|'L'|'M'|'N'|'O'|'P'|'Q'|'R'|'S'|'T'|'U'|'V'|'W'|'X'|'Y'|'Z' + /*Letter_List ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']*/; + + +Number + : '-'? Int '.' Int EXP? // 1.35, 1.35E-9, 0.3, -4.5 + | '-'? Int EXP // 1e10 -3e4 + | '-'? Int // -3, 45 + ; +fragment EXP : [Ee] [+\-]? Int ; // \- since - means "range" inside [...] + +Direction_List + : '上'|'下'|'左'|'右' + /*Direction_List ['up','down','left','right']*/; + +DirectionEx_List + : '不变'|'朝上'|'朝下'|'朝左'|'朝右'|'左转'|'右转'|'背对'|'角色同向'|'角色反向' + /*DirectionEx_List ['null','up','down','left','right',':left',':right',':back',':hero',':backhero']*/; + +StepString + : (Direction_List Int?)+ + ; + +IdString + : [0-9a-zA-Z_][0-9a-zA-Z_:]* + ; + +FixedId_List + : '生命'|'生命上限'|'攻击'|'防御'|'护盾'|'黄钥匙'|'蓝钥匙'|'红钥匙'|'金币'|'经验'|'魔力'|'魔力上限'|'当前横坐标'|'当前纵坐标'|'当前朝向'|'攻击增益'|'防御增益'|'护盾增益' + /*FixedId_List ['status:hp','status:hpmax','status:atk','status:def','status:mdef','item:yellowKey','item:blueKey','item:redKey','status:money','status:exp','status:mana','status:manamax','status:x','status:y','status:direction','buff:atk','buff:def','buff:mdef']*/; + +Id_List + : '变量' | '状态' | '物品' | '增益' | '独立开关' | '临时变量' |'全局存储' + /*Id_List ['flag','status','item', 'buff', 'switch', 'temp', 'global']*/; + +EnemyId_List + : '生命'|'攻击'|'防御'|'金币'|'经验'|'加点'|'特殊属性'|'名称'|'映射名'|'属性值'|'退化扣攻'|'退化扣防'|'不可炸'|'九宫格领域'|'领域范围'|'连击数'|'吸血到自身'|'固伤值' + /*EnemyId_List ['hp','atk','def','money','exp','point','special','name','displayInBook','value','atkValue','defValue','notBomb','zoneSquare','range','n','add','damage']*/; + +EnemyPoint_List + : '生命'|'攻击'|'防御'|'金币'|'经验'|'加点'|'名称' + /*EnemyPoint_List ['hp','atk','def','money','exp','point','name']*/; + +Equip_List + : '生命'|'生命上限'|'攻击'|'防御'|'护盾'|'魔力'|'魔力上限' + /*Equip_List ['hp','hpmax','atk','def','mdef','mana','manamax']*/; + +Key_List + : '黄钥匙'|'蓝钥匙'|'红钥匙'|'绿钥匙'|'铁门钥匙' + /*Key_List ['yellowKey','blueKey','redKey','greenKey','steelKey']*/; + +Move_List + : '上'|'下'|'左'|'右'|'前'|'后'|'左上'|'左下'|'右上'|'右下'|'设置速度' + /*Move_List ['up','down','left','right','forward','backward','leftup','leftdown','rightup','rightdown','speed']*/; + +MoveMode_List + : '匀速移动'|'缓入快出'|'快入缓出'|'缓入缓出'|'随机' + /*MoveMode_List ['', 'easeIn', 'easeOut', 'easeInOut', 'random']*/; + +NameMap_List + : '确定'|'取消'|'操作失败'|'光标移动'|'打开界面'|'读档'|'存档'|'获得道具'|'回血'|'宝石'|'炸弹'|'飞行器'|'开关门'|'上下楼'|'跳跃'|'破墙镐'|'破冰镐'|'阻激夹域'|'穿脱装备'|'商店' + /*NameMap_List ['确定','取消','操作失败','光标移动','打开界面','读档','存档','获得道具','回血','宝石','炸弹','飞行器','开关门','上下楼','跳跃','破墙镐','破冰镐','阻激夹域','穿脱装备','商店']*/; + +//转blockly后不保留需要加" +EvalString + : Equote_double (ESC_double | ~["\\])* Equote_double + ; + +EvalString_Multi + : Equote_double (ESC_double | ~["\\])* Equote_double + ; + +fragment ESC_double : '\\' (["\\/bfnrt] | UNICODE) ; +fragment UNICODE : 'u' HEX HEX HEX HEX ; +fragment HEX : [0-9a-fA-F] ; + +BGNL + : 'BGNLaergayergfuybgv' + ; + +MeaningfulSplit : '=== meaningful ^ ===' ; + +fragment Equote_double : '"' ; + +BSTART + : '开始' + ; + +BEND: '结束' + ; + +Newline + : ('\r' '\n'?| '\n')// -> skip + ; + +WhiteSpace + : [ \t]+ -> skip + ; + +BlockComment + : '/*' .*? '*/' -> skip + ; + +LineComment + : '//' ~[\r\n]* -> skip + ; + +/* Function_0 +//this.evisitor.recieveOrder='ORDER_NONE'; +this.evisitor.valueColor=330; +this.evisitor.statementColor=70; +this.evisitor.entryColor=250; + +this.evisitor.idstring_eColor=310; +this.evisitor.subColor=250; +this.evisitor.dataColor=130; +this.evisitor.eventColor=220; +this.evisitor.soundColor=20; +this.evisitor.commentColor=285; +this.evisitor.mapColor=175; +this.evisitor.uiColor=359; +this.evisitor.imageColor=45; +*/ + +/* Function_1 +delete(this.block('negate_e').inputsInline); +this.block('idIdList_e').output='idString_e'; +this.block('idFixedList_e').output='idString_e'; +this.block('idFlag_e').output='idString_e'; +this.block('idTemp_e').output='idString_e'; +*/ + +/* Functions + +MotaActionParser() + +*/ diff --git a/_server/MotaActionParser.js b/_server/MotaActionParser.js new file mode 100644 index 0000000..1b372fc --- /dev/null +++ b/_server/MotaActionParser.js @@ -0,0 +1,1737 @@ +MotaActionParser=function(){ +function ActionParser(){ +} + +ActionParser.prototype.parse = function (obj,type) { + switch (type) { + case 'event': + if(!obj)obj={}; + if(typeof(obj)===typeof('')) obj={'data':[obj]}; + if(obj instanceof Array) obj={'data':obj}; + if (!obj.filter) obj.filter={}; + return MotaActionBlocks['event_m'].xmlText([ + obj.trigger==='action',obj.enable,obj.noPass,obj.displayDamage,obj.opacity, + obj.filter.blur,obj.filter.hue,obj.filter.grayscale,obj.filter.invert,obj.filter.shadow,this.parseList(obj.data) + ]); + + case 'autoEvent': + if(!obj)obj={}; + return MotaActionBlocks['autoEvent_m'].xmlText([ + obj.condition,obj.priority,obj.currentFloor,obj.delayExecute,obj.multiExecute,this.parseList(obj.data) + ]); + + case 'changeFloor': + if(!obj)obj={}; + if (!obj.loc) { + obj.loc = obj.loc || ['','']; + obj.stair = obj.stair || ':now'; + } + if (obj.floorId==':before'||obj.floorId==':next'||obj.floorId==':now') { + obj.floorType=obj.floorId; + delete obj.floorId; + } + return MotaActionBlocks['changeFloor_m'].xmlText([ + obj.floorType||'floorId',obj.floorId,obj.stair||'loc',obj.loc[0],obj.loc[1],obj.direction, + obj.time,obj.ignoreChangeFloor + ]); + + case 'afterGetItem': + if (!obj) obj = []; + if (obj instanceof Array) obj = {'data': obj}; + return MotaActionBlocks['afterGetItem_m'].xmlText([ + obj.disableOnGentleClick||false, this.parseList(obj.data) + ]); + + case 'level': + if(!obj)obj={}; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + text_choices=MotaActionBlocks['levelCase'].xmlText([ + this.expandEvalBlock([choice.need]),choice.title,choice.clear||false,this.parseList(choice.action),text_choices]); + } + return MotaActionBlocks['level_m'].xmlText([text_choices]); + + case 'levelChoose': + if(!obj) obj=[]; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + text_choices=MotaActionBlocks['levelChooseChoice'].xmlText([ + choice.title, choice.name, choice.hard||0, choice.color, 'rgba('+choice.color+')', this.parseList(choice.action), text_choices]); + } + return MotaActionBlocks['levelChoose_m'].xmlText([text_choices]); + + case 'floorPartition': + if(!obj) obj=[]; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + text_choices=MotaActionBlocks['floorPartitionItem'].xmlText([choice[0], choice[1]||"", text_choices]); + } + return MotaActionBlocks['floorPartition_m'].xmlText([text_choices]); + + case 'equip': + if(!obj) obj={}; + var buildEquip = function (obj) { + obj = obj || {}; + var text_choices = null; + var knownEquipListKeys = MotaActionBlocks['Equip_List'].options.map(function (one) {return one[1];}) + Object.keys(obj).sort().forEach(function (key) { + var one = knownEquipListKeys.indexOf(key) >= 0 ? 'equipKnown' : 'equipUnknown'; + text_choices = MotaActionBlocks[one].xmlText([ + key, obj[key], text_choices + ]); + }) + return text_choices; + } + return MotaActionBlocks['equip_m'].xmlText([obj.type, obj.animate, buildEquip(obj.value), buildEquip(obj.percentage), + this.parseList(obj.equipEvent), this.parseList(obj.unequipEvent)]); + + case 'doorInfo': + if(!obj) obj={}; + var buildKeys = function (obj) { + obj = obj || {}; + var text_choices = null; + var knownListKeys = MotaActionBlocks['Key_List'].options.map(function (one) {return one[1];}) + Object.keys(obj).sort().forEach(function (key) { + var noNeed = key.endsWith(':o'); + if (noNeed) key = key.substring(0, key.length - 2); + var one = knownListKeys.indexOf(key) >= 0 ? 'doorKeyKnown' : 'doorKeyUnknown'; + text_choices = MotaActionBlocks[one].xmlText([ + one == 'doorKeyUnknown' ? MotaActionFunctions.replaceToName_token(key) : key, obj[key], noNeed, text_choices + ]); + }) + return text_choices; + } + return MotaActionBlocks['doorInfo_m'].xmlText([obj.time || 160, obj.openSound, obj.closeSound, buildKeys(obj.keys), this.parseList(obj.afterOpenDoor)]); + + case 'floorImage': + if(!obj) obj=[]; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + text_choices=MotaActionBlocks['floorOneImage'].xmlText([ + choice.name, choice.reverse, choice.canvas||'bg', choice.x||0, choice.y||0, choice.disable||false, + choice.sx, choice.sy, choice.w, choice.h, choice.frame, text_choices]); + } + return MotaActionBlocks['floorImage_m'].xmlText([text_choices]); + + case 'faceIds': + if(!obj) obj={}; + return MotaActionBlocks['faceIds_m'].xmlText([obj.down||"", obj.left||"", obj.right||"", obj.up||""]); + + case 'splitImages': + if(!obj) obj=[]; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + text_choices=MotaActionBlocks['splitImagesOne'].xmlText([ + choice.name, choice.width, choice.height, choice.prefix, text_choices]); + } + return MotaActionBlocks['splitImages_m'].xmlText([text_choices]); + + case 'mainStyle': + if(!obj) obj={}; + return MotaActionBlocks['mainStyle_m'].xmlText([ + obj.startBackground, obj.startVerticalBackground || obj.startBackground, obj.startLogoStyle, obj.startButtonsStyle, + obj.statusLeftBackground, obj.statusTopBackground, obj.toolsBackground, obj.floorChangingStyle, + obj.statusBarColor, 'rgba('+obj.statusBarColor+')', obj.borderColor, 'rgba('+obj.borderColor+')', + obj.selectColor, 'rgba(' + obj.selectColor + ')', obj.font + ]); + + case 'nameMap': + if (!obj) obj={}; + var items = Object.keys(obj); + var result = null; + for (var ii=items.length-1,one;one=items[ii];ii--) { + var value = obj[one]; + var knownItems = MotaActionBlocks['NameMap_List'].options.map(function (one) {return one[1];}) + if (knownItems.indexOf(one) >= 0) { + result = MotaActionBlocks['nameMapSoundKnown'].xmlText([one, value, result]); + continue; + } + if (main.bgms.indexOf(value) >= 0) { + result = MotaActionBlocks['nameMapBgm'].xmlText([one, value, result]); + continue; + } + if (main.sounds.indexOf(value) >= 0) { + result = MotaActionBlocks['nameMapSoundUnknown'].xmlText([one, value, result]); + continue; + } + if (main.images.indexOf(value) >= 0) { + result = MotaActionBlocks['nameMapImage'].xmlText([one, value, result]); + continue; + } + if (main.animates.indexOf(value) >= 0) { + result = MotaActionBlocks['nameMapAnimate'].xmlText([one, value, result]); + continue; + } + result = MotaActionBlocks['nameMapUnknown'].xmlText([one, value, result]); + } + return MotaActionBlocks['nameMap_m'].xmlText([result]); + + case 'shop': + var buildsub = function(obj,parser,next){ + var text_choices = null; + for(var ii=obj.choices.length-1,choice;choice=obj.choices[ii];ii--) { + text_choices=MotaActionBlocks['shopChoices'].xmlText([ + choice.text,choice.need||'',choice.icon,choice.color,'rgba('+choice.color+')',choice.condition,parser.parseList(choice.action),text_choices]); + } + var info = parser.getTitleAndPosition(obj.text || ''); + return MotaActionBlocks['shopsub'].xmlText([ + obj.id,info[0],info[1],info[3],obj.textInList,obj.mustEnable,obj.disablePreview,text_choices,next + ]); + } + var buildcommentevent = function(obj,parser,next){ + if (obj.args instanceof Array) { + obj.args = JSON.stringify(obj.args); + } + else obj.args = null; + return MotaActionBlocks['shopcommonevent'].xmlText([ + obj.id,parser.EvalString(obj.textInList),obj.mustEnable,parser.EvalString(obj.commonEvent),obj.args,next + ]); + } + var builditem = function (obj,parser,next){ + var text_choices = null; + for(var ii=obj.choices.length-1,choice;choice=obj.choices[ii];ii--) { + text_choices = MotaActionBlocks['shopItemChoices'].xmlText([ + choice.id, choice.number == null ? "" : (""+choice.number), choice.money == null ? "" : (""+choice.money), + choice.sell == null ? "" : (""+choice.sell), choice.condition || "", text_choices + ]); + } + return MotaActionBlocks['shopitem'].xmlText([ + obj.id,obj.textInList,obj.use||'money',obj.mustEnable,text_choices,next + ]); + } + var next=null; + if(!obj)obj=[]; + while(obj.length){ + var shopobj=obj.pop() + if(shopobj.item) + next=builditem(shopobj,this,next); + else if(shopobj.choices) + next=buildsub(shopobj,this,next); + else if(shopobj.commonEvent) + next=buildcommentevent(shopobj,this,next); + else + throw new Error("[警告]出错啦!\n"+shopobj.id+" 无效的商店"); + } + return MotaActionBlocks['shop_m'].xmlText([next]); + + default: + return MotaActionBlocks[type+'_m'].xmlText([this.parseList(obj)]); + } +} + +////// 开始解析一系列自定义事件 ////// +ActionParser.prototype.parseList = function (list) { + if (!this.isset(list)) return MotaActionBlocks['pass_s'].xmlText([],true); + if (!(list instanceof Array)) { + list = [list]; + } + if (list.length===0) return MotaActionBlocks['pass_s'].xmlText([],true); + this.event = {'id': 'action', 'data': { + 'list': list + }} + this.next = null; + this.result = null; + this.parseAction(); + return this.result; +} + +////// 解析当前自定义事件列表中的最后一个事件 ////// +ActionParser.prototype.parseAction = function() { + + // 事件处理完毕 + if (this.event.data.list.length==0) { + this.result = this.next; + this.next = null; + return; + } + + var data = this.event.data.list.pop(); + this.event.data.current = data; + + // 不同种类的事件 + + // 如果是文字:显示 + if (typeof data == "string") { + data={"type": "text", "text": data} + } + this.event.data.type=data.type; + switch (data.type) { + case "_next": + this.result = this.next; + this.next = data.next; + return; + case "text": // 文字/对话 + var info = this.getTitleAndPosition(data.text); + var textDrawing = []; + info[3] = (info[3] || "").replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + var ss = str.split(","); + if (ss.length == 3 || ss.length == 5 || ss.length >=9) { + var swap = function (i, j) { var x = ss[i]; ss[i] = ss[j]; ss[j] = x;} + if (ss.length >= 9) { + swap(1,5); swap(2,6); swap(3,7); swap(4,8); + } + textDrawing.push(ss); + } + return ''; + }); + if (textDrawing.length > 0) { + var buildTextDrawing = function (obj) { + if(!obj) obj=[]; + var text_choices = null; + for(var ii=obj.length-1,choice;choice=obj[ii];ii--) { + var reverse = 'null'; + if (choice[0].endsWith(':o') || choice[0].endsWith(':x') || choice[0].endsWith(':y')) { + reverse = choice[0].substring(choice[0].length - 2); + choice[0] = choice[0].substring(0, choice[0].length - 2); + } + text_choices=MotaActionBlocks['textDrawing'].xmlText([ + choice[0], reverse, choice[1], choice[2], choice[3], choice[4], choice[5], choice[6], + choice[7], choice[8], choice[9], choice[10], text_choices]); + } + return text_choices; + } + data.pos = data.pos || []; + this.next = MotaActionFunctions.xmlText('text_2_s', [ + info[0], info[1], info[2], data.pos[0], data.pos[1], data.pos[2], data.code||0, data.async||false, info[3], buildTextDrawing(textDrawing), this.next + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + } else if (info[0] || info[1] || info[2] || data.pos || data.code) { + data.pos = data.pos || []; + this.next = MotaActionFunctions.xmlText('text_1_s',[ + info[0], info[1], info[2], data.pos[0], data.pos[1], data.pos[2], data.code||0, data.async||false, info[3], this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + } + else { + this.next = MotaActionFunctions.xmlText('text_0_s', [info[3],this.next], + /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + } + break; + case "moveTextBox": // 移动对话框 + data.loc = data.loc || ['','']; + this.next = MotaActionBlocks['moveTextBox_s'].xmlText([ + data.code, data.loc[0], data.loc[1], data.relative||false, data.moveMode, data.time, data.async, this.next]); + break; + case "clearTextBox": // 清除对话框 + this.next = MotaActionBlocks['clearTextBox_s'].xmlText([(data.code||"").toString(),this.next]); + break; + case "autoText": // 自动剧情文本 + var info = this.getTitleAndPosition(data.text); + this.next = MotaActionBlocks['autoText_s'].xmlText([ + info[0],info[1],info[2],data.time,info[3],this.next]); + break; + case "scrollText": + this.next = MotaActionBlocks['scrollText_s'].xmlText([ + data.time, data.lineHeight||1.4, data.async||false, this.EvalString_Multi(data.text), this.next]); + break; + case "comment": // 注释 + this.next = MotaActionBlocks['comment_s'].xmlText([this.EvalString_Multi(data.text),this.next]); + break; + case "setText": // 设置剧情文本的属性 + data.title=this.Colour(data.title); + data.text=this.Colour(data.text); + if (!/^\w+\.png$/.test(data.background)) + data.background=this.Colour(data.background); + this.next = MotaActionBlocks['setText_s'].xmlText([ + data.position,data.offset,data.align,data.bold,data.title,'rgba('+data.title+')', + data.text,'rgba('+data.text+')',data.background,'rgba('+data.background+')', + data.titlefont,data.textfont,data.lineHeight,data.time,data.letterSpacing,data.animateTime,this.next]); + break; + case "tip": + this.next = MotaActionBlocks['tip_s'].xmlText([ + data.text,data.icon||"",this.next]); + break; + case "show": // 显示 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['show_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',data.time,data.async||false,this.next]); + break; + case "hide": // 消失 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['hide_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',data.remove||false,data.time,data.async||false,this.next]); + break; + case "setBlock": // 设置图块 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['setBlock_s'].xmlText([ + data.number||0,x_str.join(','),y_str.join(','),data.floorId||'',data.time,data.async||false,this.next]); + break; + case "setBlockOpacity": // 设置图块不透明度 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['setBlockOpacity_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',data.opacity,data.time,data.async||false,this.next]); + break; + case "setBlockFilter": // 设置图块不透明度 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['setBlockFilter_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',data.blur,data.hue,data.grayscale,data.invert||false,data.shadow,this.next]); + break; + case "turnBlock": // 事件转向 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['turnBlock_s'].xmlText([ + data.direction,x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "showFloorImg": // 显示贴图 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['showFloorImg_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "hideFloorImg": // 隐藏贴图 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['hideFloorImg_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "showBgFgMap": // 显示图层块 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['showBgFgMap_s'].xmlText([ + data.name||'bg', x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "hideBgFgMap": // 隐藏图层块 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['hideBgFgMap_s'].xmlText([ + data.name||'bg', x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "setBgFgBlock": // 设置图块 + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['setBgFgBlock_s'].xmlText([ + data.name||'bg', data.number||0, x_str.join(','),y_str.join(','),data.floorId||'',this.next]); + break; + case "setHeroIcon": // 改变勇士 + this.next = MotaActionBlocks['setHeroIcon_s'].xmlText([ + data.name||"", data.noDraw || false, this.next]); + break; + case "move": // 移动事件 + data.loc=data.loc||['','']; + var buildMoveDirection= function (obj) { + obj = MotaActionFunctions.processMoveDirections(obj||[]); + var res = null; + for(var ii=obj.length-1,one;one=obj[ii];ii--) { + var v = one.split(':'); + res=MotaActionBlocks['moveDirection'].xmlText([v[0], parseInt(v[1]), res]); + } + return res; + } + this.next = MotaActionBlocks['move_s'].xmlText([ + data.loc[0],data.loc[1],data.time,data.keep||false,data.async||false,buildMoveDirection(data.steps),this.next]); + break; + case "moveAction": // 前进一格或撞击 + this.next = MotaActionBlocks['moveAction_s'].xmlText([this.next]); + break; + case "moveHero": // 无视地形移动勇士 + var buildMoveDirection= function (obj) { + obj = MotaActionFunctions.processMoveDirections(obj||[]); + var res = null; + for(var ii=obj.length-1,one;one=obj[ii];ii--) { + var v = one.split(':'); + res=MotaActionBlocks['moveDirection'].xmlText([v[0], parseInt(v[1]), res]); + } + return res; + } + this.next = MotaActionBlocks['moveHero_s'].xmlText([ + data.time,data.async||false,buildMoveDirection(data.steps),this.next]); + break; + case "jump": // 跳跃事件 + data.from=data.from||['','']; + if (data.dxy) { + this.next = MotaActionBlocks['jump_1_s'].xmlText([ + data.from[0],data.from[1],data.dxy[0],data.dxy[1],data.time,data.keep||false,data.async||false,this.next]); + } else { + data.to=data.to||['','']; + this.next = MotaActionBlocks['jump_s'].xmlText([ + data.from[0],data.from[1],data.to[0],data.to[1],data.time,data.keep||false,data.async||false,this.next]); + } + break; + case "jumpHero": // 跳跃勇士 + if (data.dxy) { + this.next = MotaActionBlocks['jumpHero_1_s'].xmlText([ + data.dxy[0],data.dxy[1],data.time,data.async||false,this.next]); + } else { + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['jumpHero_s'].xmlText([ + data.loc[0],data.loc[1],data.time,data.async||false,this.next]); + } + break; + case "changeFloor": // 楼层转换 + if (!data.loc) { + data.loc = data.loc || ['','']; + data.stair = data.stair || ':now'; + } + if (data.floorId==':before'||data.floorId==':next'||data.floorId==':now') { + data.floorType=data.floorId; + delete data.floorId; + } + this.next = MotaActionBlocks['changeFloor_s'].xmlText([ + data.floorType||'floorId',data.floorId,data.stair||'loc',data.loc[0],data.loc[1],data.direction, + data.time, this.next]); + break; + case "changePos": // 直接更换勇士位置, 不切换楼层 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['changePos_s'].xmlText([ + data.loc[0],data.loc[1],data.direction,this.next]); + break; + case "follow": // 跟随勇士 + this.next = MotaActionBlocks['follow_s'].xmlText([data.name||"", this.next]); + break; + case "unfollow": // 取消跟随 + this.next = MotaActionBlocks['unfollow_s'].xmlText([data.name||"", this.next]); + break; + case "animate": // 显示动画 + if (data.loc == 'hero') { + this.next = MotaActionBlocks['animate_1_s'].xmlText([ + data.name,data.async||false,this.next]); + } else { + data.loc=data.loc||['','']; + this.next = MotaActionBlocks['animate_s'].xmlText([ + data.name,data.loc[0],data.loc[1],data.alignWindow||false,data.async||false,this.next]); + } + break; + case "stopAnimate": // 停止所有动画 + this.next = MotaActionBlocks['stopAnimate_s'].xmlText([data.doCallback||false,this.next]); + break; + case "setViewport": // 设置视角 + if (data.dxy) { + this.next = MotaActionBlocks['setViewport_1_s'].xmlText([ + data.dxy[0],data.dxy[1],data.moveMode||'', data.time||0,data.async||false,this.next]); + } else { + data.loc = data.loc||['','']; + this.next = MotaActionBlocks['setViewport_s'].xmlText([ + data.loc[0],data.loc[1],data.moveMode||'', data.time||0,data.async||false,this.next]); + } + break; + case "lockViewport": + this.next = MotaActionBlocks['lockViewport_s'].xmlText([data.lock, + this.next]); + break; + case "vibrate": // 画面震动 + this.next = MotaActionBlocks['vibrate_s'].xmlText([data.direction||'horizontal', + data.time||0, data.speed, data.power, data.async||false, this.next]); + break; + case "showImage": // 显示图片 + data.loc=data.loc||['',''] + if (data.sloc) { + this.next = MotaActionBlocks['showImage_1_s'].xmlText([ + data.code,data.image||data.name,data.reverse,data.sloc[0],data.sloc[1],data.sloc[2],data.sloc[3],data.opacity, + data.loc[0],data.loc[1],data.loc[2],data.loc[3],data.time||0,data.async||false,this.next + ]); + } + else { + this.next = MotaActionBlocks['showImage_s'].xmlText([ + data.code,data.image||data.name,data.reverse,data.loc[0],data.loc[1],data.opacity,data.time||0,data.async||false,this.next]); + } + break; + case "hideImage": // 清除图片 + this.next = MotaActionBlocks['hideImage_s'].xmlText([ + data.code,data.time||0,data.async||false,this.next]); + break; + case "showTextImage": // 显示图片化文本 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['showTextImage_s'].xmlText([ + this.EvalString_Multi(data.text),data.code,data.loc[0],data.loc[1],data.lineHeight||1.4,data.reverse,data.opacity,data.time||0,data.async||false,this.next]); + break; + case "moveImage": // 移动图片 + data.to=data.to||['',''] + this.next = MotaActionBlocks['moveImage_s'].xmlText([ + data.code, data.to[0], data.to[1], data.opacity, data.moveMode||'', data.time||0, data.async||false, this.next]); + break; + case "rotateImage": // 旋转图片 + data.center=data.center||['',''] + this.next = MotaActionBlocks['rotateImage_s'].xmlText([ + data.code, data.center[0], data.center[1], data.moveMode||'', data.angle||0, data.time||0, data.async||false, this.next]); + break; + case "scaleImage": // 放缩图片 + data.center=data.center||['',''] + this.next = MotaActionBlocks['scaleImage_s'].xmlText([ + data.code, data.center[0], data.center[1], data.moveMode||'', data.scale, data.time||0, data.async||false, this.next]); + break; + case "showGif": // 显示动图 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['showGif_s'].xmlText([ + data.name,data.loc[0],data.loc[1],this.next]); + break; + case "setCurtain": // 颜色渐变 + if(this.isset(data.color)){ + data.color = this.Colour(data.color); + this.next = MotaActionBlocks['setCurtain_0_s'].xmlText([ + data.color,'rgba('+data.color+')',data.time,data.moveMode,data.keep||false,data.async||false,this.next]); + } else { + this.next = MotaActionBlocks['setCurtain_1_s'].xmlText([ + data.time,data.moveMode,data.async||false,this.next]); + } + break; + case "screenFlash": // 画面闪烁 + data.color = this.Colour(data.color); + this.next = MotaActionBlocks['screenFlash_s'].xmlText([ + data.color,'rgba('+data.color+')',data.time||500,data.times,data.moveMode,data.async||false,this.next]); + break; + case "setWeather": // 更改天气 + this.next = MotaActionBlocks['setWeather_s'].xmlText([ + data.name,data.level||1,data.keep||false,this.next]); + break; + case "openDoor": // 开一个门, 包括暗墙 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['openDoor_s'].xmlText([ + data.loc[0],data.loc[1],data.floorId||'',data.needKey||false,data.async||false,this.next]); + break; + case "closeDoor": // 关一个门,需要该点无事件 + data.loc=data.loc||['',''] + this.next = MotaActionBlocks['closeDoor_s'].xmlText([ + data.loc[0],data.loc[1],data.id,data.async||false,this.next]); + break; + case "useItem": // 使用道具 + this.next = MotaActionBlocks['useItem_s'].xmlText([ + MotaActionFunctions.replaceToName_token(data.id),this.next]); + break; + case "loadEquip": // 装上装备 + this.next = MotaActionBlocks['loadEquip_s'].xmlText([ + MotaActionFunctions.replaceToName_token(data.id),this.next]); + break; + case "unloadEquip": // 卸下装备 + this.next = MotaActionBlocks['unloadEquip_s'].xmlText([ + data.pos,this.next]); + break; + case "openShop": // 打开一个全局商店 + this.next = MotaActionBlocks['openShop_s'].xmlText([ + data.id,data.open||false,this.next]); + break; + case "disableShop": // 禁用一个全局商店 + this.next = MotaActionBlocks['disableShop_s'].xmlText([ + data.id,this.next]); + break; + case "battle": // 强制战斗 + if (data.id) { + this.next = MotaActionBlocks['battle_s'].xmlText([ + MotaActionFunctions.replaceToName_token(data.id),this.next]); + } + else { + data.loc = data.loc || []; + this.next = MotaActionBlocks['battle_1_s'].xmlText([ + data.loc[0],data.loc[1],this.next]); + } + break; + case "trigger": // 触发另一个事件 + data.loc = data.loc || []; + this.next = MotaActionBlocks['trigger_s'].xmlText([ + data.loc[0],data.loc[1],this.next]); + break; + case "insert": // 强制插入另一个点的事件在当前事件列表执行,当前坐标和楼层不会改变 + if (data.args instanceof Array) { + data.args = JSON.stringify(data.args); + } + else data.args = null; + if (this.isset(data.name)) { + this.next = MotaActionBlocks['insert_1_s'].xmlText([ + data.name, data.args||"", this.next]); + } + else { + data.loc = data.loc || []; + this.next = MotaActionBlocks['insert_2_s'].xmlText([ + data.loc[0],data.loc[1],data.which,data.floorId||'',data.args||"",this.next]); + } + break; + case "playSound": + var knownItems = MotaActionBlocks['NameMap_List'].options.map(function (one) {return one[1];}); + if (knownItems.indexOf(data.name) >= 0) { + this.next = MotaActionBlocks['playSound_1_s'].xmlText([ + data.name,data.stop,data.pitch||"",data.sync,this.next]); + } else { + this.next = MotaActionBlocks['playSound_s'].xmlText([ + data.name,data.stop,data.pitch||"",data.sync,this.next]); + } + break; + case "playBgm": + this.next = MotaActionBlocks['playBgm_s'].xmlText([ + data.name,data.startTime||0,data.keep||false,this.next]); + break + case "pauseBgm": + this.next = MotaActionBlocks['pauseBgm_s'].xmlText([ + this.next]); + break + case "resumeBgm": + this.next = MotaActionBlocks['resumeBgm_s'].xmlText([ + data.resume||false,this.next]); + break + case "loadBgm": + this.next = MotaActionBlocks['loadBgm_s'].xmlText([ + data.name,this.next]); + break + case "freeBgm": + this.next = MotaActionBlocks['freeBgm_s'].xmlText([ + data.name,this.next]); + break + case "stopSound": + this.next = MotaActionBlocks['stopSound_s'].xmlText([ + this.next]); + break + case "setVolume": + this.next = MotaActionBlocks['setVolume_s'].xmlText([ + data.value, data.time, data.async||false, this.next]); + break + case "setBgmSpeed": + this.next = MotaActionBlocks['setBgmSpeed_s'].xmlText([ + data.value, data.pitch||false, this.next]); + break; + case "setValue": + this.next = MotaActionBlocks['setValue_s'].xmlText([ + this.expandIdBlock([data.name]), data["operator"]||'=', + this.expandEvalBlock([data.value]), + data.norefresh || false, + this.next]); + break; + case "setEnemy": + this.next = MotaActionBlocks['setEnemy_s'].xmlText([ + MotaActionFunctions.replaceToName_token(data.id), data.name, data["operator"]||'=', this.expandEvalBlock([data.value]), + data.norefresh||false, this.next]); + break; + case "setEnemyOnPoint": + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['setEnemyOnPoint_s'].xmlText([ + x_str.join(','),y_str.join(','),data.floorId||'',data.name, data["operator"]||'=', this.expandEvalBlock([data.value]), + data.norefresh||false, this.next]); + break; + case "resetEnemyOnPoint": + data.loc=data.loc||[]; + if (!(data.loc[0] instanceof Array)) + data.loc = [data.loc]; + var x_str=[],y_str=[]; + data.loc.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['resetEnemyOnPoint_s'].xmlText([ + x_str.join(','),y_str.join(','), data.floorId||'',data.norefresh||false,this.next]); + break; + case "moveEnemyOnPoint": + data.from=data.from||['',''] + if (data.dxy) { + this.next = MotaActionBlocks['moveEnemyOnPoint_1_s'].xmlText([ + data.from[0], data.from[1], data.dxy[0], data.dxy[1], data.floorId||'',data.norefresh||false,this.next]); + } else { + data.to=data.to||['',''] + this.next = MotaActionBlocks['moveEnemyOnPoint_s'].xmlText([ + data.from[0], data.from[1], data.to[0], data.to[1], data.floorId||'',data.norefresh||false,this.next]); + } + break; + case "setEquip": + this.next = MotaActionBlocks['setEquip_s'].xmlText([ + MotaActionFunctions.replaceToName_token(data.id), data.valueType||'value', data.name, data["operator"]||'=', this.expandEvalBlock([data.value]), this.next]); + break; + case "setFloor": + this.next = MotaActionBlocks['setFloor_s'].xmlText([ + data.name, data.floorId||null, JSON.stringify(data.value), this.next]); + break; + case "setGlobalAttribute": + this.next = MotaActionBlocks['setGlobalAttribute_s'].xmlText([ + data.name, data.value, this.next]); + break; + case "setGlobalValue": + this.next = MotaActionBlocks['setGlobalValue_s'].xmlText([ + data.name, data.value, this.next]); + break; + case "setGlobalFlag": + this.next = MotaActionBlocks['setGlobalFlag_s'].xmlText([ + data.name, data.value, this.next]); + break; + case "setNameMap": + this.next = MotaActionBlocks['setNameMap_s'].xmlText([ + data.name, data.value, this.next]); + break; + case "input": + this.next = MotaActionBlocks['input_s'].xmlText([ + data.text,this.next]); + break; + case "input2": + this.next = MotaActionBlocks['input2_s'].xmlText([ + data.text,this.next]); + break; + case "if": // 条件判断 + if (data["false"]) { + this.next = MotaActionFunctions.xmlText('if_s', [ + this.expandEvalBlock([data.condition]), + this.insertActionList(data["true"]), + this.insertActionList(data["false"]), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + } + else { + this.next = MotaActionFunctions.xmlText('if_1_s', [ + this.expandEvalBlock([data.condition]), + this.insertActionList(data["true"]), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + } + break; + case "confirm": // 显示确认框 + this.next = MotaActionFunctions.xmlText('confirm_s', [ + this.EvalString_Multi(data.text), data.timeout||0, data["default"], + this.insertActionList(data["yes"]), + this.insertActionList(data["no"]), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "switch": // 多重条件分歧 + var case_caseList = null; + for(var ii=data.caseList.length-1,caseNow;caseNow=data.caseList[ii];ii--) { + case_caseList=MotaActionFunctions.xmlText('switchCase', [ + this.isset(caseNow.case)?this.expandEvalBlock([caseNow.case]):"值",caseNow.nobreak,this.insertActionList(caseNow.action),case_caseList], + /* isShadow */false, /*comment*/ null, /*collapsed*/ caseNow._collapsed, /*disabled*/ caseNow._disabled); + } + this.next = MotaActionFunctions.xmlText('switch_s', [ + this.expandEvalBlock([data.condition]), + case_caseList,this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "choices": // 提供选项 + var text_choices = null; + for(var ii=data.choices.length-1,choice;choice=data.choices[ii];ii--) { + choice.color = this.Colour(choice.color); + text_choices=MotaActionFunctions.xmlText('choicesContext', [ + choice.text,choice.icon,choice.color,'rgba('+choice.color+')',choice.need||'',choice.condition||'',this.insertActionList(choice.action),text_choices], + /* isShadow */false, /*comment*/ null, /*collapsed*/ choice._collapsed, /*disabled*/ choice._disabled); + } + if (!this.isset(data.text)) data.text = ''; + var info = this.getTitleAndPosition(data.text); + this.next = MotaActionFunctions.xmlText('choices_s', [ + info[3],info[0],info[1],data.timeout||0,data.width,text_choices,this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "for": // 循环遍历 + this.next = MotaActionFunctions.xmlText('for_s',[ + this.expandEvalBlock([data.name]), + data.from || 0, data.to || 0, data.step || 0, + this.insertActionList(data.data), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "forEach": // 循环遍历列表 + this.next = MotaActionFunctions.xmlText('forEach_s',[ + this.expandEvalBlock([data.name]), + JSON.stringify(data.list), + this.insertActionList(data.data), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "while": // 前置条件循环处理 + this.next = MotaActionFunctions.xmlText('while_s',[ + this.expandEvalBlock([data.condition]), + this.insertActionList(data.data), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "dowhile": // 后置条件循环处理 + this.next = MotaActionFunctions.xmlText('dowhile_s',[ + this.insertActionList(data.data), + this.expandEvalBlock([data.condition]), + this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "break": // 跳出循环 + this.next = MotaActionBlocks['break_s'].xmlText([ + data.n || 1, this.next]); + break; + case "continue": // 继续执行当前循环 + this.next = MotaActionBlocks['continue_s'].xmlText([ + data.n || 1, this.next]); + break; + case "win": + this.next = MotaActionBlocks['win_s'].xmlText([ + data.reason,data.norank?true:false,data.noexit?true:false,this.next]); + break; + case "lose": + this.next = MotaActionBlocks['lose_s'].xmlText([ + data.reason,this.next]); + break; + case "restart": + this.next = MotaActionBlocks['restart_s'].xmlText([ + this.next]); + break; + case "function": + var func = data["function"]; + func=func.split('{').slice(1).join('{').split('}').slice(0,-1).join('}').trim().split('\n').join('\\n'); + this.next = MotaActionBlocks['function_s'].xmlText([ + data.async||false,func,this.next]); + break; + case "update": + this.next = MotaActionBlocks['update_s'].xmlText([ + this.doNotCheckAutoEvents||false, this.next]); + break; + case "showStatusBar": + this.next = MotaActionBlocks['showStatusBar_s'].xmlText([ + this.next]); + break; + case "hideStatusBar": + this.next = MotaActionBlocks['hideStatusBar_s'].xmlText([ + data.toolbox||false,this.next]); + break; + case "setHeroOpacity": + this.next = MotaActionBlocks['setHeroOpacity_s'].xmlText([ + data.opacity, data.moveMode, data.time, data.async||false, this.next]); + break; + case "sleep": // 等待多少毫秒 + this.next = MotaActionBlocks['sleep_s'].xmlText([ + data.time||0,data.noSkip||false,this.next]); + break; + case "wait": // 等待用户操作 + var case_waitList = null; + if (data.data) { + for(var ii=data.data.length-1,caseNow;caseNow=data.data[ii];ii--) { + if (caseNow["case"] == "keyboard") { + case_waitList = MotaActionFunctions.xmlText('waitContext_1',[ + caseNow.keycode || "0", caseNow["break"] || false, this.insertActionList(caseNow.action), case_waitList + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ caseNow._collapsed, /*disabled*/ caseNow._disabled); + } else if (caseNow["case"] == "mouse") { + case_waitList = MotaActionFunctions.xmlText('waitContext_2',[ + caseNow.px[0], caseNow.px[1], caseNow.py[0], caseNow.py[1], caseNow["break"] || false, this.insertActionList(caseNow.action), case_waitList + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ caseNow._collapsed, /*disabled*/ caseNow._disabled); + } else if (caseNow["case"] == "condition") { + case_waitList = MotaActionFunctions.xmlText('waitContext_3',[ + this.expandEvalBlock([caseNow.condition]), caseNow["break"] || false, this.insertActionList(caseNow.action), case_waitList + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ caseNow._collapsed, /*disabled*/ caseNow._disabled); + } else if (caseNow["case"] == "timeout") { + case_waitList = MotaActionFunctions.xmlText('waitContext_4',[ + caseNow["break"] || false, this.insertActionList(caseNow.action), case_waitList + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ caseNow._collapsed, /*disabled*/ caseNow._disabled); + } + } + } + this.next = MotaActionFunctions.xmlText('wait_s',[ + data.forceChild||false,data.timeout||0,case_waitList, this.next], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "waitAsync": // 等待所有异步事件执行完毕 + this.next = MotaActionBlocks['waitAsync_s'].xmlText([ + data.excludeAnimates||false, data.includeSounds||false, this.next]); + break; + case "stopAsync": // 立刻停止所有异步事件 + this.next = MotaActionBlocks['stopAsync_s'].xmlText([ + this.next]); + break; + case "callBook": // 呼出怪物手册 + this.next = MotaActionBlocks['callBook_s'].xmlText([ + this.next]); + break; + case "callSave": // 呼出存档界面 + this.next = MotaActionBlocks['callSave_s'].xmlText([ + this.next]); + break; + case "autoSave": // 自动存档 + this.next = MotaActionBlocks['autoSave_s'].xmlText([ + data.removeLast||false, this.next]); + break; + case "forbidSave": // 禁止存档 + this.next = MotaActionBlocks['forbidSave_s'].xmlText([ + data.forbid||false, this.next]); + break; + case "callLoad": // 呼出读档界面 + this.next = MotaActionBlocks['callLoad_s'].xmlText([ + this.next]); + break; + case "exit": // 立刻结束事件 + this.next = MotaActionBlocks['exit_s'].xmlText([ + this.next]); + break; + case "previewUI": // UI绘制预览 + this.next = MotaActionFunctions.xmlText('previewUI_s',[ + this.insertActionList(data.action), this.next + ], /* isShadow */false, /*comment*/ null, /*collapsed*/ data._collapsed, /*disabled*/ data._disabled); + break; + case "clearMap": // 清除画布 + this.next = MotaActionBlocks['clearMap_s'].xmlText([ + data.x, data.y, data.width, data.height, this.next + ]); + break; + case "setAttribute": // 设置画布属性 + data.fillStyle=this.Colour(data.fillStyle); + data.strokeStyle=this.Colour(data.strokeStyle); + this.next = MotaActionBlocks['setAttribute_s'].xmlText([ + data.font,data.fillStyle,'rgba('+data.fillStyle+')',data.strokeStyle,'rgba('+data.strokeStyle+')', + data.lineWidth,data.alpha,data.align,data.baseline,data.z,this.next]); + break; + case "setFilter": + this.next = MotaActionBlocks['setFilter_s'].xmlText([ + data.blur, data.hue, data.grayscale, data.invert||false, data.shadow, this.next]); + break; + case "fillText": // 绘制一行文本 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['fillText_s'].xmlText([ + data.x, data.y, data.style, 'rgba('+data.style+')', data.font, data.maxWidth, this.EvalString(data.text), this.next + ]); + break; + case "fillBoldText": // 绘制一行描边文本 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['fillBoldText_s'].xmlText([ + data.x, data.y, data.style, 'rgba('+data.style+')', data.strokeStyle, 'rgba('+(data.strokeStyle||"0,0,0,1")+')', + data.font, this.EvalString(data.text), this.next + ]); + break; + case "drawTextContent": // 绘制多行文本 + data.color = this.Colour(data.color); + this.next = MotaActionBlocks['drawTextContent_s'].xmlText([ + this.EvalString_Multi(data.text), data.left, data.top, data.maxWidth, data.color, 'rgba('+data.color+')', + data.align, data.fontSize, data.lineHeight, data.bold, this.next + ]); + break; + case "fillRect": // 绘制矩形 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['fillRect_s'].xmlText([ + data.x, data.y, data.width, data.height, data.radius, data.angle, data.style, 'rgba('+data.style+')', this.next + ]); + break; + case "strokeRect": // 绘制矩形边框 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['strokeRect_s'].xmlText([ + data.x, data.y, data.width, data.height, data.radius, data.angle, data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "drawLine": // 绘制线段 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['drawLine_s'].xmlText([ + data.x1, data.y1, data.x2, data.y2, data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "drawArrow": // 绘制线段 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['drawArrow_s'].xmlText([ + data.x1, data.y1, data.x2, data.y2, data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "fillPolygon": // 绘制多边形 + data.style = this.Colour(data.style); + var x_str=[],y_str=[]; + data.nodes.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['fillPolygon_s'].xmlText([ + x_str.join(','), y_str.join(','), data.style, 'rgba('+data.style+')', this.next + ]); + break; + case "strokePolygon": // 绘制多边形 + data.style = this.Colour(data.style); + var x_str=[],y_str=[]; + data.nodes.forEach(function (t) { + x_str.push(t[0]); + y_str.push(t[1]); + }) + this.next = MotaActionBlocks['strokePolygon_s'].xmlText([ + x_str.join(','), y_str.join(','), data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "fillEllipse": // 绘制椭圆 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['fillEllipse_s'].xmlText([ + data.x, data.y, data.a, data.b, data.angle, data.style, 'rgba('+data.style+')', this.next + ]); + break; + case "strokeEllipse": // 绘制椭圆边框 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['strokeEllipse_s'].xmlText([ + data.x, data.y, data.a, data.b, data.angle, data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "fillArc": // 绘制弧 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['fillArc_s'].xmlText([ + data.x, data.y, data.r, data.start, data.end, data.style, 'rgba('+data.style+')', this.next + ]); + break; + case "strokeArc": // 绘制弧 + data.style = this.Colour(data.style); + this.next = MotaActionBlocks['strokeArc_s'].xmlText([ + data.x, data.y, data.r, data.start, data.end, data.style, 'rgba('+data.style+')', data.lineWidth, this.next + ]); + break; + case "drawImage": // 绘制图片 + if (data.x1 != null && data.y1 != null && data.w1 != null && data.h1 != null) { + this.next = MotaActionBlocks['drawImage_1_s'].xmlText([ + data.image, data.reverse, data.x, data.y, data.w, data.h, data.x1, data.y1, data.w1, data.h1, data.angle, this.next + ]); + } + else { + this.next = MotaActionBlocks['drawImage_s'].xmlText([ + data.image, data.reverse, data.x, data.y, data.w, data.h, data.angle, this.next + ]); + } + break; + case "drawIcon": // 绘制图标 + this.next = MotaActionBlocks['drawIcon_s'].xmlText([ + data.id, data.frame||0, data.x, data.y, data.width, data.height, this.next + ]); + break; + case "drawBackground": // 绘制背景 + if (!/^\w+\.png$/.test(data.background)) + data.background=this.Colour(data.background); + this.next = MotaActionBlocks['drawBackground_s'].xmlText([ + data.background, 'rgba('+data.background+')', data.x, data.y, data.width, data.height, this.next + ]); + break; + case "drawSelector": // 绘制光标 + if (data.image) { + this.next = MotaActionBlocks['drawSelector_s'].xmlText([ + data.image, data.code, data.x, data.y, data.width, data.height, this.next + ]); + } + else { + this.next = MotaActionBlocks['drawSelector_1_s'].xmlText([data.code, this.next]); + } + case "animateImage": // 兼容 animateImage + break; + default: + this.next = MotaActionBlocks['unknown_s'].xmlText([ + JSON.stringify(data),this.next]); + } + this.parseAction(); + return; +} + +////// 往当前事件列表之后添加一个事件组 ////// +ActionParser.prototype.insertActionList = function (actionList) { + if (actionList.length===0) return null; + this.event.data.list.push({"type": "_next", "next": this.next}); + this.event.data.list=this.event.data.list.concat(actionList); + this.next = null; + this.parseAction(); + return this.result; +} + +////// 判断某对象是否不为undefined也不会null ////// +ActionParser.prototype.isset = function (val) { + if (val === undefined || val === null) { + return false; + } + return true +} + +ActionParser.prototype.StepString = function(steplist) { + var stepchar = { + 'up': '上', + 'down': '下', + 'left': '左', + 'right': '右', + 'forward': '前', + 'backward': '后' + } + var StepString = ''; + var last = null, number = 0; + steplist.forEach(function (v) { + if (v != last) { + if (last != null) { + StepString += stepchar[last]; + if (number > 1) StepString += number; + } + last = v; + number = 1; + } else { + number++; + } + }); + if (last != null) { + StepString += stepchar[last]; + if (number > 1) StepString += number; + } + return StepString; +} + +ActionParser.prototype.EvalString = function(EvalString) { + return EvalString.split('\b').join('\\b').split('\t').join('\\t').split('\n').join('\\n'); +} + +ActionParser.prototype.EvalString_Multi = function(EvalString) { + return EvalString.split('\b').join('\\b').split('\t').join('\\t'); +} + +ActionParser.prototype.getTitleAndPosition = function (string) { + string = this.EvalString_Multi(string); + var title = '', icon = '', position = ''; + string = string.replace(/\\t\[(([^\],]+),)?([^\],]+)\]/g, function (s0, s1, s2, s3) { + if (s3) title = s3; + if (s2) { icon = s3; title = s2; } + if (icon && !/^(.*)\.(jpg|jpeg|png)$/.test(icon) + && !/^[0-9a-zA-Z_][0-9a-zA-Z_:]*$/.test(icon)) { title += "," + icon; icon = ''; } + return ""; + }).replace(/\\b\[(.*?)\]/g, function (s0, s1) { + position = s1; return ""; + }); + return [title, icon, position, string]; +} + +ActionParser.prototype.Colour = function(color) { + return color?JSON.stringify(color).slice(1,-1):null; +} + +ActionParser.prototype.matchId = function(args) { + var rt=function(xml,args){ + // 此处刻意不写成 xml:MotaActionBlocks[str].xmlText 来方便搜索 + return {ret:true,xml:xml,args:args} + } + var match = /nothing/.exec('nothing') + // 固定列表 + var FixedId_List=MotaActionBlocks['FixedId_List'].options; // [["生命", "status:hp"], ...] + match=new RegExp('^('+FixedId_List.map(function(v){return v[1]}).join('|')+')$').exec(args[0]) + if(match){ + return rt(MotaActionBlocks['idFixedList_e'].xmlText, args); + } + // 独立开关 + match=/^switch:([A-Z])$/.exec(args[0]) + if(match){ + args[0]=match[1] + return rt(MotaActionBlocks['idFlag_e'].xmlText, args); + } + // 临时变量 + match=/^temp:([A-Z])$/.exec(args[0]) + if(match){ + args[0]=match[1] + return rt(MotaActionBlocks['idTemp_e'].xmlText, args); + } + // id列表 + var Id_List = MotaActionBlocks['Id_List'].options; // [["变量", "flag"], ...] + match=new RegExp('^('+Id_List.map(function(v){return v[1]}).join('|')+'):([a-zA-Z0-9_\\u4E00-\\u9FCC\\u3040-\\u30FF\\u2160-\\u216B\\u0391-\\u03C9]+)$').exec(args[0]) + if(match){ + if (match[1] == 'status' || match[1] == 'item' || match[1] == 'buff') { + match[2] = MotaActionFunctions.replaceToName_token(match[2]); + } + args=[match[1],match[2]] + return rt(MotaActionBlocks['idIdList_e'].xmlText, args); + } + return {ret:false} +} + +ActionParser.prototype.matchEvalAtom = function(args) { + var rt=function(xml,args){ + // 此处刻意不写成 xml:MotaActionBlocks[str].xmlText 来方便搜索 + return {ret:true,xml:xml,args:args} + } + var match = /nothing/.exec('nothing') + // 勾选框 + match = /^(true|false)$/.exec(args[0]) + if(match){ + args[0]='true'==args[0]; + return rt(MotaActionBlocks['bool_e'].xmlText, args); + } + // 怪物属性 + var EnemyId_List=MotaActionBlocks['EnemyId_List'].options; // [["生命", "hp"], ...] + match=new RegExp("^enemy:([a-zA-Z0-9_]+):(" + EnemyId_List.map(function(v){return v[1]}).join('|') + ")$").exec(args[0]) + if(match){ + args=[MotaActionFunctions.replaceToName_token(match[1]),match[2]] + return rt(MotaActionBlocks['enemyattr_e'].xmlText, args); + } + // 图块ID + match=/^blockId:(-?\d+),(-?\d+)$/.exec(args[0]) + if(match){ + args=[match[1],match[2]] + return rt(MotaActionBlocks['blockId_e'].xmlText, args); + } + // 图块数字 + match=/^blockNumber:(-?\d+),(-?\d+)$/.exec(args[0]) + if(match){ + args=[match[1],match[2]] + return rt(MotaActionBlocks['blockNumber_e'].xmlText, args); + } + // 图块类别 + match=/^blockCls:(-?\d+),(-?\d+)$/.exec(args[0]) + if(match){ + args=[match[1],match[2]] + return rt(MotaActionBlocks['blockCls_e'].xmlText, args); + } + // 装备孔 + match=/^equip:(-?\d+)$/.exec(args[0]) + if(match){ + args[0]=match[1] + return rt(MotaActionBlocks['equip_e'].xmlText, args); + } + match=/^core\.isReplaying\(\)$/.exec(args[0]); + if (match) { + return rt(MotaActionBlocks['isReplaying_e'].xmlText, args); + } + match=/^core\.(nextX|nextY)\((-?\d*)\)$/.exec(args[0]); + if (match) { + if (match[2] == "") match[2] = "1"; + args=[match[2], match[1]]; + return rt(MotaActionBlocks['nextXY_e'].xmlText, args); + } + match=/^core\.hasVisitedFloor\(['"](.*?)['"']\)$/.exec(args[0]); + if (match) { + args[0]=match[1]; + return rt(MotaActionBlocks['hasVisitedFloor_e'].xmlText, args); + } + match=/^core\.isShopVisited\(['"](.*?)['"']\)$/.exec(args[0]); + if (match) { + args[0]=match[1]; + return rt(MotaActionBlocks['isShopVisited_e'].xmlText, args); + } + match=/^core\.hasEquip\(['"](.*?)['"']\)$/.exec(args[0]); + if (match) { + args[0]=match[1]; + return rt(MotaActionBlocks['hasEquip_e'].xmlText, args); + } + match=/^core\.canBattle\(['"](.*?)['"']\)$/.exec(args[0]); + if (match) { + args[0]=match[1]; + return rt(MotaActionBlocks['canBattle_e'].xmlText, args); + } + match=/^core\.rand\((\d+)\)$/.exec(args[0]); + if (match) { + args[0]=match[1]; + return rt(MotaActionBlocks['rand_e'].xmlText, args); + } + return {ret:false} +} + +ActionParser.prototype.matchEvalCompare=function(args, isShadow){ + if (MotaActionFunctions.disableExpandCompare) return {ret:false}; + var raw=args[0].replace(/>/g,'>').replace(/</g,'<').replace(/"/g,'"').replace(/ /g,' ').replace(/&/g,'&') + if (raw[0]+raw.slice(-1)=='()') raw=raw.slice(1,-1); + var str=raw + var xml=MotaActionBlocks['expression_arithmetic_0'].xmlText + if (!/<=|<|>=|>|==|!=|===|!==|&&|\|\|/.exec(str)) return {ret:false}; + str=str.replace(/[^<>=!()&|]/g,' ') + // 处理括号匹配 + var old; + do { + old=str; + str=str.replace(/\([^()]*\)/g,function(v){return Array.from({length:v.length+1}).join(' ')}) + } while (old!=str); + // 按优先级依次寻找以下符号 + var oplist=['<','<=','>','>=','==','!=','===','!==','&&','||'].reverse() + for (var index = 0,op; op=oplist[index]; index++) { + var match=new RegExp(' '+(op=='||'?'\\|\\|':op)+' ').exec(str) + if (!match) continue; + args=[this.expandEvalBlock([raw.slice(0,match.index+1)],isShadow),op.replace(/&/g,'&').replace(//g,'>'),this.expandEvalBlock([raw.slice(match.index+1+op.length)],isShadow)] + return {ret:true,xml:xml,args:args} + } + return {ret:false} +} + +ActionParser.prototype.expandIdBlock = function(args, isShadow, comment) { + args[0]=MotaActionFunctions.replaceFromName(args[0]) + var xml=MotaActionBlocks['idString_e'].xmlText + var ret=this.matchId(args) + if (ret.ret){ + xml=ret.xml; + args=ret.args; + } else { + for (var index = 0; index < args.length; index++) { + args[index]=MotaActionFunctions.replaceToName(args[index]) + } + } + return xml(args, isShadow, comment); +} + +ActionParser.prototype.expandEvalBlock = function(args, isShadow, comment) { + args[0]=MotaActionFunctions.replaceFromName(args[0]) + var xml=MotaActionBlocks['evalString_e'].xmlText + if (args[0].indexOf('\n') >= 0 || args[0].indexOf('\\n') >= 0) return xml(args, isShadow, comment); + var ret=this.matchId(args) + if (ret.ret){ + xml=ret.xml; + args=ret.args; + } else if( (ret=this.matchEvalAtom(args)).ret ){ + xml=ret.xml; + args=ret.args; + } else if(/^(!.*|\(!.*\))$/.exec(args[0])){ + // 非 + xml=MotaActionBlocks['negate_e'].xmlText + var content=args[0][0]=='!'?args[0].slice(1):args[0].slice(2,-1) + args[0]=this.expandEvalBlock([content],isShadow) + } else if( (ret=this.matchEvalCompare(args, isShadow)).ret ){ + // 大小比较 + xml=ret.xml; + args=ret.args; + } else { + for (var index = 0; index < args.length; index++) { + args[index]=MotaActionFunctions.replaceToName(args[index]) + } + } + return xml(args, isShadow, comment); +} + +MotaActionFunctions.actionParser = new ActionParser(); + +MotaActionFunctions.workspace = function(){return workspace} + +MotaActionFunctions.parse = function(obj,type) { + try { + obj = obj.map(function (e) { + if (e.type == "function") return e; + else return JSON.parse(MotaActionFunctions.replaceToName(JSON.stringify(e))); + }); + } catch (e) { } + MotaActionFunctions.workspace().clear(); + xml_text = MotaActionFunctions.actionParser.parse(obj, type || 'event'); + xml = Blockly.Xml.textToDom('' + xml_text + ''); + Blockly.Xml.domToWorkspace(xml, MotaActionFunctions.workspace()); +} + +MotaActionFunctions.EvalString_pre = function(EvalString){ + if (EvalString.indexOf('__door__')!==-1) throw new Error('请修改开门变量__door__,如door1,door2,door3等依次向后。请勿存在两个门使用相同的开门变量。'); + EvalString = MotaActionFunctions.replaceFromName(EvalString); + return EvalString.replace(/([^\\])"/g,'$1\\"').replace(/^"/g,'\\"').replace(/""/g,'"\\"'); +} + +MotaActionFunctions.EvalString_Multi_pre = function(EvalString){ + if (EvalString.indexOf('__door__')!==-1) throw new Error('请修改开门变量__door__,如door1,door2,door3等依次向后。请勿存在两个门使用相同的开门变量。'); + EvalString = MotaActionFunctions.replaceFromName(EvalString); + return EvalString.replace(/([^\\])"/g,'$1\\"').replace(/^"/g,'\\"').replace(/""/g,'"\\"').replace(/\n/g, '\\n'); +} + +MotaActionFunctions.JsonEvalString_pre = function (JsonEvalString) { + if (JsonEvalString == '') return ''; + JsonEvalString = MotaActionFunctions.replaceFromName(JsonEvalString); + try { + return JSON.stringify(JSON.parse(JsonEvalString)); + } catch (e) { + throw new Error('此处需要填写一个合法的JSON内容'); + } +} + +MotaActionFunctions.IntString_pre = function (IntString) { + if (!/^[+-]?\d*$/.test(IntString)) throw new Error('此项必须是整数或不填'); + return IntString; +} + +MotaActionFunctions.IdString_pre = function(IdString){ + if (IdString.indexOf('__door__')!==-1) throw new Error('请修改开门变量__door__,如door1,door2,door3等依次向后。请勿存在两个门使用相同的开门变量。'); + IdString = MotaActionFunctions.replaceFromName(IdString); + IdString = MotaActionFunctions.replaceFromName_token(IdString); + if (IdString && !(MotaActionFunctions.pattern.id.test(IdString)) && !(MotaActionFunctions.pattern.idWithoutFlag.test(IdString))) + throw new Error('id: '+IdString+'中包含了0-9 a-z A-Z _ - :之外的字符'); + return IdString; +} + +MotaActionFunctions.PosString_pre = function(PosString){ + if (!PosString || /^-?\d+$/.test(PosString)) return PosString; + //if (!(MotaActionFunctions.pattern.id.test(PosString)))throw new Error(PosString+'中包含了0-9 a-z A-Z _ 和中文之外的字符,或者是没有以flag: 开头'); + var comma = PosString.indexOf(','); + if (comma >= 0 && PosString.substring(0, comma).indexOf('(') < 0) throw '此处不可写多点坐标'; + return '"'+MotaActionFunctions.replaceFromName(PosString)+'"'; +} + +MotaActionFunctions.processMoveDirections = function (steps) { + var curr = null, num = null; + var result = []; + steps.forEach(function (one) { + var v = one.split(':'); + if (v.length == 1) v.push("1"); + if (v[0] != curr) { + if (curr != null) result.push(curr+":"+num); + curr = v[0]; + num = parseInt(v[1]); + } else { + num += parseInt(v[1]); + } + }); + if (curr != null) result.push(curr+":"+num); + return result; +} + +MotaActionFunctions.processMultiLoc = function (EvalString_0, EvalString_1) { + var floorstr = ''; + if (EvalString_0 && EvalString_1) { + var x = EvalString_0, y = EvalString_1; + var pattern = /^([+-]?\d+)(, ?[+-]?\d+)*$/; + if (pattern.test(x) && pattern.test(y) && x.split(',').length == y.split(',').length) { + x=x.split(','); + y=y.split(','); + for(var ii=0;ii 此文件是editor的结构说明, 不是使用文档 + +## 组成 + +本目录下所有文件,以及`../editor.html`,`../editor-mobile.html`和`../启动服务.exe`,`../server.py`是地图编辑器的所有组件. + +### 父目录 ++ editor(-mobile).html + 编辑器的[入口页面](http://127.0.0.1:1055/editor.html) + 以`display:none`的形式引入了core的`index.html`的`dom`,修改了原来的`.gameCanvas #ui #data`等的名字以避免冲突 ++ 启动服务.exe [源码](http://github.com/ckcz123/mota-js-server/) + 为fs.js提供后端支持, 同时集成了一些实用工具 ++ server.py + 非windows平台中为fs.js提供后端支持 + +### core + +游戏运行时中部分代码根据`main.mod=='editor'`进行了调整 + ++ 通过`main.init('editor')`加载数据 + ++ `editor`模式关闭了部分动画 + ++ `core.drawMap`中`editor`模式下不再画图,而是生成画图的函数提+ 供给`editor` + ++ `editor`模式下`GlobalAnimate`可以独立的选择是否播放 + ++ `core.playBgm`和`core.playSound`中非`play`模式不再播放声音 + ++ `core.show`和`core.hide`中非`play`模式不再进行动画而是立刻+ 完成并执行回调 + ++ `editor`模式不执行`core.resize` + + +### fs.js + +依照[issue#31](https://github.com/ckcz123/mota-js/issues/13)的约定, 模仿node的fs模块提供如下api,与`启动服务.exe`,`server.py`配合为js提供文件读写功能, 是编辑器成立的前提 + +``` js +fs.readFile('file.in','utf-8',callback) +//读文本文件 +//callback:function(err, data) +//data:字符串 +fs.readFile('file.in','base64',callback) +//读二进制文件 +//callback:function(err, data) +//data:base64字符串 + +fs.writeFile('file.out', data ,'utf-8', callback) +//写文本文件 +//callback:function(err) +//data:字符串 +fs.writeFile('file.out', data ,'base64', callback) +//写二进制文件 +//callback:function(err) +//data:base64字符串 + +fs.readdir(path, callback) +//callback:function(err, data) +//path:支持"/"做分隔符 +//data:[filename1,filename2,..] filename是字符串,只包含文件不包含目录 + +//所有参数不允许缺省 +``` + +### editor_multi.js + +用[CodeMirror](https://github.com/codemirror/CodeMirror) 实现有高亮的多行文本编辑 + +编辑选定`id_`的文本域 +``` js +editor_multi.import(id_,{lint:true}) +``` + +编辑blockly方块的特定域 +``` js +editor_multi.multiLineEdit(value,b,f,{lint:true},callback) +``` + +配置表格 +``` js +editor_multi.editCommentJs(mod) +``` + +### MotaAction.g4 + +通过[antlr-blockly](https://github.com/zhaouv/antlr-blockly)的语法定义core中各事件对应的方块. + +借助google的[blockly](https://github.com/google/blockly)来实现事件的可视化编辑. + +入口方块以`_m`结尾 + +一般语句写在`action`中, 以`_s`结尾 + +### editor_blockly.js + +把选定`id_`的事件用blockly编辑 +``` js +editor_blockly.import(id_,{type:'event'}); +``` + +把文本区域的代码转换成图块 +``` js +editor_blockly.parse(); +``` + +`initscript中`的`toolboxObj`定义了侧边栏中显示的图块 + +自定义`Blockly.FieldColour.prototype.createWidget_`修改了颜色选择器的行为 + +自定义`Blockly.FieldTextInput.prototype.showInlineEditor_`添加了自动补全 + + +### editor_mode.js + +用于切换数据区 + +加载数据 +```javascript +editor.mode.loc(); +editor.mode.enemyitem(); +editor.mode.floor(); +editor.mode.tower(); +editor.mode.functions(); +``` + +切换模式 +```javascript +editor.mode.onmode('');//清空 +editor.mode.onmode('save');//保存 +editor.mode.onmode('nextChange');//下次onmode时前端进行切换 + +editor.mode.onmode('loc'); +editor.mode.onmode('enemyitem'); +editor.mode.onmode('floor'); +editor.mode.onmode('tower'); +editor.mode.onmode('functions'); +editor.mode.onmode('map'); +editor.mode.onmode('appendpic'); +``` +在`onmode('save')`时,改动才会保存到文件,涉及到图片的改动需要刷新页面使得`editor`能看到 + +数据区一些通用的函数也定义在这里 + +### editor_table.js + +处理表格的生成, 及其响应的事件 + +其接受来自../project/\*.js的数据`obj`和来自table/\*.comment.js的注释`commentObj` + +commentObj的结构如示例 +``` js +{ + "_type": "object", + "_data": { + "events": { + "_type": "object", + "_data": { + "resetGame": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "重置整个游戏" + }, + "setInitData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "设置初始属性" + }, +``` +一层正常数据, 一层`_`开头的结构说明, 忽略`_`层的话与obj同结构 + +通过 +``` js +editor.table.objToTable(obj, commentObj) +editor.table.objToTr +editor.table.objToTd +``` +遍历这两个对象来生成表格, 叶节点根据`_type`渲染成对应的dom + +表格的值变化`onchange`, 双击`dblclickfunc`, 删除`deletefunc`, 添加`addfunc`也定义在此文件 + +### editor_mappanel.js + +与地图区相关的功能 ++ 画地图 线/矩形/tileset ++ 通过地图选中事件或素材 ++ 右键菜单 ++ 切换楼层 ++ 大地图移动可视窗口 + +### editor_materialpanel.js + +与素材区相关的功能 ++ 选中 ++ 展开/折叠 + +### editor_datapanel.js + +与数据区相关的功能 (且与表格无关的功能) ++ 地图编辑 + - 创建新地图 + - 批量创建 ++ 地图选点 ++ 图块属性 + - 注册素材 + - 修改id ++ 楼层属性 + - 修改楼层id ++ 全塔属性 ++ 脚本编辑 ++ 追加素材 + - 选择导入的区域 + - 导入图片 + - 改色相 + - 选中图片中的格子 + - 确认追加 ++ 公共事件 ++ 插件编写 + +### editor_ui.js + +ui事件中没有具体到前三个区中的函数 ++ 响应点击 ++ 快捷键 ++ 显示帮助 ++ UI预览 & 地图选点相关 + +### editor_util.js + +一些通用的函数 ++ 生成id ++ HTML转义 ++ 像素处理 ++ base64的encode/decode ++ 检查值是否为空 + +### editor_listen.js + +界面与功能的绑定 + +### editor_file.js + +包装fs.js, 把数据读写到对应的文件 + +### editor_game.js + +游戏数据的处理 + +此部分的重构未完成, 实际上是由editor_file.js和editor_file_unsorted.js来做的 + +### editor.js + +初始化加整合各模块 + +现状是还放了一些游戏数据有关的函数未挪到editor_game, 以及部分和入口页面生成有关的函数 \ No newline at end of file diff --git a/_server/config.json b/_server/config.json new file mode 100644 index 0000000..dfacc36 --- /dev/null +++ b/_server/config.json @@ -0,0 +1 @@ +{"viewportLoc":[0,0],"lastUsed":[{"idnum":46,"id":"fly","images":"items","y":12,"recent":1732768196310,"frequent":2},{"idnum":89,"id":"portal","images":"animates","y":17,"recent":1732590284009,"frequent":4},{"idnum":92,"id":"leftPortal","images":"animates","y":24,"recent":1732590243601,"frequent":4},{"idnum":94,"id":"rightPortal","images":"animates","y":25,"recent":1732590229450,"frequent":6},{"idnum":206,"id":"bigBat","images":"enemys","y":5,"recent":1732518600961,"frequent":1},{"idnum":211,"id":"skeletonCaptain","images":"enemys","y":10,"recent":1732518593961,"frequent":3},{"idnum":205,"id":"bat","images":"enemys","y":4,"recent":1732518591433,"frequent":1},{"idnum":88,"id":"downFloor","images":"terrains","y":5,"recent":1732510497803,"frequent":1},{"idnum":87,"id":"upFloor","images":"terrains","y":6,"recent":1732510482564,"frequent":3},{"idnum":93,"id":"downPortal","images":"animates","y":23,"recent":1732510408539,"frequent":3},{"idnum":91,"id":"upPortal","images":"animates","y":26,"recent":1732510340731,"frequent":3},{"idnum":257,"id":"dragon","images":"enemys","y":56,"recent":1732431861421,"frequent":4},{"idnum":130,"id":"expShop","images":"npcs","y":9,"recent":1732092503297,"frequent":1},{"idnum":27,"id":"redGem","images":"items","y":16,"recent":1732092498097,"frequent":1},{"idnum":246,"id":"blueKing","images":"enemys","y":45,"recent":1731057412587,"frequent":9},{"idnum":43,"id":"sword5","images":"items","y":54,"recent":1730963163489,"frequent":1},{"idnum":36,"id":"shield1","images":"items","y":55,"recent":1730963160769,"frequent":1},{"idnum":1,"id":"yellowWall","images":"animates","y":10,"recent":1673440212744,"frequent":2},{"idnum":45,"id":"book","images":"items","y":9,"recent":1732787462314,"frequent":1}],"editor_multi.fontSize":14,"editorLastFloorId":"MT7"} \ No newline at end of file diff --git a/_server/editor.js b/_server/editor.js new file mode 100644 index 0000000..88a22f7 --- /dev/null +++ b/_server/editor.js @@ -0,0 +1,1054 @@ +function editor() { + this.version = "2.0"; + this.brushMod = "line";//["line","rectangle","tileset"] + this.layerMod = "map";//["fgmap","map","bgmap"] + this.isMobile = false; + + this.dom={ + body:document.body, + eui:document.getElementById('eui'), + efg:document.getElementById('efg'), + ebm:document.getElementById('ebm'), + euiCtx:document.getElementById('eui').getContext('2d'), + efgCtx:document.getElementById('efg').getContext('2d'), + ebmCtx:document.getElementById('ebm').getContext('2d'), + mid:document.getElementById('mid'), + mapEdit:document.getElementById('mapEdit'), + selectFloor:document.getElementById('selectFloor'), + iconExpandBtn :document.getElementById('iconExpandBtn'), + dataSelection : document.getElementById('dataSelection'), + iconLib:document.getElementById('iconLib'), + midMenu:document.getElementById('midMenu'), + extraEvent: document.getElementById('extraEvent'), + chooseThis : document.getElementById('chooseThis'), + chooseInRight : document.getElementById('chooseInRight'), + copyLoc : document.getElementById('copyLoc'), + pasteLoc : document.getElementById('pasteLoc'), + clearEvent : document.getElementById('clearEvent'), + clearLoc : document.getElementById('clearLoc'), + brushMod:document.getElementById('brushMod'), + brushMod2:document.getElementById('brushMod2'), + brushMod3:document.getElementById('brushMod3'), + brushMod4:document.getElementById('brushMod4'), + layerMod:document.getElementById('layerMod'), + layerMod2:document.getElementById('layerMod2'), + layerMod3:document.getElementById('layerMod3'), + viewportButtons:document.getElementById('viewportButtons'), + appendPicCanvas : document.getElementById('appendPicCanvas'), + appendBgCtx : document.getElementById('appendPicCanvas').children[0].getContext('2d'), + appendSource : document.getElementById('appendPicCanvas').children[1], + appendPicClick : document.getElementById('appendPicCanvas').children[2], + appendSprite : document.getElementById('appendPicCanvas').children[3], + appendSourceCtx:document.getElementById('appendPicCanvas').children[1].getContext('2d'), + appendSpriteCtx:document.getElementById('appendPicCanvas').children[3].getContext('2d'), + appendPicSelection : document.getElementById('appendPicSelection'), + selectAppend : document.getElementById('selectAppend'), + selectFileBtn :document.getElementById('selectFileBtn'), + changeFloorId :document.getElementById('changeFloorId'), + changeFloorSize: document.getElementById('changeFloorSize'), + left1 : document.getElementById('left1'), + editModeSelect :document.getElementById('editModeSelect'), + mid2 : document.getElementById('mid2'), + clearLastUsedBtn: document.getElementById('clearLastUsedBtn'), + lastUsedTitle: document.getElementById('lastUsedTitle'), + lastUsedDiv: document.getElementById('lastUsedDiv'), + lastUsed: document.getElementById('lastUsed'), + lastUsedCtx: document.getElementById('lastUsed').getContext('2d'), + showMovable: document.getElementById('showMovable'), + gameInject: document.getElementById('gameInject'), + undoFloor: document.getElementById('undoFloor'), + selectFloorBtn: document.getElementById('selectFloorBtn'), + editorTheme: document.getElementById('editorTheme'), + bigmapBtn : document.getElementById('bigmapBtn'), + mapRowMark: document.getElementById('mapRowMark'), + mapColMark: document.getElementById('mapColMark'), + maps: ['bgmap', 'fgmap', 'map'], + canvas: ['bg', 'fg'], + }; + + this.uivalues={ + // 绘制区拖动有关 + holdingPath : 0, + stepPostfix : null,//用于存放寻路检测的第一个点之后的后续移动 + mouseOutCheck : 2, + startPos:null, + endPos:null, + lastMoveE:{buttons:0,clientX:0,clientY:0}, + selectedArea: null, + // 材料区拖动有关 + lastMoveMaterE:null, + tileSize: [1,1], + startLoc: null, + // 撤销/恢复 + preMapData : [], + preMapMax: 10, + postMapData: [], + // + shortcut:{}, + copyedInfo : null, + // 折叠素材 + scrollBarHeight :0, + folded:false, + foldPerCol: 50, + // + ratio : 1, + // 是否是大地图模式 + bigmap : false, + bigmapInfo: { + top: 0, + left: 0, + size: 32, + }, + // blockly转义 + disableBlocklyReplace: false, + // blockly展开比较 + disableBlocklyExpandCompare: false, + + // 绑定机关门事件相关 + bindSpecialDoor: { + loc: null, + n: -1, + enemys: [] + }, + + // 复制怪物或道具属性 + copyEnemyItem : { + type: null, + data: {} + }, + + // tile + lockMode: false, + + showMovable: false, + + // 最近使用的图块 + lastUsedType: null, + lastUsed: [], + + // 最近访问的楼层 + recentFloors: [] + }; + + window.onerror = function (msg, url, lineNo, columnNo, error) { + var string = msg.toLowerCase(); + var substring = "script error"; + var message; + if (string.indexOf(substring) > -1){ + message = 'Script Error: See Browser Console for Detail'; + } else { + if (url) url = url.substring(url.lastIndexOf('/')+1); + message = [ + 'Message: ' + msg, + 'URL: ' + url, + 'Line: ' + lineNo, + 'Column: ' + columnNo, + 'Error object: ' + JSON.stringify(error) + ].join(' - '); + // alert(message); + } + try { + printe(message) + } catch (e) { + alert(message); + } + return false; + }; +} + +editor.prototype.uifunctions={}; + +/* +editor.loc +editor.pos +editor.info +始终是最后一次点击的结果 +注意editor.info可能因为点击其他地方而被清空 +*/ + +/////////// 数据相关 /////////// + +editor.prototype.init = function (callback) { + + editor.airwallImg = new Image(); + editor.airwallImg.src = './project/materials/airwall.png'; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'index.html', true); + xhr.onload = function () { + if (xhr.status != 200) { + alert("HTTP " + xhr.status); + return; + } + var str = xhr.response.split(''); + if (str.length != 3) window.onerror("index.html格式不正确"); + editor.dom.gameInject.innerHTML = str[1]; + + var cvs = ['bg', 'event', 'event2', 'fg'].map(function(e) { + return document.getElementById(e); + }); + ['bg', 'ev', 'ev2', 'fg'].forEach(function(e, i) { + editor.dom[e+'c'] = cvs[i]; + editor.dom[e+'Ctx'] = cvs[i].getContext('2d'); + + editor.dom.mapEdit.insertBefore(cvs[i], editor.dom.ebm); + }); + + var mainScript = document.createElement('script'); + + mainScript.onload = function() { + + var useCompress = main.useCompress; + main.useCompress = false; + + main.init('editor', function () { + editor_util_wrapper(editor); + editor_game_wrapper(editor, main, core); + editor_file_wrapper(editor); + editor_table_wrapper(editor); + editor_ui_wrapper(editor); + editor_uievent_wrapper(editor); + editor_mappanel_wrapper(editor); + editor_datapanel_wrapper(editor); + editor_materialpanel_wrapper(editor); + editor_listen_wrapper(editor); + editor.printe=printe; + afterMainInit(); + }); + + var afterMainInit = function () { + editor.game.fixFunctionInGameData(); + editor.main = main; + editor.core = core; + editor.fs = fs; + editor_file = editor_file(editor, function () { + editor.file = editor_file; + editor_mode = editor_mode(editor); + editor.mode = editor_mode; + var canvases = document.getElementsByClassName('gameCanvas'); + for (var one in canvases) { + canvases[one].width = canvases[one].height = core.__PIXELS__; + } + core.resetGame(core.firstData.hero, null, core.firstData.floorId, core.cloneArray(core.initStatus.maps)); + var floorId = editor.config.get('editorLastFloorId', core.status.floorId); + if (core.floorIds.indexOf(floorId) < 0) floorId = core.status.floorId; + + core.status.floorId = floorId; + core.resizeMap(floorId); + core.clearMap('all'); + core.generateGroundPattern(floorId); + core.extractBlocks(floorId); + core.status.thisMap = core.status.maps[floorId]; + afterCoreReset(); + }); + } + + var afterCoreReset = function () { + + editor.game.idsInit(core.maps, core.icons.icons); // 初始化图片素材信息 + editor.drawInitData(core.icons.icons); // 初始化绘图 + + editor.game.fetchMapFromCore(); + editor.pos = {x: 0, y: 0}; + editor.updateMap(); + editor.buildMark(); + var viewportLoc = editor.config.get('viewportLoc', []); + editor.setViewport(viewportLoc[0] || 0, viewportLoc[1] || 0); + editor.drawEventBlock(); + + editor.mode.loc(); + editor.info = editor.ids[editor.indexs[201]]; + editor.mode.enemyitem(); + editor.mode.floor(); + editor.mode.tower(); + editor.mode.functions(); + editor.mode.commonevent(); + editor.mode.showMode('tower'); + + editor_multi = editor_multi(); + editor_blockly = editor_blockly(); + + // --- 所有用到的flags + editor.used_flags = {}; + editor.addUsedFlags(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d)); + // 楼层属性 + for (var floorId in editor.main.floors) { + editor.addUsedFlags(JSON.stringify(editor.main.floors[floorId])); + } + // 公共事件 + for (var name in events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + editor.addUsedFlags(JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent[name])); + } + // 道具效果 + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + editor.addUsedFlags(JSON.stringify(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id])); + } + // 全局商店 + editor.addUsedFlags(JSON.stringify(editor.main.core.firstData.shops)); + // 怪物战前战后 + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) { + editor.addUsedFlags(JSON.stringify(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id])); + } + // 图块属性 + for (var id in maps_90f36752_8815_4be8_b32b_d7fad1d0542e) { + editor.addUsedFlags(JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e[id])); + } + + if (editor.useCompress == null) editor.useCompress = useCompress; + if (Boolean(callback)) callback(); + + } + } + + mainScript.id = "mainScript"; + mainScript.src = "main.js"; + editor.dom.gameInject.appendChild(mainScript); + }; + xhr.onabort = xhr.ontimeout = xhr.onerror = function () { + alert("无法访问index.html"); + } + + editor.config = new editor_config(); + editor.config.load(function() { + var theme = editor.config.get('theme', 'editor_color'); + document.getElementById('color_css').href = '_server/css/' + theme + '.css'; + editor.dom.editorTheme.value = theme; + xhr.send(); + }); +} + +editor.prototype.mapInit = function () { + var ec = editor.dom.evCtx; + ec.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); + editor.dom.ev2Ctx.clearRect(0, 0, core.bigmap.width*32, core.bigmap.height*32); + editor.map = []; + var sy=editor.currentFloorData.map.length,sx=editor.currentFloorData.map[0].length; + for (var y = 0; y < sy; y++) { + editor.map[y] = []; + for (var x = 0; x < sx; x++) { + editor.map[y][x] = 0; + } + } + editor.dom.maps.forEach(function (one) { + editor.currentFloorData[one] = editor[one] = JSON.parse(JSON.stringify(editor.map)); + }); + editor.currentFloorData.firstArrive = []; + editor.currentFloorData.eachArrive = []; + editor.currentFloorData.events = {}; + editor.currentFloorData.autoEvent = {}; + editor.currentFloorData.changeFloor = {}; + editor.currentFloorData.beforeBattle = {}; + editor.currentFloorData.afterBattle = {}; + editor.currentFloorData.afterGetItem = {}; + editor.currentFloorData.afterOpenDoor = {}; + editor.currentFloorData.cannotMove = {}; +} + +editor.prototype.changeFloor = function (floorId, callback) { + for(var ii=0,name;name=editor.dom.maps[ii];ii++){ + var mapArray=editor[name].map(function (v) { + return v.map(function (v) { + return v.idnum || v || 0 + }) + }); + editor.currentFloorData[name]=mapArray; + } + editor.uivalues.preMapData = []; + editor.uivalues.postMapData = []; + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(true); + + core.status.floorId = floorId; + core.resizeMap(floorId); + core.clearMap('all'); + core.generateGroundPattern(floorId); + core.extractBlocks(floorId); + core.status.thisMap = core.status.maps[floorId]; + + editor.game.fetchMapFromCore(); + editor.updateMap(); + editor_mode.floor(); + editor.drawEventBlock(); + + editor.viewportLoc = editor.viewportLoc || {}; + var loc = editor.viewportLoc[floorId] || [], x = loc[0] || 0, y = loc[1] || 0; + editor.setViewport(x, y); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.config.set('editorLastFloorId', floorId); + if (callback) callback(); +} + +/////////// 游戏绘图相关 /////////// + +editor.prototype.drawEventBlock = function () { + var fg=editor.dom.efgCtx; + + fg.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + if (editor.uivalues.bigmap) return this._drawEventBlock_bigmap(); + + var firstData = editor.game.getFirstData(); + // 不可通行性 + var movableArray = {}; + if (editor.uivalues.showMovable) { + movableArray = core.generateMovableArray() || {}; + fg.fillStyle = "rgba(0,0,0,0.4)"; + fg.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + for (var i=0;i= 0) { + fg.textAlign = 'right'; + editor.game.doCoreFunc("fillBoldText", fg, index + 1, + 32 * i + 28, 32 * j + 15, '#FF7F00', null, '14px Verdana'); + } + var offset = 0; + if (editor.currentFloorData.upFloor && editor.currentFloorData.upFloor.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔼", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + if (editor.currentFloorData.downFloor && editor.currentFloorData.downFloor.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔽", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + if (editor.currentFloorData.flyPoint && editor.currentFloorData.flyPoint.toString() == loc) { + fg.textAlign = 'left'; + editor.game.doCoreFunc("fillText", fg, "🔃", 32 * i + offset, 32 * j + 8, null, "8px Verdana"); + offset += 8; + } + } + } +} + +editor.prototype._drawEventBlock_bigmap = function () { + var fg=editor.dom.efgCtx; + var info = editor.uivalues.bigmapInfo, size = info.size, psize = size / 4; + + // 不可通行性 + var movableArray = {}; + if (editor.uivalues.showMovable) { + movableArray = core.generateMovableArray() || {}; + fg.fillStyle = "rgba(0,0,0,0.4)"; + fg.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + for (var i = 0; i < editor.currentFloorData.width; ++i) { + for (var j = 0; j < editor.currentFloorData.height; ++j) { + var directions = (movableArray[i]||{})[j]; + if (directions == null) continue; + if (!directions.includes('left') && i != 0) { + var ndirections = (movableArray[i-1]||{})[j]; + if (ndirections != null && !ndirections.includes('right')) { + core.drawLine(fg, info.left + size * i, info.top + size * j + size / 4, info.left + size * i, info.top + size * j + size * 3 / 4, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i, info.top + size * j + size / 3, info.left + size * i, info.top + size * j + size * 2 / 3, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size / 4, info.top + size * j + size * 3 / 8], + [info.left + size * i, info.top + size * j + size / 2], + [info.left + size * i + size / 4, info.top + size * j + size * 5 / 8]], '#FF0000'); + } + } + if (!directions.includes('right') && i != editor.currentFloorData.width - 1) { + var ndirections = (movableArray[i+1]||{})[j]; + if (ndirections != null && !ndirections.includes('left')) { + core.drawLine(fg, info.left + size * i + size, info.top + size * j + size / 4, info.left + size * i + size, info.top + size * j + size * 3 / 4, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size, info.top + size * j + size / 3, info.left + size * i + size, info.top + size * j + size * 2 / 3, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 4, info.top + size * j + size * 3 / 8], + [info.left + size * i + size, info.top + size * j + size / 2], + [info.left + size * i + size * 3 / 4, info.top + size * j + size * 5 / 8]], '#FF0000'); + } + } + if (!directions.includes('up') && j != 0) { + var ndirections = movableArray[i][j-1]; + if (ndirections != null && !ndirections.includes('down')) { + core.drawLine(fg, info.left + size * i + size / 4, info.top + size * j, info.left + size * i + size * 3 / 4, info.top + size * j, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size / 3, info.top + size * j, info.left + size * i + size * 2 / 3, info.top + size * j, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 8, info.top + size * j + size / 4], + [info.left + size * i + size / 2, info.top + size * j], + [info.left + size * i + size * 5 / 8, info.top + size * j + size / 4]], '#FF0000'); + } + } + if (!directions.includes('down') && j != editor.currentFloorData.height - 1) { + var ndirections = movableArray[i][j+1]; + if (ndirections != null && !ndirections.includes('up')) { + core.drawLine(fg, info.left + size * i + size / 4, info.top + size * j + size, info.left + size * i + size * 3 / 4, info.top + size * j + size, '#FF0000', 2); + } else { + core.drawLine(fg, info.left + size * i + size / 3, info.top + size * j + size, info.left + size * i + size * 2 / 3, info.top + size * j + size, '#FF0000', 2); + core.fillPolygon(fg, [[info.left + size * i + size * 3 / 8, info.top + size * j + size * 3 / 4], + [info.left + size * i + size / 2, info.top + size * j + size], + [info.left + size * i + size * 5 / 8, info.top + size * j + size * 3 / 4]], '#FF0000'); + } + } + } + } + return; + } + + for (var i = 0; i < editor.currentFloorData.width; ++i) { + for (var j = 0; j < editor.currentFloorData.height; ++j) { + var color = this._drawEventBlock_getColor(i+","+j); + for(var kk=0,cc;cc=color[kk];kk++){ + fg.fillStyle = cc; + fg.fillRect(info.left + size * i + psize * kk, info.top + size * (j + 1) - psize, psize, psize); + } + } + } +} + +editor.prototype._drawEventBlock_getColor = function (loc) { + var color = []; + if (editor.currentFloorData.events[loc]) + color.push('#FF0000'); + if (editor.currentFloorData.autoEvent[loc]) { + var x = editor.currentFloorData.autoEvent[loc]; + for (var index in x) { + if (x[index] && x[index].data) { + color.push('#FFA500'); + break; + } + } + } + if (editor.currentFloorData.beforeBattle[loc]) + color.push('#0000FF'); + if (editor.currentFloorData.afterBattle[loc]) + color.push('#FFFF00'); + if (editor.currentFloorData.changeFloor[loc]) + color.push('#00FF00'); + if (editor.currentFloorData.afterGetItem[loc]) + color.push('#00FFFF'); + if (editor.currentFloorData.afterOpenDoor[loc]) + color.push('#FF00FF'); + return color; +} + +editor.prototype.drawPosSelection = function () { + this.drawEventBlock(); + var fg=editor.dom.efgCtx; + fg.strokeStyle = 'rgba(255,255,255,0.7)'; + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo, size = info.size, psize = size / 8; + fg.lineWidth = psize; + fg.strokeRect(info.left + editor.pos.x * size + psize, info.top + editor.pos.y * size + psize, size - 2*psize, size - 2*psize); + } else { + fg.lineWidth = 4; + fg.strokeRect(32*editor.pos.x - core.bigmap.offsetX + 4, 32*editor.pos.y - core.bigmap.offsetY + 4, 24, 24); + } +} + +editor.prototype._updateMap_bigmap = function () { + var bm=editor.dom.ebmCtx; + bm.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + bm.fillStyle = '#000000'; + bm.fillRect(0, 0, core.__PIXELS__, core.__PIXELS__); + core.drawThumbnail(editor.currentFloorId, null, {ctx: bm, all: true}); + var width = editor.currentFloorData.width; + var height = editor.currentFloorData.height; + editor.uivalues.bigmapInfo.top = core.__PIXELS__ * Math.max(0, (1 - height / width) / 2); + editor.uivalues.bigmapInfo.left = core.__PIXELS__ * Math.max(0, (1 - width / height) / 2); + editor.uivalues.bigmapInfo.size = core.__PIXELS__ / Math.max(width, height); + this.drawEventBlock(); + this.updateLastUsedMap(); +} + +editor.prototype.updateMap = function () { + var blocks = core.maps._mapIntoBlocks(editor.map.map(function (v) { + return v.map(function (v) { + try { + return v.idnum || v || 0 + } + catch (e) { + console.log("Unable to read idnum from "+v); + return 0; + } + }); + }), null, editor.currentFloorId); + core.status.thisMap.blocks = blocks; + if (editor.uivalues.bigmap) return this._updateMap_bigmap(); + + var updateMap = function () { + core.removeGlobalAnimate(); + editor.dom.canvas.forEach(function (one) { + core.clearMap(one); + }); + core.clearMap('event'); + core.clearMap('event2'); + core.maps._drawMap_drawAll(); + } + updateMap(); + + var drawTile = function (ctx, x, y, tileInfo) { // 绘制一个普通块 + + //ctx.clearRect(x*32, y*32, 32, 32); + if (tileInfo == 0) return; + + if (typeof(tileInfo) == typeof([][0]) || !Object.prototype.hasOwnProperty.call(tileInfo, 'idnum')) {//未定义块画红块 + if (typeof(tileInfo) != typeof([][0]) && Object.prototype.hasOwnProperty.call(tileInfo, 'images')) { + ctx.drawImage(core.material.images[tileInfo.images], 0, tileInfo.y * 32, 32, 32, x * 32, y * 32, 32, 32); + } + ctx.strokeStyle = 'red'; + var OFFSET = 2; + ctx.lineWidth = OFFSET; + ctx.strokeRect(x * 32 + OFFSET, y * 32 + OFFSET, 32 - OFFSET * 2, 32 - OFFSET * 2); + ctx.font = "30px Verdana"; + ctx.textAlign = 'center'; + ctx.fillStyle = 'red'; + ctx.fillText("?", x * 32 + 16, y * 32 + 27); + return; + } + //ctx.drawImage(core.material.images[tileInfo.images], 0, tileInfo.y*32, 32, 32, x*32, y*32, 32, 32); + } + if (editor.map.length * editor.map[0].length < 4096) { + for (var y = 0; y < editor.map.length; y++) { + for (var x = 0; x < editor.map[0].length; x++) { + drawTile(editor.dom.evCtx, x, y, editor.map[y][x]); + editor.dom.canvas.forEach(function (one) { + drawTile(editor.dom[one + 'Ctx'], x, y, editor[one+'map'][y][x]); + }); + } + } + } + + // 绘制地图 end + + editor.drawEventBlock(); + this.updateLastUsedMap(); +} + +editor.prototype.setLastUsedType = function (type) { + if (type == editor.uivalues.lastUsedType) return; + editor.uivalues.lastUsedType = type; + var _buildHtml = function (type, text) { + if (type == null) return "" + text + ""; + else return `${text}`; + } + editor.dom.lastUsedTitle.innerHTML + = type == 'frequent' ? (_buildHtml('recent', '最近使用') + " | " + _buildHtml(null, '最常使用')) + : (_buildHtml(null, '最近使用') + " | " + _buildHtml('frequent', '最常使用')); + this.updateLastUsedMap(); + editor.dom.lastUsedDiv.scrollTop = 0; +} + +editor.prototype.updateLastUsedMap = function () { + var lastUsed = editor.uivalues.lastUsed.sort(function (a, b) { + if ((a.istop || 0) != (b.istop || 0)) return (b.istop || 0) - (a.istop || 0); + return (b[editor.uivalues.lastUsedType] || 0) - (a[editor.uivalues.lastUsedType] || 0); + }); + + // 绘制最近使用事件 + var ctx = editor.dom.lastUsedCtx; + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.strokeStyle = 'rgba(255,128,0,0.85)'; + ctx.fillStyle = 'rgba(255,0,0,0.85)'; + ctx.lineWidth = 4; + for (var i = 0; i < lastUsed.length; ++i) { + try { + var per_row = core.__SIZE__ - 1; + var x = i % per_row, y = parseInt(i / per_row); + var info = lastUsed[i]; + if (!info || !info.images) continue; + if (info.isTile && core.material.images.tilesets[info.images]) { + ctx.drawImage(core.material.images.tilesets[info.images], 32 * info.x, 32 * info.y, 32, 32, x*32, y*32, 32, 32); + } else if (info.images == 'autotile' && core.material.images.autotile[info.id]) { + ctx.drawImage(core.material.images.autotile[info.id], 0, 0, 32, 32, x * 32, y * 32, 32, 32); + } else { + var per_height = info.images.endsWith('48') ? 48 : 32; + ctx.drawImage(core.material.images[info.images], 0, info.y * per_height, 32, per_height, x * 32, y * 32, 32, 32); + } + if (info.istop) { + ctx.fillRect(32 * x, 32 * y + 24, 8, 8); + } + if (selectBox.isSelected() && editor.info.id == info.id) { + ctx.strokeRect(32 * x + 2, 32 * y + 2, 28, 28); + } + } catch (e) {} + } +} + +editor.prototype.setViewport=function (x, y) { + core.bigmap.offsetX = core.clamp(x, 0, 32*core.bigmap.width-core.__PIXELS__); + core.bigmap.offsetY = core.clamp(y, 0, 32*core.bigmap.height-core.__PIXELS__); + editor.viewportLoc = editor.viewportLoc || {}; + editor.viewportLoc[editor.currentFloorId] = [core.bigmap.offsetX, core.bigmap.offsetY]; + core.control.updateViewport(); + editor.config.set('viewportLoc', editor.viewportLoc[editor.currentFloorId]); + editor.buildMark(); + editor.drawPosSelection(); +} + +editor.prototype.moveViewport=function(x,y){ + editor.setViewport(core.bigmap.offsetX+32*x, core.bigmap.offsetY+32*y); + printi("你可以按【大地图】(或F键)快捷切换大地图模式"); +} + +/////////// 界面交互相关 /////////// + +editor.prototype.drawInitData = function (icons) { + var ratio = 1; + var images = core.material.images; + var maxHeight = 700; + var sumWidth = 0; + editor.widthsX = {}; + editor.uivalues.folded = editor.config.get('folded', false); + // editor.uivalues.folded = true; + editor.uivalues.foldPerCol = editor.config.get('foldPerCol', 50); + // var imgNames = Object.keys(images); //还是固定顺序吧; + editor.setLastUsedType(editor.config.get('lastUsedType', 'recent')); + var ids = editor.ids.map(function (x) {return x.id || "";}); + editor.uivalues.lastUsed = editor.config.get("lastUsed", []).filter(function (one) { + return ids.indexOf(one.id) >= 0; + }); + var imgNames = ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"]; + + var splitCanvas = document.createElement('canvas'); + var splitCtx = splitCanvas.getContext('2d'); + splitCtx.imageSmoothingEnabled = false; + + var splitImage = function (image, width, height) { + if (image.width == width && image.height == height) { + return [image]; + } + var ans = []; + for (var j = 0; j < image.height; j += h) { + var h = Math.min(height, image.height - j); + splitCanvas.width = width; + splitCanvas.height = h; + core.drawImage(splitCtx, image, 0, j, width, h, 0, 0, width, h); + var data = new Image(); + data.src = splitCanvas.toDataURL("image/png"); + ans.push(data); + } + return ans; + } + + for (var ii = 0; ii < imgNames.length; ii++) { + var img = imgNames[ii], tempy = 0; + if (img == 'autotile') { + var autotiles = images[img]; + for (var im in autotiles) { + tempy += editor.uivalues.folded ? 32 : autotiles[im].height; + } + var tempx = editor.uivalues.folded ? 32 : 3 * 32; + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + tempx) / 32, tempy]; + sumWidth += tempx; + maxHeight = Math.max(maxHeight, tempy); + continue; + } + var width = images[img].width, height = images[img].height, mh = height; + if (editor.uivalues.folded) { + if (img == 'terrains') { + width = Math.ceil((height / 32 + 2) / editor.uivalues.foldPerCol) * 32; + if (width > 32) mh = 32 * editor.uivalues.foldPerCol; + } else { + var per_height = (img == 'enemy48' || img == 'npc48' ? 48 : 32); + width = Math.ceil(height / per_height / editor.uivalues.foldPerCol) * 32; + if (width > 32) mh = per_height * editor.uivalues.foldPerCol; + } + } + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + width) / 32, height]; + sumWidth += width; + maxHeight = Math.max(maxHeight, mh + 64); + } + var tilesets = images.tilesets; + for (var ii in core.tilesets) { + var img = core.tilesets[ii]; + editor.widthsX[img] = [img, sumWidth / 32, (sumWidth + tilesets[img].width) / 32, tilesets[img].height]; + sumWidth += tilesets[img].width; + maxHeight = Math.max(maxHeight, tilesets[img].height); + } + + var fullWidth = ~~(sumWidth * ratio); + var fullHeight = ~~(maxHeight * ratio); + + /* + if (fullWidth > edata.width) edata.style.width = (edata.width = fullWidth) / ratio + 'px'; + edata.style.height = (edata.height = fullHeight) / ratio + 'px'; + */ + var iconImages = document.getElementById('iconImages'); + iconImages.style.width = (iconImages.width = fullWidth) / ratio + 'px'; + iconImages.style.height = (iconImages.height = fullHeight) / ratio + 'px'; + document.body.ondrop = function (e) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return false; + } + document.body.ondragover = function (e) { + e.preventDefault(); + e.stopPropagation(); + e.stopImmediatePropagation(); + return false; + } + var drawImage = function (image, x, y, cls) { + image.style.left = x + 'px'; + image.style.top = y + 'px'; + image.ondrop = (function (cls) { return function (e) { + e.stopPropagation(); + e.preventDefault(); + if (!cls) return false; + var files = e.dataTransfer.files; + if (files.length >= 1) { + var file = files[0]; + if (file.type == 'image/png') { + editor.uifunctions.dragImageToAppend(file, cls); + } else { + printe('只支持png图片的快速追加!'); + } + } + return false; + }; })(cls); + image.ondragover = function(e) { + e.stopPropagation(); + e.preventDefault(); + e.dataTransfer.dropEffect = 'copy'; + return false; + } + iconImages.appendChild(image); + } + + var nowx = 0, nowy = 0; + for (var ii = 0; ii < imgNames.length; ii++) { + var img = imgNames[ii]; + if (img == 'terrains') { + (function(image,nowx){ + if (image.complete) { + drawImage(image, nowx, 32); + core.material.images.airwall = image; + delete(editor.airwallImg); + } else image.onload = function () { + drawImage(image, nowx, 32); + core.material.images.airwall = image; + delete(editor.airwallImg); + editor.updateMap(); + } + })(editor.airwallImg,nowx); + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var canvas = document.createElement("canvas"); + canvas.width = 32; + canvas.height = images[img].height + 64; + canvas.getContext('2d').drawImage(images[img], 0, 64); + var subimgs = splitImage(canvas, 32, editor.uivalues.foldPerCol * 32); + for (var i = 0; i < subimgs.length; i++) { + drawImage(subimgs[i], nowx, 0, img); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 32*2, img); + nowx += images[img].width; + } + continue; + } + if (img == 'autotile') { + var autotiles = images[img]; + var tempx = editor.uivalues.folded ? 32 : 96; + for (var im in autotiles) { + var tempy = editor.uivalues.folded ? 32 : autotiles[im].height; + var subimgs = splitImage(autotiles[im], tempx, tempy); + drawImage(subimgs[0], nowx, nowy, img); + nowy += tempy; + } + nowx += tempx; + continue; + } + if (editor.uivalues.folded) { + // --- 单列 & 折行 + var per_height = img.endsWith('48') ? 48 : 32; + var subimgs = splitImage(images[img], 32, editor.uivalues.foldPerCol * per_height); + for (var i = 0; i < subimgs.length; i++) { + drawImage(subimgs[i], nowx, 0, img); + nowx += 32; + } + } + else { + drawImage(images[img], nowx, 0, img); + nowx += images[img].width; + } + } + for (var ii in core.tilesets) { + var img = core.tilesets[ii]; + drawImage(tilesets[img], nowx, 0); + nowx += tilesets[img].width; + } + //editor.mapInit(); +} + +editor.prototype.buildMark = function(){ + // 生成定位编号 + var arrColMark=document.getElementById('arrColMark'); + var arrRowMark=document.getElementById('arrRowMark'); + var mapColMark=document.getElementById('mapColMark'); + var mapRowMark=document.getElementById('mapRowMark'); + var buildMark = function (offsetX,offsetY) { + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (i+offsetX) + '
'; + colNum += tpl; + } + arrColMark.innerHTML = '' + colNum + ''; + mapColMark.innerHTML = '' + colNum + ''; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (i+offsetY) + '
'; + rowNum += tpl; + } + arrRowMark.innerHTML = rowNum; + mapRowMark.innerHTML = rowNum; + } + var buildMark_mobile = function (offsetX,offsetY) { + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; + colNum += tpl; + } + arrColMark.innerHTML = '' + colNum + ''; + //mapColMark.innerHTML = '' + colNum + ''; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '' + (' '+i).slice(-2).replace(' ',' ') + '
'; + rowNum += tpl; + } + arrRowMark.innerHTML = rowNum; + //mapRowMark.innerHTML = rowNum; + //===== + var colNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
' + (' '+(i+offsetX)).slice(-2).replace(' ',' ') + '
'; + colNum += tpl; + } + mapColMark.innerHTML = '
' + colNum + '
'; + var rowNum = ' '; + for (var i = 0; i < core.__SIZE__; i++) { + var tpl = '
' + (' '+(i+offsetY)).slice(-2).replace(' ',' ') + '
'; + rowNum += tpl; + } + mapRowMark.innerHTML = rowNum; + } + if(editor.isMobile){ + buildMark_mobile(core.bigmap.offsetX/32,core.bigmap.offsetY/32); + } else { + buildMark(core.bigmap.offsetX/32,core.bigmap.offsetY/32); + } +} + +editor.prototype.setSelectBoxFromInfo=function(thisevent, scrollTo){ + var pos={x: 0, y: 0, images: "terrains"}; + var ysize = 32; + if(thisevent==0){ + } else if (thisevent.idnum==17){ + pos.y=1; + } else { + pos.x=editor.widthsX[thisevent.images][1]; + pos.y=thisevent.y; + if(thisevent.x)pos.x+=thisevent.x; + ysize = thisevent.images.endsWith('48') ? 48 : 32; + if (editor.uivalues.folded && core.tilesets.indexOf(thisevent.images)==-1) { + pos.x += Math.floor(pos.y / editor.uivalues.foldPerCol); + pos.y %= editor.uivalues.foldPerCol; + } + if(pos.x == 0) pos.y+=2; + } + if (!editor.isMobile && scrollTo) { + editor.dom.iconLib.scrollLeft = pos.x * 32 - editor.dom.iconLib.offsetWidth / 2; + editor.dom.iconLib.scrollTop = pos.y * ysize - editor.dom.iconLib.offsetHeight / 2; + } + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + editor.dom.dataSelection.style.width = 32 - 6 + 'px'; + setTimeout(function(){ + selectBox.isSelected(true); + editor.updateLastUsedMap(); + }); + editor.info = JSON.parse(JSON.stringify(thisevent)); + editor.pos=pos; + editor_mode.onmode('nextChange'); + editor_mode.onmode('enemyitem'); + editor.uifunctions.showBlockInfo(JSON.parse(JSON.stringify(thisevent))); +} + +editor.prototype.addUsedFlags = function (s) { + s.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + s.replace(/flags\.([a-zA-Z_]\w*)/g, function (s0, s1) { + editor.used_flags[s1] = true; return s0; + }); + if (window.flags) { + for (var s in editor.used_flags) { + if (!(s in window.flags)) { + window.flags[s] = null; + } + } + } +} + +editor.prototype.listen = function () { + // 移动至 editor_listen.js +}//绑定事件 + +editor.prototype.mobile_listen=function(){ + // 移动至 editor_listen.js +} + + + + +editor = new editor(); \ No newline at end of file diff --git a/_server/editor_blockly.js b/_server/editor_blockly.js new file mode 100644 index 0000000..4f0f62d --- /dev/null +++ b/_server/editor_blockly.js @@ -0,0 +1,1213 @@ +editor_blockly = function () { + + var editor_blockly = { entryType: 'event' }; + + editor.uivalues.disableBlocklyReplace = editor.config.get("disableBlocklyReplace", false); + var replaceCheckbox = document.getElementById('blocklyReplace'); + replaceCheckbox.checked = !editor.uivalues.disableBlocklyReplace; + + editor_blockly.triggerReplace = function () { + editor.uivalues.disableBlocklyReplace = !replaceCheckbox.checked; + editor.config.set("disableBlocklyReplace", !replaceCheckbox.checked); + if (MotaActionFunctions) MotaActionFunctions.disableReplace = !replaceCheckbox.checked; + alert("已" + (replaceCheckbox.checked ? "开启" : "关闭") + "中文变量名替换!\n关闭并重开事件编辑器以生效。"); + } + + editor.uivalues.disableBlocklyExpandCompare = editor.config.get("disableBlocklyExpandCompare", false); + var expandCompareCheckbox = document.getElementById('blocklyExpandCompare'); + expandCompareCheckbox.checked = !editor.uivalues.disableBlocklyExpandCompare; + + editor_blockly.triggerExpandCompare = function () { + editor.uivalues.disableBlocklyExpandCompare = !expandCompareCheckbox.checked; + editor.config.set("disableBlocklyExpandCompare", !expandCompareCheckbox.checked); + if (MotaActionFunctions) MotaActionFunctions.disableExpandCompare = !expandCompareCheckbox.checked; + } + + var input_ = ''; + editor_blockly.runOne = function () { + //var printf = console.log; + //var printf = function(){}; + var grammerFile = input_; + converter = new Converter().init(); + converter.generBlocks(grammerFile); + //printf(converter.blocks); + converter.renderGrammerName(); + //converter.generToolbox(); + converter.generMainFile(); + //printf(converter.mainFile.join('')); + //console.log(converter); + + + var script = document.createElement('script'); + script.innerHTML = converter.mainFile[5] + editor_blocklyconfig; + document.body.appendChild(script); + } + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState != 4) return; + if (xhr.status != 200) { + alert("图块描述文件加载失败, 请在'启动服务.exe'中打开编辑器"); + return; + } + input_ = xhr.responseText; + editor_blockly.runOne(); + MotaActionFunctions.disableReplace = editor.uivalues.disableBlocklyReplace; + MotaActionFunctions.disableExpandCompare = editor.uivalues.disableBlocklyExpandCompare; + } + xhr.open('GET', '_server/MotaAction.g4', true); + xhr.send(null); + + var codeAreaHL = CodeMirror.fromTextArea(document.getElementById("codeArea"), { + lineNumbers: true, + matchBrackets: true, + lineWrapping: true, + continueComments: "Enter", + extraKeys: { "Ctrl-Q": "toggleComment" }, + }); + codeAreaHL.on('changes', function () { + editor_blockly.highlightParse(!changeFromBlockly); + changeFromBlockly = false; + }); + var changeFromBlockly = false; + var shouldNotifyParse = false; + + editor_blockly.showXML = function () { + var xml = Blockly.Xml.workspaceToDom(editor_blockly.workspace); + var xml_text = Blockly.Xml.domToPrettyText(xml); + console.log(xml_text); + var xml_text = Blockly.Xml.domToText(xml); + console.log(xml_text); + console.log(xml); + } + + editor_blockly.runCode = function () { + // Generate JavaScript code and run it. + window.LoopTrap = 1000; + Blockly.JavaScript.INFINITE_LOOP_TRAP = + 'if (--window.LoopTrap == 0) throw "Infinite loop.";\n'; + var code = Blockly.JavaScript.workspaceToCode(editor_blockly.workspace); + Blockly.JavaScript.INFINITE_LOOP_TRAP = null; + try { + eval('obj=' + code); + console.log(obj); + } catch (e) { + alert(e); + } + } + + editor_blockly.setValue = function (value) { + changeFromBlockly = true; + codeAreaHL.setValue(value); + } + + editor_blockly.parse = function () { + MotaActionFunctions.parse( + eval('obj=' + codeAreaHL.getValue().replace(/[<>&]/g, function (c) { + return { '<': '<', '>': '>', '&': '&' }[c]; + }).replace(/\\(r|f|i|c|d|e|g|z)/g, '\\\\$1')), + editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType + ); + } + + editor_blockly.id = ''; + + var _lastOpenPosition = {}; + + editor_blockly.import = function (id_, args) { + var thisTr = document.getElementById(id_); + if (!thisTr) return false; + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var type = args.type; + if (!type) return false; + editor_blockly.id = id_; + editor_blockly.setValue(input.value); + editor_blockly.entryType = type; + editor_blockly.parse(); + editor_blockly.show(); + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + editor_blockly.workspace.scroll(0, _lastOpenPosition[_offsetIndex] || 0) + return true; + } + + var blocklyWidgetDiv = document.getElementsByClassName('blocklyWidgetDiv'); + editor_blockly.show = function () { + if (typeof (selectBox) !== typeof (undefined)) selectBox.isSelected(false); + document.getElementById('left6').style = ''; + for (var ii = 0, node; node = blocklyWidgetDiv[ii]; ii++) { + node.style.zIndex = 201; + node.style.opacity = ''; + } + } + editor_blockly.hide = function () { + document.getElementById('left6').style = 'z-index:-1;opacity: 0;'; + for (var ii = 0, node; node = blocklyWidgetDiv[ii]; ii++) { + node.style.zIndex = -1; + node.style.opacity = 0; + } + } + + var blocklyParseBtn = document.getElementById('blocklyParse'); + editor_blockly.highlightParse = function (shouldHighLight) { + if (shouldNotifyParse == shouldHighLight) return; + shouldNotifyParse = shouldHighLight; + if (shouldHighLight) blocklyParseBtn.classList.add('highlight'); + else blocklyParseBtn.classList.remove('highlight'); + } + + editor_blockly.cancel = function () { + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + _lastOpenPosition[_offsetIndex] = editor_blockly.workspace.scrollY; + + editor_blockly.id = ''; + editor_blockly.hide(); + } + + editor_blockly.confirm = function (keep) { + if (!editor_blockly.id) { + editor_blockly.id = ''; + return; + } + if (shouldNotifyParse) { + alert('你尚未解析修改后的内容,请进行解析或放弃操作'); + return; + } + if (editor_blockly.workspace.topBlocks_.length >= 2) { + editor_blockly.setValue('入口方块只能有一个'); + return; + } + var eventType = editor_blockly.entryType; + if (editor_blockly.workspace.topBlocks_.length == 1) { + var blockType = editor_blockly.workspace.topBlocks_[0].type; + if (blockType !== eventType + '_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')) { + editor_blockly.setValue('入口方块类型错误'); + return; + } + } + var setvalue = function (value) { + var thisTr = document.getElementById(editor_blockly.id); + var input = thisTr.children[2].children[0].children[0]; + input.value = value; + if (!keep) { + editor_blockly.id = ''; + editor_blockly.hide(); + } + else alert('保存成功!'); + input.onchange(); + } + if (codeAreaHL.getValue() === '') { + eventType === 'shop' ? setvalue('[]') : setvalue('null'); + return; + } + var code = Blockly.JavaScript.workspaceToCode(editor_blockly.workspace); + code = code.replace(/\\(i|c|d|e|g|z)/g, '\\\\$1'); + eval('var obj=' + code); + if (this.checkAsync(obj) && confirm("警告!存在不等待执行完毕的事件但却没有用【等待所有异步事件处理完毕】来等待" + + "它们执行完毕,这样可能会导致录像检测系统出问题。\n你要返回修改么?")) return; + + var _offsetIndex = [editor_blockly.entryType, editor.pos.x, editor.pos.y, editor.currentFloorId].join(":"); + _lastOpenPosition[_offsetIndex] = editor_blockly.workspace.scrollY; + setvalue(JSON.stringify(obj)); + } + + // 检查"不等待处理完毕" + editor_blockly.checkAsync = function (obj) { + if (!(obj instanceof Array)) return false; + var hasAsync = false; + for (var i = 0; i < obj.length; ++i) { + var one = obj[i]; + if (one.type == 'if' && (this.checkAsync(one['true']) || this.checkAsync(one['false']))) + return true; + if ((one.type == 'while' || one.type == 'dowhile') && this.checkAsync(one.data)) + return true; + if (one.type == 'confirm' && (this.checkAsync(one.yes) || this.checkAsync(one.no))) + return true; + if (one.type == 'choices') { + var list = one.choices; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'switch') { + var list = one.caseList; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'wait') { + var list = one.data; + if (list instanceof Array) { + for (var j = 0; j < list.length; j++) { + if (this.checkAsync(list[j].action)) return true; + } + } + } + if (one.type == 'previewUI' && this.checkAsync(one.action)) return true; + if (one.async && one.type != 'animate' && one.type != 'function' && one.type != 'text') hasAsync = true; + if (one.type == 'waitAsync' || one.type == 'stopAsync') hasAsync = false; + } + return hasAsync; + } + + var _isTextAttributeSet = false; + + editor_blockly.previewBlock = function (b, args) { + + var previewTextDrawing = function (content) { + var arr = []; + content.replace(/(\f|\\f)\[(.*?)]/g, function (text, sympol, str) { + var list = str.split(","); + if (list.length == 3 || list.length == 5 || list.length >= 9) { + var name = list[0]; + var obj = { "type": "drawImage" }; + if (name.endsWith(":o") || name.endsWith(":x") || name.endsWith(":y")) { + obj.reverse = name.substring(name.length - 2); + name = name.substring(0, name.length - 2); + } + obj.image = name; + obj.x = parseFloat(list[1]); + obj.y = parseFloat(list[2]); + if (list.length >= 5) { + obj.w = parseFloat(list[3]); + obj.h = parseFloat(list[4]); + } + if (list.length >= 9) { + obj.x1 = parseFloat(list[5]); + obj.y1 = parseFloat(list[6]); + obj.w1 = parseFloat(list[7]); + obj.h1 = parseFloat(list[8]); + } + if (list.length >= 10) { + arr.push({ "type": "setAttribute", "alpha": parseFloat(list[9]) }); + } + if (list.length >= 11) { + obj.angle = parseFloat(list[10]); + } + arr.push(obj); + } + return ""; + }); + editor.uievent.previewUI(arr); + return true; + } + + try { + // 特殊处理立绘 + if (b.type == 'textDrawing') { + previewTextDrawing(Blockly.JavaScript.blockToCode(b)); + return true; + } + + var code = "[" + Blockly.JavaScript.blockToCode(b).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1') + "]"; + eval("var obj=" + code); + if (obj.length == 0) return true; + obj = obj[0]; + switch (b.type) { + case 'text_0_s': + case 'text_1_s': + case 'text_2_s': + case 'choices_s': + case 'confirm_s': + if (!_isTextAttributeSet) { + alert('警告!你尚未设置用于预览的剧情文本的属性,将采用默认属性进行预览。\n你可以双击“设置剧情文本的属性”事件来设置用于预览的属性。'); + core.status.textAttribute = core.clone(core.initStatus.textAttribute); + _isTextAttributeSet = true; + } + editor.uievent.previewUI([obj]); + break; + case 'setText_s': // 设置剧情文本的属性 + _isTextAttributeSet = true; + core.status.textAttribute = core.clone(core.initStatus.textAttribute); + core.setTextAttribute(obj); + alert('已成功设置此属性为显示文章的预览属性!') + break; + case 'waitContext_2': // 等待用户操作坐标预览 + editor.uievent.previewUI([{ + "type": "fillRect", "x": obj.px[0], "y": obj.py[0], + "width": "(" + obj.px[1] + ")-(" + obj.px[0] + ")", "height": "(" + obj.py[1] + ")-(" + obj.py[0] + ")", + "style": "rgba(255,0,0,0.5)" + }]); + break; + case 'showImage_s': // 显示图片 + case 'showImage_1_s': + if (obj.sloc) { + editor.uievent.previewUI([ + { type: "setAttribute", alpha: obj.opacity }, + { + type: "drawImage", image: obj.image, x: obj.sloc[0], y: obj.sloc[1], w: obj.sloc[2], h: obj.sloc[3], + x1: obj.loc[0], y1: obj.loc[1], w1: obj.loc[2], h1: obj.loc[3], reverse: obj.reverse + } + ]); + } else { + editor.uievent.previewUI([ + { type: "setAttribute", alpha: obj.opacity }, + { type: "drawImage", image: obj.image, x: obj.loc[0], y: obj.loc[1], w: obj.loc[2], h: obj.loc[3], reverse: obj.reverse } + ]); + } + break; + case 'showGif_s': // 显示动图 + if (obj.name && obj.loc) { + editor.uievent.previewUI([{ type: "drawImage", image: obj.name, x: obj.loc[0], y: obj.loc[1] }]); + } + break; + case 'setCurtain_0_s': // 更改色调 + if (obj.color) { + editor.uievent.previewUI([{ type: "fillRect", x: 0, y: 0, width: core.__PIXELS__, height: core.__PIXELS__, style: obj.color }]); + } + break; + case 'floorOneImage': // 楼层贴图 + obj.w = obj.w / (obj.frame || 1); + editor.uievent.previewUI([ + { + type: "drawImage", image: obj.name, x: obj.sx || 0, y: obj.sy || 0, w: obj.w, h: obj.h, + x1: obj.x, y1: obj.y, w1: obj.w, h1: obj.h, reverse: obj.reverse + } + ]); + break; + case 'previewUI_s': // 预览 + editor.uievent.previewUI(obj.action); + break; + default: + if (b.type.startsWith(obj.type)) { + editor.uievent.previewUI([obj]); + } + } + } catch (ee) { console.error(ee) } + + } + + editor_blockly.selectMaterial = function (b, material) { + var value = b.getFieldValue(material[1]); + value = main.nameMap[value] || value; + editor.uievent.selectMaterial([value], '请选择素材', material[0], function (one) { + if (b.type == 'animate_s' || b.type == 'animate_1_s' || b.type == 'nameMapAnimate') { + return /^[-A-Za-z0-9_.]+\.animate$/.test(one) ? one.substring(0, one.length - 8) : null; + } + return /^[-A-Za-z0-9_.]+$/.test(one) ? one : null; + }, function (value) { + if (value instanceof Array && value.length > 0) { + value = value[0]; + // 检测是否别名替换 + for (var name in main.nameMap) { + if (main.nameMap[name] == value) { + if (confirm("检测到该文件存在别名:" + name + "\n是否使用别名进行替换?")) { + b.setFieldValue(name, material[1]); + return; + } else { + break; + } + } + } + b.setFieldValue(value, material[1]); + } + }); + } + + editor_blockly.doubleclicktext = function (b, f) { + var value = b.getFieldValue(f); + //多行编辑 + editor_multi.multiLineEdit(value, b, f, { 'lint': f === 'RawEvalString_0' }, function (newvalue, b, f) { + if (!f.startsWith('EvalString_Multi')) { + newvalue = newvalue.split('\n').join('\\n'); + } + b.setFieldValue(newvalue, f); + }); + } + + editor_blockly.doubleClickBlock = function (blockId) { + var b = editor_blockly.workspace.getBlockById(blockId); + + if (b && MotaActionBlocks[b.type].previewBlock) { + editor_blockly.previewBlock(b, MotaActionBlocks[b.type].previewBlock) + return; + } + + if (b && MotaActionBlocks[b.type].selectPoint) { // selectPoint + editor_blockly.selectPoint(b, eval(MotaActionBlocks[b.type].selectPoint)); + return; + } + + if (b && MotaActionBlocks[b.type].material) { + editor_blockly.selectMaterial(b, JSON.parse(MotaActionBlocks[b.type].material)); + return; + } + + if (b && MotaActionBlocks[b.type].doubleclicktext) { //多行编辑 + editor_blockly.doubleclicktext(b, MotaActionBlocks[b.type].doubleclicktext); + return; + } + } + + editor_blockly.selectPointFromButton = function () { + var b = Blockly.selected; + if (b && MotaActionBlocks[b.type].selectPoint) { + editor_blockly.selectPoint(b, eval(MotaActionBlocks[b.type].selectPoint)); + return; + } else { + editor.uievent.selectPoint(); + } + } + + editor_blockly.showKeyCodes = function () { + alert('键值查询表:\nA65 B66 C67 D68 E69 F70 G71 H72 I73 J74 K75 L76 M77\n' + + 'N78 O79 P80 Q81 R82 S83 T84 U85 V86 W87 X88 Y89 Z90\n0:48 1:49 2:50 3:51 4:52 5:53 6:54 7:55 8:56 9:57\n' + + '空格:13 回车:32 ESC:27 后退:8 Tab:9 Shift:16 Ctrl:17 Alt:18\nPgUp:33 PgDn:34 左:37 上:38 右:39 下:40\n更多键值请自行百度查表') + } + + editor_blockly.lastUsedType = [ + 'text_0_s', + 'comment_s', + 'show_s', + 'hide_s', + 'setValue_s', + 'if_s', + 'while_s', + 'battle_s', + 'openDoor_s', + 'choices_s', + 'setText_s', + 'exit_s', + 'sleep_s', + 'setBlock_s', + 'insert_1_s' + ]; // 最常用的15个事件 + editor_blockly.lastUsedTypeNum = 15; + + editor_blockly.addIntoLastUsedType = function (blockId) { + var b = editor_blockly.workspace.getBlockById(blockId); + if (!b) return; + var blockType = b.type; + if (!blockType || blockType.indexOf("_s") !== blockType.length - 2 || blockType === 'pass_s') return; + editor_blockly.lastUsedType = editor_blockly.lastUsedType.filter(function (v) { return v !== blockType; }); + if (editor_blockly.lastUsedType.length >= editor_blockly.lastUsedTypeNum) + editor_blockly.lastUsedType.pop(); + editor_blockly.lastUsedType.unshift(blockType); + + document.getElementById("searchBlock").value = ''; + } + + // Index from 1 - 9 + editor_blockly.openToolbox = function (index) { + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); + } + editor_blockly.reopenToolbox = function (index) { + if (index < 0) index += editor_blockly.workspace.toolbox_.tree_.children_.length; + editor_blockly.workspace.toolbox_.tree_.setSelectedItem(editor_blockly.workspace.toolbox_.tree_.children_[index]); + editor_blockly.workspace.getFlyout().show(editor_blockly.workspace.toolbox_.tree_.children_[index].blocks); + } + + editor_blockly.closeToolbox = function () { + editor_blockly.workspace.toolbox_.clearSelection(); + } + + var searchInput = document.getElementById("searchBlock"); + searchInput.onfocus = function () { + editor_blockly.reopenToolbox(-1); + } + + searchInput.oninput = function () { + editor_blockly.reopenToolbox(-1); + } + + editor_blockly.searchBlock = function (value) { + if (value == null) value = searchInput.value; + value = value.toLowerCase(); + if (value == '') return editor_blockly.lastUsedType; + var results = []; + for (var name in MotaActionBlocks) { + if (typeof name !== 'string' || name.indexOf("_s") !== name.length - 2) continue; + var block = MotaActionBlocks[name]; + if (block && block.json) { + if ((block.json.type || "").toLowerCase().indexOf(value) >= 0 + || (block.json.message0 || "").toLowerCase().indexOf(value) >= 0 + || (block.json.tooltip || "").toLowerCase().indexOf(value) >= 0) { + results.push(name); + if (results.length >= editor_blockly.lastUsedTypeNum) + break; + } + } + } + + return results.length == 0 ? editor_blockly.lastUsedType : results; + } + + // ------ select point ------ + + editor_blockly.selectPoint = function (block, arr) { + + var floorId = editor.currentFloorId, pos = editor.pos, x = pos.x, y = pos.y; + + var xv = block.getFieldValue(arr[0]), yv = block.getFieldValue(arr[1]); + if (xv != null) x = xv; + if (yv != null) y = yv; + if (arr[2] != null) floorId = block.getFieldValue(arr[2]) || floorId; + + editor.uievent.selectPoint(floorId, x, y, false, function (fv, xv, yv) { + if (!arr) return; + if (arr[2] != null) { + if (fv != editor.currentFloorId || editor_blockly.entryType == 'commonEvent') block.setFieldValue(fv, arr[2]); + else block.setFieldValue(arr[3] ? fv : "", arr[2]); + } + block.setFieldValue(xv + "", arr[0]); + block.setFieldValue(yv + "", arr[1]); + if (block.type == 'changeFloor_m' || block.type == 'changeFloor_s') { + block.setFieldValue("floorId", "Floor_List_0"); + block.setFieldValue("loc", "Stair_List_0"); + } + }); + } + + editor_blockly.getAutoCompletions = function (content, type, name, pb) { + // --- content为当前框中输入内容;将返回一个列表,为后续所有可补全内容 + + // console.log(type, name); + + // 检查 status:xxx,item:xxx和flag:xxx + var index = Math.max(content.lastIndexOf(":"), content.lastIndexOf(":")); + if (index >= 0) { + var ch = content.charAt(index); + var before = content.substring(0, index), token = content.substring(index + 1); + if (/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]*$/.test(token)) { + if (before.endsWith("状态") || (ch == ':' && before.endsWith("status"))) { + var list = Object.keys(core.status.hero); + if (before.endsWith("状态") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceStatusList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("物品") || (ch == ':' && before.endsWith("item"))) { + var list = Object.keys(core.material.items); + if (before.endsWith("物品") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceItemList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + else if (before.endsWith("变量") || (ch == ':' && before.endsWith("flag"))) { + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } else if (before.endsWith("怪物") || (ch == ':' && before.endsWith("enemy"))) { + var list = Object.keys(core.material.enemys); + if (before.endsWith("怪物") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceEnemyList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }) + } else { + var index2 = Math.max(content.lastIndexOf(":", index - 1), content.lastIndexOf(":", index - 1)); + var ch2 = content.charAt(index2); + if (index2 >= 0) { + before = content.substring(0, index2); + if (before.endsWith("怪物") || (ch == ':' && ch2 == ':' && before.endsWith("enemy"))) { + var list = MotaActionBlocks['EnemyId_List'].options.map(function (v) { return v[1] }); + if (before.endsWith("怪物") && MotaActionFunctions) { + list = MotaActionFunctions.pattern.replaceEnemyValueList.map(function (v) { + return v[1]; + }).concat(list); + } + return list.filter(function (one) { + return one != token && one.startsWith(token); + }) + } + } + + } + } + } + + // 提供 core.xxx 的补全 + index = content.lastIndexOf("core."); + if (index >= 0) { + var s = content.substring(index + 5); + if (/^[\w.]*$/.test(s)) { + var tokens = s.split("."); + var now = core, prefix = tokens[tokens.length - 1]; + for (var i = 0; i < tokens.length - 1; ++i) { + now = now[tokens[i]]; + if (now == null) break; + } + if (now != null) { + var candidates = []; + for (var i in now) { + candidates.push(i); + } + return candidates.filter(function (one) { + return one != prefix && one.startsWith(prefix); + }).sort(); + } + } + } + + // 提供 flags.xxx 补全 + index = content.lastIndexOf("flags."); + if (index >= 0) { + var token = content.substring(index + 6); + return Object.keys(editor.used_flags || {}).filter(function (one) { + return one != token && one.startsWith(token) + && /^[a-zA-Z_]\w*$/.test(one); + }).sort(); + } + + // 提供 hero.xxx 补全 + index = content.lastIndexOf("hero."); + if (index >= 0) { + var token = content.substring(index + 6); + return Object.keys(core.status.hero).filter(function (one) { + return one != token && one.startsWith(token); + }).sort(); + } + + // 提供 IdText_0 的补全 + if (type == 'idIdList_e' && name == 'IdText_0') { + var list = []; + switch (pb.getFieldValue('Id_List_0')) { + case 'status': + list = Object.keys(core.status.hero); + if (MotaActionFunctions && replaceCheckbox.checked) { + list = MotaActionFunctions.pattern.replaceStatusList.map(function (v) { + return v[1]; + }).concat(list); + } + break; + case 'item': + list = Object.keys(core.material.items); + if (MotaActionFunctions && replaceCheckbox.checked) { + list = MotaActionFunctions.pattern.replaceItemList.map(function (v) { + return v[1]; + }).concat(list); + } + break; + case 'flag': + list = Object.keys(editor.used_flags || {}); + break; + } + return list.filter(function (one) { + return one != content && one.startsWith(content); + }).sort(); + } + + var namesObj = {}; + + namesObj.allIds = ["this"].concat(core.getAllIconIds()); + namesObj.allIconIds = namesObj.allIds.concat(Object.keys(core.statusBar.icons).filter(function (x) { + return core.statusBar.icons[x] instanceof Image; + })); + namesObj.allImages = Object.keys(core.material.images.images) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.images.images[main.nameMap[one]]; })); + namesObj.allEnemys = Object.keys(core.material.enemys); + if (MotaActionFunctions && !MotaActionFunctions.disableReplace) { + namesObj.allEnemys = namesObj.allEnemys.concat(MotaActionFunctions.pattern.replaceEnemyList.map(function (x) { + return x[1]; + })) + } + namesObj.allItems = Object.keys(core.material.items); + namesObj.allEquips = namesObj.allItems.filter(function (one) { return core.material.items[one].cls == 'equips' }); + if (MotaActionFunctions && !MotaActionFunctions.disableReplace) { + namesObj.allItems = namesObj.allItems.concat(MotaActionFunctions.pattern.replaceItemList.map(function (x) { + return x[1]; + })); + namesObj.allEquips = namesObj.allEquips.concat(MotaActionFunctions.pattern.replaceItemList.filter(function (x) { + return namesObj.allEquips.includes(x[0]); + }).map(function (x) { return x[1]; })); + } + namesObj.allAnimates = Object.keys(core.material.animates) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.animates[main.nameMap[one]]; })); + namesObj.allBgms = Object.keys(core.material.bgms) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.bgms[main.nameMap[one]]; })); + namesObj.allSounds = Object.keys(core.material.sounds) + .concat(Object.keys(main.nameMap).filter(function (one) { return core.material.sounds[main.nameMap[one]]; }));; + namesObj.allShops = Object.keys(core.status.shops); + namesObj.allFloorIds = core.floorIds; + namesObj.allColors = ["aqua(青色)", "black(黑色)", "blue(蓝色)", "fuchsia(品红色)", "gray(灰色)", "green(深绿色)", "lime(绿色)", + "maroon(深红色)", "navy(深蓝色)", "gold(金色)", "olive(黄褐色)", "orange(橙色)", "purple(品红色)", + "red(红色)", "silver(淡灰色)", "teal(深青色)", "white(白色)", "yellow(黄色)"]; + namesObj.allFonts = [main.styles.font].concat(main.fonts); + namesObj.allDoors = ["this"].concat(Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e) + .map(function (key) { return maps_90f36752_8815_4be8_b32b_d7fad1d0542e[key]; }) + .filter(function (one) { return one.doorInfo != null; }) + .map(function (one) { return one.id; })); + namesObj.allEvents = Object.keys(core.events.commonEvent); + var filter = function (list, content) { + return list.filter(function (one) { + return one != content && one.startsWith(content); + }).sort(); + } + + // 对任意图块提供补全 + // 对怪物ID提供补全 + // 对道具ID进行补全 + // 对图片名进行补全 + // 对动画进行补全 + // 对音乐进行补全 + // 对音效进行补全 + // 对全局商店进行补全 + // 对楼层名进行补全 + for (var ii = 0, names; names = ['allIds', 'allEnemys', 'allItems', 'allEquips', 'allImages', 'allAnimates', 'allBgms', 'allSounds', 'allShops', 'allFloorIds', 'allDoors', 'allEvents'][ii]; ii++) { + if (MotaActionBlocks[type][names] && eval(MotaActionBlocks[type][names]).indexOf(name) !== -1) { + return filter(namesObj[names], content); + } + } + + // 对\f进行自动补全 + index = Math.max(content.lastIndexOf("\f["), content.lastIndexOf("\\f[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf(",") < 0 && after.indexOf("]") < 0) { + return filter(namesObj.allImages, after); + } + } + + // 对\\i进行补全 + index = content.lastIndexOf("\\i["); + if (index >= 0) { + var after = content.substring(index + 3); + if (after.indexOf("]") < 0) { + return filter(namesObj.allIconIds, after); + } + } + + // 对\r进行补全 + index = Math.max(content.lastIndexOf("\r["), content.lastIndexOf("\\r[")); + if (index >= 0) { + if (content.charAt(index) == '\\') index++; + var after = content.substring(index + 2); + if (after.indexOf("]") < 0) { + return filter(namesObj.allColors, after); + } + } + + // 对\g进行补全 + index = content.lastIndexOf("\\g["); + if (index >= 0) { + var after = content.substring(index + 3); + if (after.indexOf("]") < 0) { + return filter(namesObj.allFonts, after); + } + } + + // 对\进行补全! + if (content.charAt(content.length - 1) == '\\') { + return ["n(换行)", "f(立绘)", "r(变色)", "i(图标)", "z(暂停打字)", "t(标题图标)", "b(对话框)", "c(字体大小)", "d(粗体)", "e(斜体)", "g(字体)"]; + } + + return []; + } + + editor_blockly.completeItems = []; + + editor_blockly.onTextFieldCreate = function (self, htmlInput) { + var pb = self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf = args[args.indexOf(self.name) + 1] + + // ------ colour + + if (targetf && targetf.slice(0, 7) === 'Colour_') { + var inputDom = htmlInput; + // var getValue=function(){ // 获得自己的字符串 + // return pb.getFieldValue(self.name); + // } + var setValue = function (newValue) { // 设置右边颜色块的css颜色 + pb.setFieldValue(newValue, targetf) + } + // 给inputDom绑事件 + inputDom.oninput = function () { + var value = inputDom.value + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(value)) { + setValue('rgba(' + value + ')') + } + } + } + else { + + htmlInput.onkeydown = function (e) { + if (e.keyCode == 13 && awesomplete.opened && awesomplete.selected) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + awesomplete.select(); + return false; + } + } + + // --- awesomplete + var awesomplete = new Awesomplete(htmlInput, { + minChars: 1, + maxItems: 12, + autoFirst: true, + replace: function (text) { + text = text.toString(); + var index = text.indexOf("("); + if (index >= 0) text = text.substring(0, index); + var value = this.input.value, index = this.input.selectionEnd; + if (index == null) index = value.length; + if (index < awesomplete.prefix.length) index = awesomplete.prefix.length; + var str = value.substring(0, index - awesomplete.prefix.length) + text + value.substring(index); + this.input.value = str; + pb.setFieldValue(str, self.name); + self.forceRerender(); + self.resizeEditor_(); + index += text.length - awesomplete.prefix.length; + this.input.setSelectionRange(index, index); + + editor_blockly.completeItems = editor_blockly.completeItems.filter(function (x) { + return x != text; + }); + editor_blockly.completeItems.unshift(text); + }, + filter: function () { return true; }, + item: function (text, input) { + var id = text.label, info = core.getBlockInfo(id); + var li = document.createElement("li"); + li.setAttribute("role", "option"); + li.setAttribute("aria-selected", "false"); + input = awesomplete.prefix.trim(); + if (input != "") text = text.replace(new RegExp("^" + input, "i"), "$&"); + li.innerHTML = text; + if (info) { + var height = (info.height || 32), width = 32; + var scale = 75; + height *= scale / 100; + width *= scale / 100; + var ctx = core.createCanvas('list_' + id, 0, 0, width, height), + canvas = ctx.canvas; + canvas.style.display = 'inline'; + canvas.style.marginRight = '8px'; + core.drawIcon(ctx, id, 0, 0, width, height); + canvas.style.position = ''; + li.insertBefore(canvas, li.children[0]); + } + return li; + }, + sort: function (a, b) { + a = a.toString(); b = b.toString(); + var ia = editor_blockly.completeItems.indexOf(a), ib = editor_blockly.completeItems.indexOf(b); + if (ia < 0) ia = editor_blockly.completeItems.length; + if (ib < 0) ib = editor_blockly.completeItems.length; + if (ia != ib) return ia - ib; + if (a.length != b.length) return a.length - b.length; + return a < b ? -1 : 1; + } + }); + + htmlInput.oninput = function () { + var value = htmlInput.value, index = htmlInput.selectionEnd; + if (index == null) index = value.length; + value = value.substring(0, index); + // cal prefix + awesomplete.prefix = value; + for (var i = index - 1; i >= 0; i--) { + var c = value.charAt(i); + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]$/.test(c)) { + awesomplete.prefix = value.substring(i + 1); + break; + } + } + + var list = editor_blockly.getAutoCompletions(value, pb.type, self.name, pb); + + awesomplete.list = list; + var caretPosition = getCaretCoordinates(htmlInput, htmlInput.selectionStart); + awesomplete.ul.style.marginLeft = caretPosition.left - htmlInput.scrollLeft - 20 + "px"; + var totalHeight = parseFloat(Blockly.WidgetDiv.DIV.style.height.replace('px', '')); + awesomplete.ul.style.marginTop = caretPosition.top + caretPosition.height - totalHeight + 10 + 'px'; + awesomplete.evaluate(); + } + + awesomplete.container.style.width = "100%"; + + window.awesomplete = awesomplete; + } + } + + editor_blockly.isBlockCollapsedSupported = function (block) { + var supportedDisabledBlocks = [ + 'text_0_s', 'text_1_s', 'text_2_s', 'if_s', 'if_1_s', 'confirm_s', 'switch_s', 'choices_s', + 'for_s', 'forEach_s', 'while_s', 'dowhile_s', 'wait_s', 'previewUI_s', + 'waitContext_1', 'waitContext_2', 'waitContext_3', 'switchCase', 'choicesContext' + ]; + return supportedDisabledBlocks.indexOf(block.type || "") >= 0; + } + + return editor_blockly; +} + +// --- modify Blockly + +Blockly.FieldColour.prototype.showEditor_ = function () { + Blockly.WidgetDiv.hide(); + + // console.log('here') + var self = this; + var pb = self.sourceBlock_ + var args = MotaActionBlocks[pb.type].args + var targetf = args[args.indexOf(self.name) - 1] + + var getValue = function () { + // return self.getValue() // css颜色 + var f = pb.getFieldValue(targetf); + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(f)) { + return f; + } + return ""; + // 也可以用 pb.getFieldValue(targetf) 获得颜色块左边的域的内容 + } + + var setValue = function (newValue) { // css颜色 + self.setValue(newValue) + pb.setFieldValue(newValue.replace("rgba(", "").replace(")", ""), targetf) // 放在颜色块左边的域中 + } + + setTimeout(function () { + document.getElementById("colorPicker").value = getValue(); + // 设置位置 + var scaledBBox = self.getScaledBBox(); + openColorPicker(scaledBBox.left, scaledBBox.bottom, setValue); + }); + + return document.createElement('table'); +}; + +Blockly.FieldColour.prototype.setValue = function (colour) { + this.doValueUpdate_(colour); +} + +Blockly.FieldColour.prototype.initView = function () { + this.size_ = new Blockly.utils.Size( + this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH, + this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT); + if (!this.getConstants().FIELD_COLOUR_FULL_BLOCK) { + this.createBorderRect_(); + this.borderRect_.style['fillOpacity'] = '1'; + this.borderRect_.classList.add('blocklyColourFieldRect'); + } else { + this.clickTarget_ = this.sourceBlock_.getSvgRoot(); + } +}; + +Blockly.FieldTextInput.prototype.showInlineEditor_ = function (quietInput) { + Blockly.WidgetDiv.show( + this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this)); + this.htmlInput_ = this.widgetCreate_(); + this.isBeingEdited_ = true; + + editor_blockly.onTextFieldCreate(this, this.htmlInput_); + + if (!quietInput) { + this.htmlInput_.focus({ preventScroll: true }); + this.htmlInput_.select(); + } +}; + +Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function (e) { + if (e.keyCode == Blockly.utils.KeyCodes.ENTER && !(window.awesomplete && window.awesomplete.opened)) { + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == Blockly.utils.KeyCodes.ESC) { + this.htmlInput_.value = this.htmlInput_.defaultValue; + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + } else if (e.keyCode == Blockly.utils.KeyCodes.TAB) { + Blockly.WidgetDiv.hide(); + Blockly.DropDownDiv.hideWithoutAnimation(); + this.sourceBlock_.tab(this, !e.shiftKey); + e.preventDefault(); + } +}; + +Blockly.FieldMultilineInput.prototype.showInlineEditor_ = function (quietInput) { + Blockly.FieldMultilineInput.superClass_.showInlineEditor_.call(this, quietInput); + // force to resize the input + this.htmlInput_.style.height = Blockly.WidgetDiv.DIV.style.height; +}; + +Blockly.FieldMultilineInput.prototype.onHtmlInputChange_ = function (e) { + Blockly.FieldMultilineInput.superClass_.onHtmlInputChange_.call(this, e); + // force to resize the input + this.htmlInput_.style.height = Blockly.WidgetDiv.DIV.style.height; +}; + +Blockly.copy_ = function (toCopy) { + if (toCopy.isComment) { + var xml = toCopy.toXmlWithXY(); + } else { + var xml = Blockly.Xml.blockToDom(toCopy, true); + // Copy only the selected block and internal blocks. + Blockly.Xml.deleteNext(xml); + // Encode start position in XML. + var xy = toCopy.getRelativeToSurfaceXY(); + xml.setAttribute('x', toCopy.RTL ? -xy.x : xy.x); + xml.setAttribute('oy', xy.y); + xml.setAttribute('sy', toCopy.workspace.scrollY); + } + Blockly.clipboardXml_ = xml; + Blockly.clipboardSource_ = toCopy.workspace; + Blockly.clipboardTypeCounts_ = toCopy.isComment ? null : + Blockly.utils.getBlockTypeCounts(toCopy, true); +}; + +/** + * Paste the provided block onto the workspace. + * @param {!Element} xmlBlock XML block element. + */ +Blockly.WorkspaceSvg.prototype.paste = function (xmlBlock) { + if (!this.rendered || xmlBlock.getElementsByTagName('block').length >= + this.remainingCapacity()) { + return; + } + if (this.currentGesture_) { + this.currentGesture_.cancel(); // Dragging while pasting? No. + } + if (xmlBlock.tagName.toLowerCase() == 'comment') { + this.pasteWorkspaceComment_(xmlBlock); + } else { + if (xmlBlock.hasAttribute('oy') && xmlBlock.hasAttribute('sy')) { + xmlBlock.setAttribute('y', parseFloat(xmlBlock.getAttribute('oy')) + parseFloat(xmlBlock.getAttribute('sy')) - this.scrollY); + } + this.pasteBlock_(xmlBlock); + } +}; + +// -- Support showing disabled blocks + +Blockly.Generator.prototype.blockToCode = function (block, opt_thisOnly) { + if (this.isInitialized === false) { + console.warn( + 'Generator init was not called before blockToCode was called.'); + } + if (!block) { + return ''; + } + if (!block.isEnabled() && !editor_blockly.isBlockCollapsedSupported(block)) { + // Skip past this block if it is disabled. + return opt_thisOnly ? '' : this.blockToCode(block.getNextBlock()); + } + if (block.isInsertionMarker()) { + // Skip past insertion markers. + return opt_thisOnly ? '' : this.blockToCode(block.getChildren(false)[0]); + } + + var func = this[block.type]; + if (typeof func != 'function') { + throw Error('Language "' + this.name_ + '" does not know how to generate ' + + 'code for block type "' + block.type + '".'); + } + // First argument to func.call is the value of 'this' in the generator. + // Prior to 24 September 2013 'this' was the only way to access the block. + // The current preferred method of accessing the block is through the second + // argument to func.call, which becomes the first parameter to the generator. + var code = func.call(block, block); + if (Array.isArray(code)) { + // Value blocks return tuples of code and operator order. + if (!block.outputConnection) { + throw TypeError('Expecting string from statement block: ' + block.type); + } + return [this.scrub_(block, code[0], opt_thisOnly), code[1]]; + } else if (typeof code == 'string') { + if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) { + code = this.injectId(this.STATEMENT_PREFIX, block) + code; + } + if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) { + code = code + this.injectId(this.STATEMENT_SUFFIX, block); + } + return this.scrub_(block, code, opt_thisOnly); + } else if (code === null) { + // Block has handled code generation itself. + return ''; + } + throw SyntaxError('Invalid code generated: ' + code); +}; + +Blockly.BlockSvg.prototype.generateContextMenu = function () { + if (this.workspace.options.readOnly || !this.contextMenu) { + return null; + } + // Save the current block in a variable for use in closures. + var block = this; + var menuOptions = []; + + if (!this.isInFlyout) { + // 删除 + if (this.isDeletable() && this.isMovable()) { + menuOptions.push(Blockly.ContextMenu.blockDuplicateOption(block)); + } + + if (editor_blockly.isBlockCollapsedSupported(this)) { + menuOptions.push({ + text: this.isCollapsed() ? Blockly.Msg['EXPAND_BLOCK'] : Blockly.Msg['COLLAPSE_BLOCK'], + enabled: true, + callback: function () { block.setCollapsed(!block.collapsed_); } + }); + + menuOptions.push({ + text: this.isEnabled() ? Blockly.Msg['DISABLE_BLOCK'] : Blockly.Msg['ENABLE_BLOCK'], + enabled: !this.getInheritedDisabled(), + callback: function () { + var group = Blockly.Events.getGroup(); + if (!group) { + Blockly.Events.setGroup(true); + } + block.setEnabled(!block.isEnabled()); + if (!group) { + Blockly.Events.setGroup(false); + } + } + }); + } + if (this.isDeletable()) { + menuOptions.push(Blockly.ContextMenu.blockDeleteOption(block)); + } + } + + menuOptions.push(Blockly.ContextMenu.blockHelpOption(block)); + if (this.customContextMenu) this.customContextMenu(menuOptions); + return menuOptions; +}; + +Blockly.FieldDropdown.prototype.doClassValidation_ = function (opt_newValue) { + return opt_newValue; +} + +Blockly.FieldDropdown.prototype.doValueUpdate_ = function (newValue) { + Blockly.FieldDropdown.superClass_.doValueUpdate_.call(this, newValue); + var options = this.getOptions(true); + for (var i = 0, option; (option = options[i]); i++) { + if (option[1] == this.value_) { + this.selectedOption_ = option; + } + } + if (this.selectedOption_[1] != this.value_) { + options.push([this.value_, this.value_]); + this.selectedOption_ = options[options.length - 1]; + } +}; + +Blockly.FieldMultilineInput.prototype.getDisplayText_ = function () { + var value = this.value_; + if (!value) return Blockly.Field.NBSP; + var curr = '', text = ''; + for (var i = 0; i < value.length; ++i) { + if (value[i] == '\n' || curr.length == this.maxDisplayLength) { + text += curr.replace(/\s/g, Blockly.Field.NBSP) + '\n'; + curr = value[i] == '\n' ? '' : value[i]; + } else curr += value[i]; + } + return text + curr; +}; diff --git a/_server/editor_blocklyconfig.js b/_server/editor_blocklyconfig.js new file mode 100644 index 0000000..be72015 --- /dev/null +++ b/_server/editor_blocklyconfig.js @@ -0,0 +1,705 @@ +editor_blocklyconfig=(function(){ +// start mark sfergsvae + + + +(function(){ + var getCategory = function(name,custom){ + for(var node of document.getElementById('toolbox').children) { + if(node.getAttribute('name')==name) return node; + } + var node = document.createElement('category'); + node.setAttribute('name',name); + if(custom)node.setAttribute('custom',custom); + document.getElementById('toolbox').appendChild(node); + return node; + } + + var toolboxObj = { + '入口方块':[ + '', + MotaActionFunctions.actionParser.parse([ + "欢迎使用事件编辑器", + "本事件触发一次后会消失", + {"type": "hide", "time": 500}, + ],'event'), + MotaActionFunctions.actionParser.parse({ + "condition": "flag:__door__===2", + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + {"type": "openDoor", "loc": [10,5]} + ], + },'autoEvent'), + MotaActionBlocks['changeFloor_m'].xmlText(), + MotaActionFunctions.actionParser.parse([{ + "id": "shop1", + "text": "\t[贪婪之神,moneyShop]勇敢的武士啊, 给我${20+2*flag:shop1}金币就可以:", + "textInList": "1F金币商店", + "choices": [ + {"text": "生命+800", "need": "status:money>=20+2*flag:shop1", "action": [ + {"type": "comment", "text": "新版商店中需要手动扣减金币和增加访问次数"}, + {"type": "setValue", "name": "status:money", "operator": "-=", "value": "20+2*flag:shop1"}, + {"type": "setValue", "name": "flag:shop1", "operator": "+=", "value": "1"}, + {"type": "setValue", "name": "status:hp", "operator": "+=", "value": "800"} + ]} + ] + },{ + "id": "itemShop", + "item": true, + "textInList": "道具商店", + "choices": [ + {"id": "yellowKey", "number": 10, "money": 10} + ] + },{ + "id": "keyShop1", + "textInList": "回收钥匙商店", + "commonEvent": "回收钥匙商店", + "args": "" + }],'shop'), + MotaActionBlocks['common_m'].xmlText(), + MotaActionBlocks['beforeBattle_m'].xmlText(), + MotaActionBlocks['afterBattle_m'].xmlText(), + MotaActionBlocks['afterGetItem_m'].xmlText(), + MotaActionBlocks['afterOpenDoor_m'].xmlText(), + MotaActionBlocks['firstArrive_m'].xmlText(), + MotaActionBlocks['eachArrive_m'].xmlText(), + MotaActionBlocks['level_m'].xmlText(), + MotaActionFunctions.actionParser.parse([ + ['MTx', ''] + ], 'floorPartition'), + MotaActionBlocks['commonEvent_m'].xmlText(), + MotaActionBlocks['item_m'].xmlText(), + MotaActionFunctions.actionParser.parse([ + {"title":"简单", "name": "Easy", "hard": 1, "action": [ + {"type": "comment", "text": "在这里写该难度需执行的事件"} + ]} + ], 'levelChoose'), + MotaActionFunctions.actionParser.parse({ + "type": 0, "value": {"atk": 10}, "percentage": {"speed": 10}, + }, 'equip'), + MotaActionFunctions.actionParser.parse([{ + "name": "bg.jpg", "x": 0, "y": 0, "canvas": "bg" + }], 'floorImage'), + MotaActionFunctions.actionParser.parse({ + "time": 160, "openSound": "door.mp3", "closeSound": "door.mp3", "keys": {"yellowKey": 1, "orangeKey": 1} + }, 'doorInfo'), + MotaActionBlocks['faceIds_m'].xmlText(), + MotaActionBlocks['mainStyle_m'].xmlText(), + MotaActionFunctions.actionParser.parse({ + "背景音乐": "bgm.mp3", "确定": "confirm.mp3", "攻击": "attack.mp3", "背景图": "bg.jpg", "领域": "zone", "文件名": "file.jpg" + }, 'nameMap'), + MotaActionFunctions.actionParser.parse([ + {"name": "hero.png", "width": 32, "height": 32, "prefix": "hero_"}, + ], 'splitImages'), + ], + '显示文字':[ + MotaActionBlocks['text_0_s'].xmlText(), + MotaActionBlocks['text_1_s'].xmlText(), + MotaActionFunctions.actionParser.parseList("\t[小妖精,fairy]\f[fairy.png,0,0]欢迎使用事件编辑器(双击方块可直接预览)"), + MotaActionBlocks['moveTextBox_s'].xmlText(), + MotaActionBlocks['clearTextBox_s'].xmlText(), + MotaActionBlocks['comment_s'].xmlText(), + MotaActionBlocks['autoText_s'].xmlText(), + MotaActionBlocks['scrollText_s'].xmlText(), + MotaActionBlocks['setText_s'].xmlText(), + MotaActionBlocks['tip_s'].xmlText(), + MotaActionBlocks['confirm_s'].xmlText(), + MotaActionBlocks['choices_s'].xmlText([ + '选择剑或者盾','流浪者','man',0,'',MotaActionBlocks['choicesContext'].xmlText([ + '剑','','',null,'','',MotaActionFunctions.actionParser.parseList([{"type": "openDoor", "loc": [3,3]}]), + ]) + ]), + MotaActionBlocks['win_s'].xmlText(), + MotaActionBlocks['lose_s'].xmlText(), + MotaActionBlocks['restart_s'].xmlText(), + ], + '数据相关':[ + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false + ]), + MotaActionBlocks['setEnemy_s'].xmlText(), + MotaActionBlocks['setEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['resetEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['moveEnemyOnPoint_s'].xmlText(), + MotaActionBlocks['moveEnemyOnPoint_1_s'].xmlText(), + MotaActionBlocks['setEquip_s'].xmlText(), + MotaActionBlocks['setFloor_s'].xmlText(), + MotaActionBlocks['setGlobalAttribute_s'].xmlText(), + MotaActionBlocks['setGlobalValue_s'].xmlText(), + MotaActionBlocks['setGlobalFlag_s'].xmlText(), + MotaActionBlocks['setNameMap_s'].xmlText(), + MotaActionBlocks['input_s'].xmlText(), + MotaActionBlocks['input2_s'].xmlText(), + MotaActionBlocks['update_s'].xmlText(), + MotaActionBlocks['moveAction_s'].xmlText(), + MotaActionBlocks['changeFloor_s'].xmlText(), + MotaActionBlocks['changePos_s'].xmlText(), + MotaActionBlocks['battle_s'].xmlText(), + MotaActionBlocks['useItem_s'].xmlText(), + MotaActionBlocks['loadEquip_s'].xmlText(), + MotaActionBlocks['unloadEquip_s'].xmlText(), + MotaActionBlocks['openShop_s'].xmlText(), + MotaActionBlocks['disableShop_s'].xmlText(), + MotaActionBlocks['setHeroIcon_s'].xmlText(), + MotaActionBlocks['follow_s'].xmlText(), + MotaActionBlocks['unfollow_s'].xmlText(), + ], + '地图处理':[ + MotaActionBlocks['battle_1_s'].xmlText(), + MotaActionBlocks['openDoor_s'].xmlText(), + MotaActionBlocks['closeDoor_s'].xmlText(), + MotaActionBlocks['show_s'].xmlText(), + MotaActionBlocks['hide_s'].xmlText(), + MotaActionBlocks['setBlock_s'].xmlText(), + MotaActionBlocks['setBlockOpacity_s'].xmlText(), + MotaActionBlocks['setBlockFilter_s'].xmlText(), + MotaActionBlocks['turnBlock_s'].xmlText(), + MotaActionBlocks['moveHero_s'].xmlText(), + MotaActionBlocks['move_s'].xmlText(), + MotaActionBlocks['jumpHero_s'].xmlText(), + MotaActionBlocks['jumpHero_1_s'].xmlText(), + MotaActionBlocks['jump_s'].xmlText(), + MotaActionBlocks['jump_1_s'].xmlText(), + MotaActionBlocks['showBgFgMap_s'].xmlText(), + MotaActionBlocks['hideBgFgMap_s'].xmlText(), + MotaActionBlocks['setBgFgBlock_s'].xmlText(), + MotaActionBlocks['showFloorImg_s'].xmlText(), + MotaActionBlocks['hideFloorImg_s'].xmlText(), + ], + '事件控制':[ + MotaActionBlocks['if_1_s'].xmlText(), + MotaActionBlocks['if_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "switch", "condition": "判别值", "caseList": [ + {"action": [{"type": "comment", "text": "当判别值是值的场合执行此事件"}]}, + {"case": "default", "action": [{"type": "comment", "text": "当没有符合的值的场合执行default事件"}]}, + ]}), + MotaActionFunctions.actionParser.parseList({"type": "for", "name": "temp:A", "from": "0", "to": "12", "step": "1", "data": []}), + MotaActionFunctions.actionParser.parseList({"type": "forEach", "name": "temp:A", "list": ["status:atk","status:def"], "data": []}), + MotaActionBlocks['while_s'].xmlText(), + MotaActionBlocks['dowhile_s'].xmlText(), + MotaActionBlocks['break_s'].xmlText(), + MotaActionBlocks['continue_s'].xmlText(), + MotaActionBlocks['exit_s'].xmlText(), + MotaActionBlocks['trigger_s'].xmlText(), + MotaActionBlocks['insert_1_s'].xmlText(), + MotaActionBlocks['insert_2_s'].xmlText(), + ], + '特效表现':[ + MotaActionBlocks['sleep_s'].xmlText(), + MotaActionFunctions.actionParser.parseList({"type": "wait", "timeout": 0, "data": [ + {"case": "keyboard", "keycode": "13,32", "action": [{"type": "comment", "text": "当按下回车(keycode=13)或空格(keycode=32)时执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "mouse", "px": [0,32], "py": [0,32], "action": [{"type": "comment", "text": "当点击地图左上角时执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "condition", "condition": "flag:type==0\n&&flag:keycode==13", "action": [{"type": "comment", "text": "当满足自定义条件时会执行此事件\n超时剩余时间会写入flag:timeout"}]}, + {"case": "timeout", "action": [{"type": "comment", "text": "当超时未操作时执行此事件"}]}, + ]}), + MotaActionBlocks['waitAsync_s'].xmlText(), + MotaActionBlocks['stopAsync_s'].xmlText(), + MotaActionBlocks['vibrate_s'].xmlText(), + MotaActionBlocks['animate_s'].xmlText(), + MotaActionBlocks['animate_1_s'].xmlText(), + MotaActionBlocks['stopAnimate_s'].xmlText(), + MotaActionBlocks['setViewport_s'].xmlText(), + MotaActionBlocks['setViewport_1_s'].xmlText(), + MotaActionBlocks['lockViewport_s'].xmlText(), + MotaActionBlocks['showStatusBar_s'].xmlText(), + MotaActionBlocks['hideStatusBar_s'].xmlText(), + MotaActionBlocks['setHeroOpacity_s'].xmlText(), + MotaActionBlocks['setCurtain_0_s'].xmlText(), + MotaActionBlocks['setCurtain_1_s'].xmlText(), + MotaActionBlocks['screenFlash_s'].xmlText(), + MotaActionBlocks['setWeather_s'].xmlText(), + MotaActionBlocks['callBook_s'].xmlText(), + MotaActionBlocks['callSave_s'].xmlText(), + MotaActionBlocks['autoSave_s'].xmlText(), + MotaActionBlocks['forbidSave_s'].xmlText(), + MotaActionBlocks['callLoad_s'].xmlText(), + ], + '音像处理':[ + MotaActionBlocks['showImage_s'].xmlText(), + MotaActionBlocks['showImage_1_s'].xmlText(), + MotaActionBlocks['hideImage_s'].xmlText(), + MotaActionBlocks['showTextImage_s'].xmlText(), + MotaActionBlocks['moveImage_s'].xmlText(), + MotaActionBlocks['rotateImage_s'].xmlText(), + MotaActionBlocks['scaleImage_s'].xmlText(), + MotaActionBlocks['showGif_s'].xmlText(), + MotaActionBlocks['playBgm_s'].xmlText(), + MotaActionBlocks['pauseBgm_s'].xmlText(), + MotaActionBlocks['resumeBgm_s'].xmlText(), + MotaActionBlocks['loadBgm_s'].xmlText(), + MotaActionBlocks['freeBgm_s'].xmlText(), + MotaActionBlocks['playSound_s'].xmlText(), + MotaActionBlocks['playSound_1_s'].xmlText(), + MotaActionBlocks['stopSound_s'].xmlText(), + MotaActionBlocks['setVolume_s'].xmlText(), + MotaActionBlocks['setBgmSpeed_s'].xmlText(), + ], + 'UI绘制':[ + MotaActionBlocks['previewUI_s'].xmlText(), + MotaActionBlocks['clearMap_s'].xmlText(), + MotaActionBlocks['setAttribute_s'].xmlText(), + MotaActionBlocks['setFilter_s'].xmlText(), + MotaActionBlocks['fillText_s'].xmlText(), + MotaActionBlocks['fillBoldText_s'].xmlText(), + MotaActionBlocks['drawTextContent_s'].xmlText(), + MotaActionBlocks['fillRect_s'].xmlText(), + MotaActionBlocks['strokeRect_s'].xmlText(), + MotaActionBlocks['drawLine_s'].xmlText(), + MotaActionBlocks['drawArrow_s'].xmlText(), + MotaActionBlocks['fillPolygon_s'].xmlText(), + MotaActionBlocks['strokePolygon_s'].xmlText(), + MotaActionBlocks['fillEllipse_s'].xmlText(), + MotaActionBlocks['strokeEllipse_s'].xmlText(), + MotaActionBlocks['fillArc_s'].xmlText(), + MotaActionBlocks['strokeArc_s'].xmlText(), + MotaActionBlocks['drawImage_s'].xmlText(), + MotaActionBlocks['drawImage_1_s'].xmlText(), + MotaActionBlocks['drawIcon_s'].xmlText(), + MotaActionBlocks['drawBackground_s'].xmlText(), + MotaActionBlocks['drawSelector_s'].xmlText(), + MotaActionBlocks['drawSelector_1_s'].xmlText(), + ], + '原生脚本':[ + MotaActionBlocks['function_s'].xmlText(), + MotaActionBlocks['unknown_s'].xmlText(), + ], + '值块':[ + MotaActionBlocks['setValue_s'].xmlText([ + MotaActionBlocks['idIdList_e'].xmlText(['status','生命']), '=', '', false + ]), + MotaActionBlocks['expression_arithmetic_0'].xmlText(), + MotaActionBlocks['idFlag_e'].xmlText(), + MotaActionBlocks['idTemp_e'].xmlText(), + MotaActionBlocks['negate_e'].xmlText(), + MotaActionBlocks['unaryOperation_e'].xmlText(), + MotaActionBlocks['bool_e'].xmlText(), + MotaActionBlocks['idString_e'].xmlText(), + MotaActionBlocks['idIdList_e'].xmlText(), + MotaActionBlocks['idFixedList_e'].xmlText(), + MotaActionBlocks['enemyattr_e'].xmlText(), + MotaActionBlocks['blockId_e'].xmlText(), + MotaActionBlocks['blockNumber_e'].xmlText(), + MotaActionBlocks['blockCls_e'].xmlText(), + MotaActionBlocks['hasEquip_e'].xmlText(), + MotaActionBlocks['equip_e'].xmlText(), + MotaActionBlocks['nextXY_e'].xmlText(), + MotaActionBlocks['isReplaying_e'].xmlText(), + MotaActionBlocks['hasVisitedFloor_e'].xmlText(), + MotaActionBlocks['isShopVisited_e'].xmlText(), + MotaActionBlocks['canBattle_e'].xmlText(), + MotaActionBlocks['damage_e'].xmlText(), + MotaActionBlocks['damage_1_e'].xmlText(), + MotaActionBlocks['rand_e'].xmlText(), + MotaActionBlocks['evalString_e'].xmlText(), + ], + '常见事件模板':[ + '', + MotaActionFunctions.actionParser.parseList({"type": "if", "condition": "!core.musicStatus.bgmStatus", + "true": [ + "\t[系统提示]你当前音乐处于关闭状态,本塔开音乐游戏效果更佳" + ], + "false": [] + }), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "if", + "condition": "switch:A", + "true": [ + "\t[行商,trader]\b[this]这是购买我的道具后我给玩家的提示。", + { + "type": "comment", + "text": "下一条指令可视情况使用或不使用" + }, + { + "type": "hide", + "remove": true, + "time": 250 + } + ], + "false": [ + { + "type": "confirm", + "text": "我有3把黄钥匙,\n你出50金币就卖给你。", + "yes": [ + { + "type": "if", + "condition": "status:money>=50", + "true": [ + { + "type": "setValue", + "name": "status:money", + "operator": "-=", + "value": "50" + }, + { + "type": "setValue", + "name": "item:yellowKey", + "operator": "+=", + "value": "3" + }, + { + "type": "playSound", + "name": "确定", + "stop": true + }, + { + "type": "setValue", + "name": "switch:A", + "value": "true" + } + ], + "false": [ + { + "type": "playSound", + "name": "操作失败" + }, + "\t[行商,trader]\b[this]你的金币不足!" + ] + } + ], + "no": [] + } + ] + } + ], 'event'), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "comment", + "text": "全地图选中一个点,需要用鼠标或触屏操作" + }, + { + "type": "setValue", + "name": "temp:X", + "value": "status:x" + }, + { + "type": "setValue", + "name": "temp:Y", + "value": "status:y" + }, + { + "type": "tip", + "text": "再次点击闪烁位置确认" + }, + { + "type": "while", + "condition": "true", + "data": [ + { + "type": "drawSelector", + "image": "winskin.png", + "code": 1, + "x": "32*temp:X", + "y": "32*temp:Y", + "width": 32, + "height": 32 + }, + { + "type": "wait" + }, + { + "type": "if", + "condition": "(flag:type === 1)", + "true": [ + { + "type": "if", + "condition": "((temp:X===flag:x)&&(temp:Y===flag:y))", + "true": [ + { + "type": "break", + "n": 1 + } + ] + }, + { + "type": "setValue", + "name": "temp:X", + "value": "flag:x" + }, + { + "type": "setValue", + "name": "temp:Y", + "value": "flag:y" + } + ] + } + ] + }, + { + "type": "drawSelector", + "code": 1 + }, + { + "type": "comment", + "text": "流程进行到这里可以对[X,Y]点进行处理,比如" + }, + { + "type": "closeDoor", + "id": "yellowDoor", + "loc": [ + "temp:X", + "temp:Y" + ] + } + ],'event'), + '', + MotaActionFunctions.actionParser.parse([ + { + "type": "comment", + "text": "多阶段boss,请直接作为战后事件使用" + }, + { + "type": "setValue", + "name": "switch:A", + "operator": "+=", + "value": "1" + }, + { + "type": "switch", + "condition": "switch:A", + "caseList": [ + { + "case": "1", + "action": [ + { + "type": "setBlock", + "number": "redSlime" + }, + "\t[2阶段boss,redSlime]\b[this]你以为你已经打败我了吗?没听说过史莱姆有九条命吗?" + ] + }, + { + "case": "2", + "action": [ + { + "type": "setBlock", + "number": "blackSlime" + }, + "\t[3阶段boss,blackSlime]\b[this]不能消灭我的,只会让我更强大!" + ] + }, + { + "case": "3", + "action": [ + { + "type": "setBlock", + "number": "slimelord" + }, + "\t[4阶段boss,slimelord]\b[this]我还能打!" + ] + }, + { + "case": "4", + "action": [ + "\t[4阶段boss,slimelord]我一定会回来的!" + ] + } + ] + } + ],'afterBattle'), + ], + '最近使用事件':[ + '', + ] + } + var toolboxgap = '' + //xml_text = MotaActionFunctions.actionParser.parse(obj,type||'event') + //MotaActionBlocks['idString_e'].xmlText() + + for (var name in toolboxObj){ + var custom = null; + if(name=='最近使用事件')custom='searchBlockCategory'; + if(name=='入口方块')custom='entranceCategory'; + getCategory(name,custom).innerHTML = toolboxObj[name].join(toolboxgap); + } + +var blocklyArea = document.getElementById('blocklyArea'); +var blocklyDiv = document.getElementById('blocklyDiv'); +var workspace = Blockly.inject(blocklyDiv,{ + media: '_server/blockly/media/', + toolbox: document.getElementById('toolbox'), + zoom:{ + controls: true, + wheel: false,//滚轮改为上下(shift:左右)翻滚 + startScale: 1.0, + maxScale: 3, + minScale: 0.3, + scaleSpeed: 1.08 + }, + trashcan: false, +}); + +editor_blockly.isCommonEntry = function () { + var commonEntries = ['beforeBattle', 'afterBattle', 'afterOpenDoor', 'firstArrive', 'eachArrive', 'commonEvent', 'item']; + return commonEntries.indexOf(editor_blockly.entryType) >= 0; +} + +editor_blockly.entranceCategoryCallback = function(workspace) { + var list=toolboxObj['入口方块'] + var xmlList = []; + var eventType = (editor_blockly.isCommonEntry() ? 'common' : editor_blockly.entryType)+'_m'; + for(var ii=0,blockText;blockText=list[ii];ii++){ + if(new RegExp('').exec(blockText)){ + var block = Blockly.Xml.textToDom(''+blockText+'').firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); + } + } + return xmlList; +} + +workspace.registerToolboxCategoryCallback( + 'entranceCategory', editor_blockly.entranceCategoryCallback); + +editor_blockly.searchBlockCategoryCallback = function(workspace) { + var xmlList = []; + var labels = editor_blockly.searchBlock(); + for (var i = 0; i < labels.length; i++) { + var blockText = '' + + MotaActionBlocks[labels[i]].xmlText() + + ''; + var block = Blockly.Xml.textToDom(blockText).firstChild; + block.setAttribute("gap", 5); + xmlList.push(block); + } + return xmlList; +}; + +workspace.registerToolboxCategoryCallback( + 'searchBlockCategory', editor_blockly.searchBlockCategoryCallback); + +var onresize = function(e) { + blocklyDiv.style.width = blocklyArea.offsetWidth + 'px'; + blocklyDiv.style.height = blocklyArea.offsetHeight + 'px'; + Blockly.svgResize(workspace); +}; +if(typeof editor !== "undefined" && !editor.isMobile)window.addEventListener('resize', onresize, false); +onresize(); +//Blockly.svgResize(workspace); + +//Blockly.bindEventWithChecks_(workspace.svgGroup_,"wheel",workspace,function(e){}); +document.getElementById('blocklyDiv').onmousewheel = function(e){ + //console.log(e); + e.preventDefault(); + var hvScroll = e.shiftKey?'hScroll':'vScroll'; + var mousewheelOffsetValue=20/380*workspace.scrollbar[hvScroll].handleLength_*3; + workspace.scrollbar[hvScroll].handlePosition_+=( ((e.deltaY||0)+(e.detail||0)) >0?mousewheelOffsetValue:-mousewheelOffsetValue); + workspace.scrollbar[hvScroll].onScroll_(); + // workspace.setScale(workspace.scale); +} + +var doubleClickCheck=[[0,'abc']]; +function omitedcheckUpdateFunction(event) { + if(event.type==='create'){ + editor_blockly.addIntoLastUsedType(event.blockId); + } + if(event.type==='ui' && event.element == 'click'){ + var newClick = [new Date().getTime(),event.blockId]; + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if(newClick[0]-lastClick[0]<500){ + if(newClick[1]===lastClick[1]){ + editor_blockly.doubleClickBlock(newClick[1]); + } + } + } + // Only handle these events + if (["create", "move", "change", "delete"].indexOf(event.type) < 0) return; + if(editor_blockly.workspace.topBlocks_.length>=2){ + editor_blockly.setValue('入口方块只能有一个'); + return; + } + var eventType = editor_blockly.entryType; + if(editor_blockly.workspace.topBlocks_.length==1){ + var blockType = editor_blockly.workspace.topBlocks_[0].type; + if(blockType!==eventType+'_m' && !(editor_blockly.isCommonEntry() && blockType == 'common_m')){ + editor_blockly.setValue('入口方块类型错误'); + return; + } + } + try { + var code = Blockly.JavaScript.workspaceToCode(workspace).replace(/\\(i|c|d|e|g|z)/g, '\\\\$1'); + editor_blockly.setValue(code); + } catch (error) { + editor_blockly.setValue(String(error)); + if (error instanceof OmitedError){ + var blockName = error.blockName; + var varName = error.varName; + var block = error.block; + } + // console.log(error); + } + } + + workspace.addChangeListener(omitedcheckUpdateFunction); + + workspace.addChangeListener(Blockly.Events.disableOrphans); + + editor_blockly.workspace = workspace; + + MotaActionFunctions.workspace = function(){ + return editor_blockly.workspace; + } + + // 因为在editor_blockly.parse里已经HTML转义过一次了,所以这里要覆盖掉以避免在注释中出现<等 + MotaActionFunctions.xmlText = function (ruleName,inputs,isShadow,comment,collapsed,disabled) { + var rule = MotaActionBlocks[ruleName]; + var blocktext = isShadow?'shadow':'block'; + var xmlText = []; + xmlText.push('<'+blocktext+' type="'+ruleName+'"'+(collapsed ? ' collapsed="true"' : '')+(disabled ? ' disabled="true"' : '')+'>'); + if(!inputs)inputs=[]; + for (var ii=0,inputType;inputType=rule.argsType[ii];ii++) { + var input = inputs[ii]; + var _input = ''; + var noinput = (input===null || input===undefined); + if(noinput && inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type!=='field_dropdown') continue; + if(noinput && inputType==='field') { + noinput = false; + input = rule.fieldDefault(rule.args[ii]) + } + if(noinput) input = ''; + if(inputType==='field' && MotaActionBlocks[rule.argsGrammarName[ii]].type==='field_checkbox')input=input?'TRUE':'FALSE'; + if(inputType!=='field') { + var subList = false; + var subrulename = rule.argsGrammarName[ii]; + var subrule = MotaActionBlocks[subrulename]; + if (subrule instanceof Array) { + subrulename=subrule[subrule.length-1]; + subrule = MotaActionBlocks[subrulename]; + subList = true; + } + _input = subrule.xmlText([],true); + if(noinput && !subList && !isShadow) { + //无输入的默认行为是: 如果语句块的备选方块只有一个,直接代入方块 + input = subrule.xmlText(); + } + } + xmlText.push('<'+inputType+' name="'+rule.args[ii]+'">'); + xmlText.push(_input+input); + xmlText.push(''); + } + if(comment){ + xmlText.push(''); + xmlText.push(comment); + xmlText.push(''); + } + var next = inputs[rule.args.length]; + if (next) {//next + xmlText.push(''); + xmlText.push(next); + xmlText.push(''); + } + xmlText.push(''); + return xmlText.join(''); + } +})(); + + + +// end mark sfergsvae +}).toString().split('// start mark sfergsvae')[1].split('// end mark sfergsvae')[0] diff --git a/_server/editor_config.js b/_server/editor_config.js new file mode 100644 index 0000000..6a8a9e9 --- /dev/null +++ b/_server/editor_config.js @@ -0,0 +1,52 @@ +function editor_config() { + this.address = "_server/config.json"; + this._isWriting = false; +} + +editor_config.prototype.load = function(callback) { + var _this = this; + fs.readFile(this.address, "utf-8", function(e, d) { + if (e) { + console.error("无法读取配置文件, 已重新生成"); + _this.config = {}; + _this.save(callback); + } else { + try { + _this.config = JSON.parse(d); + if (callback) callback(); + } catch (e) { + console.error(e); + _this.config = {}; + _this.save(callback); + } + } + }); +} + +editor_config.prototype.get = function(key, defaultValue) { + value = this.config[key]; + return value != null ? value : defaultValue; +} + +editor_config.prototype.set = function(key, value, callback) { + this.config[key] = value; + if (callback !== false) this.save(callback); +} + +editor_config.prototype.save = function(callback) { + // 读写锁防止写文件冲突 + if (this._isWriting) return; + try { + this._isWriting = true; + var _this = this; + fs.writeFile(this.address, JSON.stringify(this.config) ,'utf-8', function(e) { + _this._isWriting = false; + if (e) console.error("写入配置文件失败"); + if (callback instanceof Function) callback(); + }) + } catch (e) { + this._isWriting = false; + console.error(e); + if (callback instanceof Function) callback(); + } +} diff --git a/_server/editor_datapanel.js b/_server/editor_datapanel.js new file mode 100644 index 0000000..075a067 --- /dev/null +++ b/_server/editor_datapanel.js @@ -0,0 +1,1228 @@ +editor_datapanel_wrapper = function (editor) { + + // 此文件内的内容仅做了分类, 未仔细整理函数 + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + var pout = document.getElementById('pout'); + var exportMap = document.getElementById('exportMap'); + var importMap = document.getElementById('importMap'); + var clearMapButton=document.getElementById('clearMapButton') + var deleteMap=document.getElementById('deleteMap') + + var formatArr= function () { + var formatArrStr = ''; + + var si=editor.map.length,sk=editor.map[0].length; + if (pout.value.split(/\D+/).join(' ').trim().split(' ').length != si*sk) return false; + var arr = pout.value.replace(/\s+/g, '').split('],['); + + if (arr.length != si) return; + for (var i = 0; i < si; i++) { + var a = []; + formatArrStr += '['; + if (i == 0 || i == si-1) a = arr[i].split(/\D+/).join(' ').trim().split(' '); + else a = arr[i].split(/\D+/); + if (a.length != sk) { + formatArrStr = ''; + return; + } + + for (var k = 0; k < sk; k++) { + var num = parseInt(a[k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk-1 ? '' : ','); + } + formatArrStr += ']' + (i == si-1 ? '' : ',\n'); + } + return formatArrStr; + } + + exportMap.onclick=function(){ + editor.updateMap(); + var sx=editor.map.length-1,sy=editor.map[0].length-1; + + var filestr = ''; + for (var yy = 0; yy <= sy; yy++) { + filestr += '[' + for (var xx = 0; xx <= sx; xx++) { + var mapxy = editor.map[yy][xx]; + if (typeof(mapxy) == typeof({})) { + if ('idnum' in mapxy) mapxy = mapxy.idnum; + else { + printe("生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!"); + return; + } + } else if (typeof(mapxy) == 'undefined') { + printe("生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!"); + return; + } + mapxy = String(mapxy); + mapxy = Array(Math.max(4 - mapxy.length, 0)).join(' ') + mapxy; + filestr += mapxy + (xx == sx ? '' : ',') + } + + filestr += ']' + (yy == sy ? '' : ',\n'); + } + pout.value = filestr; + if (formatArr()) { + pout.focus(); + pout.setSelectionRange(0, pout.value.length); + document.execCommand("Copy"); + printf("导出并复制成功!"); + } else { + printe("无法导出并复制此地图,可能有不合法块。") + } + } + importMap.onclick= function () { + var sy=editor.map.length,sx=editor.map[0].length; + var mapArray = null; + var value = pout.value.trim(); + // 去除可能末尾的 ',' + if (value.endsWith(',')) value = value.substring(0, value.length - 1); + try { mapArray = JSON.parse(value); } catch (e) {console.log(e)} + try { mapArray = mapArray || JSON.parse('[' + value + ']'); } catch (e) {console.log(e)} + if (mapArray == null || mapArray.length != sy || mapArray[0].length != sx) { + printe('格式错误!请使用正确格式(请使用地图生成器进行生成,且需要和本地图宽高完全一致)'); + return; + } + var hasError = false; + for (var y = 0; y < sy; y++) + for (var x = 0; x < sx; x++) { + var num = mapArray[y][x]; + if (num == 0) + editor.map[y][x] = 0; + else if (editor.indexs[num]==null || editor.indexs[num][0]==null) { + printe('当前有未定义ID(在地图区域显示红块),请用有效的图块进行覆盖!') + hasError = true; + editor.map[y][x] = {}; + } else editor.map[y][x] = editor.ids[[editor.indexs[num][0]]]; + } + editor.updateMap(); + if (!hasError) printf('地图导入成功!'); + } + + clearMapButton.onclick=function () { + if (!confirm('你确定要清除地图上所有内容么?此过程不可逆!')) return; + editor.mapInit(); + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw(err) + } + ;printf('地图清除成功'); + }); + editor.updateMap(); + } + + deleteMap.onclick=function () { + if (!confirm('你确定要删除此地图么?此过程不可逆!')) return; + editor_mode.onmode(''); + var index = core.floorIds.indexOf(editor.currentFloorId); + if (index>=0) { + core.floorIds.splice(index,1); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw(objs_.slice(-1)[0]) + } + ;printe('删除成功,请F5刷新编辑器生效'); + }); + } + else printe('删除成功,请F5刷新编辑器生效'); + } + + editor.uifunctions.newMap_func = function () { + + var newMap = document.getElementById('newMap'); + var newFileName = document.getElementById('newFileName'); + newMap.onclick = function () { + if (!newFileName.value) return; + var findFunc = function (id) { + var re = new RegExp(newFileName.value, 'i'); + return re.test(id); + } + if (core.floorIds.find(findFunc) != null) { + printe("同名楼层已存在!(不区分大小写)"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(newFileName.value)) { + printe("楼层名不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + if (!core.isset(width) || !core.isset(height) || width > 128 || height > 128) { + printe("新建地图的宽高都不得大于128"); + return; + } + + editor_mode.onmode(''); + editor.file.saveNewFile(newFileName.value, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds.push(newFileName.value); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('新建成功,请F5刷新编辑器生效'); + }); + }); + } + + } + + + editor.uifunctions.createNewMaps_func = function () { + + document.getElementById('newMapWidth').value = core.__SIZE__; + document.getElementById('newMapHeight').value = core.__SIZE__; + document.getElementById('newMapsWidth').value = core.__SIZE__; + document.getElementById('newMapsHeight').value = core.__SIZE__; + + var newMaps = document.getElementById('newMaps'); + var newFloors = document.getElementById('newFloors'); + newMaps.onclick = function () { + if (newFloors.style.display == 'none') newFloors.style.display = 'block'; + else newFloors.style.display = 'none'; + } + + var createNewMaps = document.getElementById('createNewMaps'); + createNewMaps.onclick = function () { + var floorIds = document.getElementById('newFloorIds').value; + if (!floorIds) return; + var from = parseInt(document.getElementById('newMapsFrom').value), + to = parseInt(document.getElementById('newMapsTo').value); + if (!core.isset(from) || !core.isset(to) || from > to) { + printe("请输入有效的起始和终止楼层"); + return; + } + if (to - from >= 100) { + printe("一次最多创建99个楼层"); + return; + } + var floorIdList = []; + for (var i = from; i <= to; i++) { + var floorId = floorIds.replace(/\${(.*?)}/g, function (word, value) { + return eval(value); + }); + var findFunc = function (id) { + var re = new RegExp(floorId, 'i'); + return re.test(id); + } + if (core.floorIds.find(findFunc) != null) { + printe("同名楼层已存在!(不区分大小写)"); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (floorIdList.indexOf(floorId) >= 0) { + printe("尝试重复创建楼层 " + floorId + " !"); + return; + } + floorIdList.push(floorId); + } + + var width = parseInt(document.getElementById('newMapsWidth').value); + var height = parseInt(document.getElementById('newMapsHeight').value); + if (!core.isset(width) || !core.isset(height) || width > 128 || height > 128) { + printe("新建地图的宽高都不得大于128"); + return; + } + editor_mode.onmode(''); + + editor.file.saveNewFiles(floorIdList, from, to, function (err) { + if (err) { + printe(err); + throw (err) + } + core.floorIds = core.floorIds.concat(floorIdList); + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('批量创建 ' + floorIdList[0] + '~' + floorIdList[floorIdList.length - 1] + ' 成功,请F5刷新编辑器生效'); + }); + }); + } + + } + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 地图选点 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + // 添加自动事件页,无需双击 + editor.uifunctions.addAutoEvent = function () { + if (editor_mode.mode != 'loc') return false; + var newid = '2'; + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); + newid = testid + ''; + } + editor_mode.addAction(['add', "['autoEvent']['" + newid + "']", null]); + editor_mode.onmode('save'); + } + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 图块属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + editor.uifunctions.newIdIdnum_func = function () { + var newIdIdnum = document.getElementById('newIdIdnum'); + newIdIdnum.children[2].onclick = function () { + if (newIdIdnum.children[0].value && newIdIdnum.children[1].value) { + var id = newIdIdnum.children[0].value; + var idnum = parseInt(newIdIdnum.children[1].value); + if (!core.isset(idnum)) { + printe('不合法的idnum'); + return; + } + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线,且不能以数字开头'); + return; + } + if (id == 'hero' || id == 'this' || id == 'none' || id == 'airwall') { + printe('不得使用保留关键字作为id!'); + return; + } + if (core.statusBar.icons[id] != null) { + alert('警告!此ID在状态栏图标中被注册;仍然允许使用,但是\\i[]等绘制可能出现冲突。'); + } + editor.file.changeIdAndIdnum(id, idnum, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('添加id和idnum成功,请F5刷新编辑器'); + }); + } else { + printe('请输入id和idnum'); + } + } + newIdIdnum.children[4].onclick = function () { + editor.file.autoRegister(editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err) + } + printe('该列所有剩余项全部自动注册成功,请F5刷新编辑器'); + }) + } + newIdIdnum.children[5].onclick = function () { + if (!confirm("警告!你确定要删除此素材吗?此过程不可逆!")) return; + editor.file.removeMaterial(editor_mode.info, function (err) { + if (err) { + printe(err); + throw err; + } + alert('删除此素材成功!'); + window.location.reload(); + }); + } + newIdIdnum.children[6].onclick = function () { + editor.uifunctions.appendMaterialByInfo(editor_mode.info); + } + } + + editor.uifunctions.changeId_func = function () { + var changeId = document.getElementById('changeId'); + changeId.children[1].onclick = function () { + var id = changeId.children[0].value; + if (id) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(id)) { + printe('不合法的id,请使用字母、数字或下划线,且不能以数字开头') + return; + } + if (id == 'hero' || id == 'this' || id == 'none' || id == 'airwall') { + printe('不得使用保留关键字作为id!'); + return; + } + if (editor_mode.info.images == 'autotile') { + printe('自动元件不可修改id!'); + return; + } + if (editor_mode.info.idnum >= 10000) { + printe('额外素材不可修改id!'); + return; + } + if (core.statusBar.icons[id] != null) { + alert('警告!此ID在状态栏图标中被注册;仍然允许使用,但是\\i[]等绘制可能出现冲突。'); + } + editor.file.changeIdAndIdnum(id, null, editor_mode.info, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('修改id成功,请F5刷新编辑器'); + }); + } else { + printe('请输入要修改到的ID'); + } + } + changeId.children[2].onclick = function () { + if (editor_mode.info.isTile) { + printe("额外素材不可删除!"); + return; + } + if (!confirm("警告!你确定要删除此素材吗?此过程不可逆!\n请务必首先进行备份操作,并保证此素材没有在地图的任何位置使用,否则可能会出现不可知的后果!")) return; + editor.file.removeMaterial(editor_mode.info, function (err) { + if (err) { + printe(err); + return; + } + alert('删除此素材成功!'); + window.location.reload(); + }); + } + changeId.children[3].onclick = function () { + editor.uifunctions.appendMaterialByInfo(editor_mode.info); + } + } + + editor.uifunctions.copyPasteEnemyItem_func = function () { + var copyEnemyItem = document.getElementById('copyEnemyItem'); + var pasteEnemyItem = document.getElementById('pasteEnemyItem'); + var clearEnemyItem = document.getElementById('clearEnemyItem'); + var clearAllEnemyItem = document.getElementById('clearAllEnemyItem'); + + copyEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || (cls != 'enemys' && cls != 'enemy48' && cls != 'items')) return; + editor.uivalues.copyEnemyItem.type = cls; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + editor.uivalues.copyEnemyItem.data = core.clone(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id]); + printf("怪物属性复制成功"); + } else if (cls == 'items') { + editor.uivalues.copyEnemyItem.data = core.clone(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]); + printf("道具属性复制成功"); + } + } + + pasteEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls || cls != editor.uivalues.copyEnemyItem.type) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要覆盖此怪物的全部属性么?这是个不可逆操作!")) { + var name = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name; + var displayIdInBook = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id] = core.clone(editor.uivalues.copyEnemyItem.data); + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].id = id; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name = name; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook = displayIdInBook; + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("怪物属性粘贴成功\n请再重新选中该怪物方可查看更新后的表格。"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要覆盖此道具的全部属性么?这是个不可逆操作!")) { + var name = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].name; + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id] = core.clone(editor.uivalues.copyEnemyItem.data); + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].id = id; + items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id].name = name; + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("道具属性粘贴成功\n请再重新选中该道具方可查看更新后的表格。"); + }) + } + } + } + + var _clearEnemy = function (id) { + var info = core.clone(comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.enemys_template); + info.id = id; + info.name = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].name; + info.displayIdInBook = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id].displayIdInBook; + enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id] = info; + } + + var _clearItem = function (id) { + for (var x in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]) { + if (x != 'id' && x!='cls' && x != 'name') delete items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id][x]; + } + } + + clearEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要清空本怪物的全部属性么?这是个不可逆操作!")) { + _clearEnemy(id); + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("怪物属性清空成功\n请再重新选中该怪物方可查看更新后的表格。"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要清空本道具的全部属性么?这是个不可逆操作!")) { + _clearItem(id); + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("道具属性清空成功\n请再重新选中该道具方可查看更新后的表格。"); + }) + } + } + } + + clearAllEnemyItem.onclick = function () { + var cls = (editor_mode.info || {}).images; + if (editor_mode.mode != 'enemyitem' || !cls) return; + var id = editor_mode.info.id; + if (cls == 'enemys' || cls == 'enemy48') { + if (confirm("你确定要批量清空【全塔怪物】的全部属性么?这是个不可逆操作!")) { + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) + _clearEnemy(id); + editor.file.saveSetting('enemys', [], function (err) { + if (err) printe(err); + else printf("全塔全部怪物属性清空成功!"); + }) + } + } else if (cls == 'items') { + if (confirm("你确定要批量清空【全塔所有自动注册且未修改ID的道具】的全部属性么?这是个不可逆操作!")) { + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + if (/^I\d+$/.test(id)) { + _clearItem(id); + } + } + editor.file.saveSetting('items', [], function (err) { + if (err) printe(err); + else printf("全塔全部道具属性清空成功!"); + }) + } + } + } + + + + + + } + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 楼层属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + editor.uifunctions.changeFloorId_func = function () { + + editor.dom.changeFloorId.children[1].onclick = function () { + var floorId = editor.dom.changeFloorId.children[0].value; + if (floorId) { + if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(floorId)) { + printe("楼层名 " + floorId + " 不合法!请使用字母、数字、下划线,且不能以数字开头!"); + return; + } + if (main.floorIds.indexOf(floorId) >= 0) { + printe("楼层名 " + floorId + " 已存在!"); + return; + } + var currentFloorId = editor.currentFloorId; + editor.currentFloorId = floorId; + editor.currentFloorData.floorId = floorId; + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err); + } + core.floorIds[core.floorIds.indexOf(currentFloorId)] = floorId; + editor.file.editTower([['change', "['main']['floorIds']", core.floorIds]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + alert("修改floorId成功,需要刷新编辑器生效。\n请注意,原始的楼层文件没有删除,请根据需要手动删除。"); + window.location.reload(); + }); + }); + } else { + printe('请输入要修改到的floorId'); + } + } + } + + editor.uifunctions.changeFloorSize_func = function () { + var children = editor.dom.changeFloorSize.children; + children[4].onclick = function () { + var width = parseInt(children[0].value); + var height = parseInt(children[1].value); + var x = parseInt(children[2].value); + var y = parseInt(children[3].value); + if (!(width <= 128 && height <= 128 && x >= 0 && y >= 0)) { + printe("参数错误!宽高不得大于128,偏移量不得小于0"); + return; + } + var currentFloorData = editor.currentFloorData; + var currWidth = currentFloorData.width; + var currHeight = currentFloorData.height; + if (width < currWidth) x = -x; + if (height < currHeight) y = -y; + // Step 1:创建一个新的地图 + var newFloorData = core.clone(currentFloorData); + newFloorData.width = width; + newFloorData.height = height; + + // Step 2:更新map, bgmap和fgmap + editor.dom.maps.forEach(function (name) { + newFloorData[name] = []; + if (currentFloorData[name] && currentFloorData[name].length > 0) { + for (var j = 0; j < height; ++j) { + newFloorData[name][j] = []; + for (var i = 0; i < width; ++i) { + var oi = i - x; + var oj = j - y; + if (oi >= 0 && oi < currWidth && oj >= 0 && oj < currHeight) { + newFloorData[name][j].push(currentFloorData[name][oj][oi]); + } else { + newFloorData[name][j].push(0); + } + } + } + } + }); + + // Step 3:更新所有坐标 + ["events", "beforeBattle", "afterBattle", "afterGetItem", "afterOpenDoor", "changeFloor", "autoEvent", "cannotMove"].forEach(function (name) { + newFloorData[name] = {}; + if (!currentFloorData[name]) return; + for (var loc in currentFloorData[name]) { + var oxy = loc.split(','), ox = parseInt(oxy[0]), oy = parseInt(oxy[1]); + var nx = ox + x, ny = oy + y; + if (nx >= 0 && nx < width && ny >= 0 && ny < height) { + newFloorData[name][nx+","+ny] = core.clone(currentFloorData[name][loc]); + } + } + }); + + // Step 4:上楼点&下楼点 + ["upFloor", "downFloor"].forEach(function (name) { + if (newFloorData[name] && newFloorData[name].length == 2) { + newFloorData[name][0]+=x; + newFloorData[name][1]+=y; + } + }); + + editor.file.saveFloor(newFloorData, function (err) { + if (err) { + printe(err); + throw(err) + } + ;alert('地图更改大小成功,即将刷新地图...\n请检查所有点的事件是否存在问题。'); + window.location.reload(); + }); + } + } + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 全塔属性 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 脚本编辑 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 追加素材 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + editor.uifunctions.appendPic_func = function () { + // --- fix ctx + [editor.dom.appendSourceCtx, editor.dom.appendSpriteCtx].forEach(function (ctx) { + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + }) + + // --- selectAppend + var selectAppend_str = []; + ["terrains", "animates", "enemys", "enemy48", "items", "npcs", "npc48", "autotile"].forEach(function (image) { + selectAppend_str.push(["\n'].join('')); + }); + editor.dom.selectAppend.innerHTML = selectAppend_str.join(''); + editor.dom.selectAppend.onchange = function () { + + var value = editor.dom.selectAppend.value; + + if (value == 'autotile') { + editor_mode.appendPic.imageName = 'autotile'; + for (var jj = 0; jj < 4; jj++) editor.dom.appendPicSelection.children[jj].style = 'display:none'; + if (editor_mode.appendPic.img) { + editor.dom.appendSprite.style.width = (editor.dom.appendSprite.width = editor_mode.appendPic.img.width) / editor.uivalues.ratio + 'px'; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = editor_mode.appendPic.img.height) / editor.uivalues.ratio + 'px'; + editor.dom.appendSpriteCtx.clearRect(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + editor.dom.appendSpriteCtx.drawImage(editor_mode.appendPic.img, 0, 0); + } + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + editor_mode.appendPic.imageName = value; + var img = core.material.images[value]; + editor_mode.appendPic.toImg = img; + var num = ~~img.width / 32; + editor_mode.appendPic.num = num; + editor_mode.appendPic.index = 0; + var selectStr = ''; + for (var ii = 0; ii < num; ii++) { + editor.dom.appendPicSelection.children[ii].style = 'left:0;top:0;height:' + (ysize - 6) + 'px'; + selectStr += '{"x":0,"y":0},' + } + editor_mode.appendPic.selectPos = eval('[' + selectStr + ']'); + for (var jj = num; jj < 4; jj++) { + editor.dom.appendPicSelection.children[jj].style = 'display:none'; + } + editor.dom.appendSprite.style.width = (editor.dom.appendSprite.width = img.width) / editor.uivalues.ratio + 'px'; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = img.height + ysize) / editor.uivalues.ratio + 'px'; + editor.dom.appendSpriteCtx.drawImage(img, 0, 0); + } + editor.dom.selectAppend.onchange(); + + // --- selectFileBtn + var autoAdjust = function (image, callback) { + var changed = false; + + // Step 1: 检测白底 + var tempCanvas = document.createElement('canvas').getContext('2d'); + tempCanvas.canvas.width = image.width; + tempCanvas.canvas.height = image.height; + tempCanvas.mozImageSmoothingEnabled = false; + tempCanvas.webkitImageSmoothingEnabled = false; + tempCanvas.msImageSmoothingEnabled = false; + tempCanvas.imageSmoothingEnabled = false; + tempCanvas.drawImage(image, 0, 0); + var imgData = tempCanvas.getImageData(0, 0, image.width, image.height); + var trans = 0, white = 0, black = 0; + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.getPixel(imgData, i, j); + if (pixel[3] == 0) trans++; + if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255 && pixel[3] == 255) white++; + // if (pixel[0]==0 && pixel[1]==0 && pixel[2]==0 && pixel[3]==255) black++; + } + } + if (white > black && white > trans * 10 && confirm("看起来这张图片是以纯白为底色,是否自动调整为透明底色?")) { + for (var i = 0; i < image.width; i++) { + for (var j = 0; j < image.height; j++) { + var pixel = editor.util.getPixel(imgData, i, j); + if (pixel[0] == 255 && pixel[1] == 255 && pixel[2] == 255 && pixel[3] == 255) { + editor.util.setPixel(imgData, i, j, [0, 0, 0, 0]); + } + } + } + tempCanvas.clearRect(0, 0, image.width, image.height); + tempCanvas.putImageData(imgData, 0, 0); + changed = true; + } + /* + if (black>white && black>trans*10 && confirm("看起来这张图片是以纯黑为底色,是否自动调整为透明底色?")) { + for (var i=0;i= num) editor_mode.appendPic.index = ii + 1 - num; + else editor_mode.appendPic.index++; + editor_mode.appendPic.selectPos[ii] = pos; + editor.dom.appendPicSelection.children[ii].style = [ + 'left:', pos.x * 32, 'px;', + 'top:', pos.y * pos.ysize, 'px;', + 'height:', pos.ysize - 6, 'px;' + ].join(''); + } + + // appendConfirm + var appendRegister = document.getElementById('appendRegister'); + var appendConfirm = document.getElementById('appendConfirm'); + appendConfirm.onclick = function () { + + var confirmAutotile = function () { + var image = editor_mode.appendPic.img; + if (image.width % 96 != 0 || image.height != 128) { + printe("不合法的Autotile图片!"); + return; + } + var imgData = editor.dom.appendSourceCtx.getImageData(0, 0, image.width, image.height); + editor.dom.appendSpriteCtx.putImageData(imgData, 0, 0); + var imgbase64 = editor.dom.appendSprite.toDataURL().split(',')[1]; + + // Step 1: List文件名 + fs.readdir('./project/autotiles', function (err, data) { + if (err) { + printe(err); + throw (err); + } + + // Step 2: 选择Autotile文件名 + var filename; + for (var i = 1; ; ++i) { + filename = 'autotile' + i; + if (data.indexOf(filename + ".png") == -1) break; + } + + // Step 3: 写入文件 + fs.writeFile('./project/autotiles/' + filename + ".png", imgbase64, 'base64', function (err, data) { + if (err) { + printe(err); + throw (err); + } + // Step 4: 自动注册 + editor.file.registerAutotile(filename, function (err) { + if (err) { + printe(err); + throw (err); + } + printe('自动元件' + filename + '注册成功,请F5刷新编辑器'); + }) + + }) + + }) + + } + + if (editor.dom.selectAppend.value == 'autotile') { + confirmAutotile(); + return; + } + + var ysize = editor.dom.selectAppend.value.endsWith('48') ? 48 : 32; + for (var ii = 0, v; v = editor_mode.appendPic.selectPos[ii]; ii++) { + // var imgData = editor.dom.appendSourceCtx.getImageData(v.x * 32, v.y * ysize, 32, ysize); + // editor.dom.appendSpriteCtx.putImageData(imgData, ii * 32, editor.dom.appendSprite.height - ysize); + // editor.dom.appendSpriteCtx.drawImage(editor_mode.appendPic.img, v.x * 32, v.y * ysize, 32, ysize, ii * 32, height, 32, ysize) + + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, v.x * 32, v.y * ysize, 32, ysize, 32 * ii, editor.dom.appendSprite.height - ysize, 32, ysize); + } + var dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var imgbase64 = editor.dom.appendSprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/materials/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.appendSprite.height; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (currHeight + ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }); + } else { + printf('追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + } + + var quickAppendConfirm = document.getElementById('quickAppendConfirm'); + quickAppendConfirm.onclick = function () { + var value = editor.dom.selectAppend.value; + if (value != 'items' && value != 'enemys' && value != 'enemy48' && value != 'npcs' && value != 'npc48') + return printe("只有怪物或NPC才能快速导入!"); + var ysize = value.endsWith('48') ? 48 : 32; + var sw = editor.dom.appendSourceCtx.canvas.width, sh = editor.dom.appendSourceCtx.canvas.height; + if (value == 'items') { + if (sw % 32 || sh % 32) { + return printe("只有长宽都是32的倍数的道具图才可以快速导入!"); + } + } else { + if ((sw != 128 && sw != 96) || sh != 4 * ysize) { + return printe("只有 3*4 或 4*4 的素材图片才可以快速导入!"); + } + } + sw = sw / 32; + sh = sh / ysize; + + var dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var appendSize = value == 'items' ? (sw * sh - 1) : 3; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (editor.dom.appendSprite.height + appendSize * ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + if (editor.dom.appendSprite.width == 32) { // 1帧:道具 + for (var i = 0; i < sw * sh; ++i) { + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32 * (i % sw), 32 * parseInt(i / sw), 32, 32, 0, editor.dom.appendSprite.height - (sw * sh - i) * ysize, 32, 32); + } + } else if (editor.dom.appendSprite.width == 64) { // 两帧 + if (sw == 3) { + // 3*4的规格使用13帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 32, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 64, 0, 32, 4 * ysize, 32, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + } else { + // 4*4的规格使用23帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32, 0, 64, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 64, 4 * ysize); + } + } else { // 四帧 + if (sw == 3) { + // 3*4的规格使用2123帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 32, 0, 32, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 32, 4 * ysize); + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 96, 4 * ysize, 32, editor.dom.appendSprite.height - 4 * ysize, 96, 4 * ysize); + } else { + // 4*4的规格使用1234帧 + editor.dom.appendSpriteCtx.drawImage(editor.dom.appendSourceCtx.canvas, 0, 0, 128, 4 * ysize, 0, editor.dom.appendSprite.height - 4 * ysize, 128, 4 * ysize); + } + } + + dt = editor.dom.appendSpriteCtx.getImageData(0, 0, editor.dom.appendSprite.width, editor.dom.appendSprite.height); + var imgbase64 = editor.dom.appendSprite.toDataURL('image/png'); + var imgName = editor_mode.appendPic.imageName; + fs.writeFile('./project/materials/' + imgName + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) { + printe(err); + throw (err) + } + var currHeight = editor.dom.appendSprite.height; + editor.dom.appendSprite.style.height = (editor.dom.appendSprite.height = (currHeight + ysize)) + "px"; + editor.dom.appendSpriteCtx.putImageData(dt, 0, 0); + core.material.images[imgName].src = imgbase64; + editor.widthsX[imgName][3] = currHeight; + if (appendRegister && appendRegister.checked) { + editor.file.autoRegister({images: imgName}, function (e) { + if (e) { + printe(e); + throw e; + } + printf('快速追加素材并自动注册成功!你可以继续追加其他素材,最后再刷新以使用。'); + }) + } else { + printf('快速追加素材成功!你可以继续追加其他素材,最后再刷新以使用。'); + } + }); + + } + + editor.uifunctions.dragImageToAppend = function (file, cls) { + editor.mode.change('appendpic'); + editor.dom.selectAppend.value = cls; + editor.dom.selectAppend.onchange(); + + var reader = new FileReader(); + reader.onload = function () { + afterReadFile(reader.result, function() { + if (cls == 'terrains') return; + if (confirm('你确定要快速追加么?')) { + if (cls == 'autotile') { + appendConfirm.onclick(); + } else { + quickAppendConfirm.onclick(); + } + } + }); + } + reader.readAsDataURL(file); + } + + editor.uifunctions.appendMaterialByInfo = function (info) { + if (info.isTile) { + printe('额外素材不支持此功能!'); + return; + } + var img = null; + var cls = info.images; + var height = cls == 'enemy48' || cls == 'npc48' ? 48 : 32; + + if (cls == 'autotile') { + img = core.material.images.autotile[info.id]; + } else { + var image = core.material.images[cls]; + var width = image.width; + img = document.createElement('canvas'); + img.width = width; + img.height = height; + img.getContext('2d').drawImage(image, 0, info.y * height, width, height, 0, 0, width, height); + } + + editor.mode.change('appendpic'); + editor.dom.selectAppend.value = cls; + editor.dom.selectAppend.onchange(); + + afterReadFile(img, function () { + changeColorInput.value = 0; + if (cls == 'autotile') return; + + editor_mode.appendPic.index = 0; + for (var ii = 0; ii < editor_mode.appendPic.num; ++ii) { + editor_mode.appendPic.selectPos[ii] = {x: ii, y: 0, ysize: height}; + editor.dom.appendPicSelection.children[ii].style = [ + 'left:', ii * 32, 'px;', + 'top:', 0, 'px;', + 'height:', height - 6, 'px;' + ].join(''); + } + }); + } + } + + /////////////////////////////////////////////////////////////////////// + //////////////////// 公共事件 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + /////////////////////////////////////////////////////////////////////// + //////////////////// 插件编写 ////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + +} diff --git a/_server/editor_file.js b/_server/editor_file.js new file mode 100644 index 0000000..1b4a200 --- /dev/null +++ b/_server/editor_file.js @@ -0,0 +1,1118 @@ +editor_file_wrapper = function (editor) { + editor_file_proto = function () { + /** + * 以 + * { + * "floor.MT1":, + * "plugins": + * } + * 的形式记录所有更改过的文件,save时写入 + * 的内容暂时还没想好 + * + * Mark相关的思路搁置 + */ + this.fileMark = {} + } + + // 这个函数之后挪到editor.table? + editor_file_proto.prototype.loadCommentjs = function (callback) { + var commentjs = { + 'comment': 'comment', + 'data.comment': 'dataComment', + 'functions.comment': 'functionsComment', + 'events.comment': 'eventsComment', + 'plugins.comment': 'pluginsComment', + } + for (var key in commentjs) { + (function (key) { + var value = commentjs[key]; + var script = document.createElement('script'); + if (window.location.href.indexOf('_server') !== -1) + script.src = key + '.js'; + else + script.src = '_server/table/' + key + '.js'; + document.body.appendChild(script); + script.onload = function () { + editor.file[value] = eval(key.replace('.', '_') + '_c456ea59_6018_45ef_8bcc_211a24c627dc'); + var loaded = Boolean(callback); + for (var key_ in commentjs) { + loaded = loaded && editor.file[commentjs[key_]] + } + if (loaded) callback(); + } + })(key); + } + } + + editor_file_proto.prototype.alertWhenCompress = function () { + if (editor.useCompress === true) { + editor.useCompress = 'alerted'; + setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请使用启动服务.exe->Js代码压缩工具重新压缩,或者把main.js的useCompress改成false来使用原始文件')", 1000) + } + } + + editor_file_proto.prototype.formatMap = function (mapArr, trySimplify) { + if (!mapArr || JSON.stringify(mapArr) == JSON.stringify([])) return ''; + if (trySimplify) { + //检查是否是全0二维数组 + var jsoncheck = JSON.stringify(mapArr).replace(/\D/g, ''); + if (jsoncheck == Array(jsoncheck.length + 1).join('0')) return ''; + } + //把二维数组格式化 + var formatArrStr = ''; + var arr = JSON.stringify(mapArr).replace(/\s+/g, '').split('],['); + var si = mapArr.length - 1, sk = mapArr[0].length - 1; + for (var i = 0; i <= si; i++) { + var a = []; + formatArrStr += ' ['; + if (i == 0 || i == si) a = arr[i].split(/\D+/).join(' ').trim().split(' '); + else a = arr[i].split(/\D+/); + for (var k = 0; k <= sk; k++) { + var num = parseInt(a[k]); + formatArrStr += Array(Math.max(4 - String(num).length, 0)).join(' ') + num + (k == sk ? '' : ','); + } + formatArrStr += ']' + (i == si ? '' : ',\n'); + } + return formatArrStr; + } + + editor_file_proto.prototype.saveFloor = function (floorData, callback) { + //callback(err:String) + var floorId = floorData.floorId; + var filename = 'project/floors/' + floorId + '.js'; + var datastr = ['main.floors.', floorId, '=\n']; + + var tempJsonObj = Object.assign({}, floorData); + var tempMap = editor.dom.maps.map(function (one) { + return [one, editor.util.guid()] + }); + tempMap.forEach(function (v) { + v[2] = tempJsonObj[v[0]]; + tempJsonObj[v[0]] = v[1]; + }); + var tempJson = JSON.stringify(tempJsonObj, editor.game.replacerForSaving, 4); + tempMap.forEach(function (v) { + tempJson = tempJson.replace('"' + v[1] + '"', '[\n' + editor.file.formatMap(v[2], v[0] != 'map') + '\n]') + }); + datastr = datastr.concat([tempJson]); + datastr = datastr.join(''); + editor.file.alertWhenCompress(); + editor.fs.writeFile(filename, editor.util.encode64(datastr), 'base64', function (err, data) { + editor.addUsedFlags(datastr); + callback(err); + }); + } + + editor_file_proto.prototype.saveScript = function (name, varName, dataObj, callback) { + // 此处格式化以及写入 project/xxx.js 形式的文件 + editor.file.alertWhenCompress(); + + if (['maps', 'enemys'].indexOf(name) === -1) { + // 全部用\t展开 + var content = JSON.stringify(dataObj, editor.game.replacerForSaving, '\t'); + } else { + // 只用\t展开第一层 + var emap = {}; + var estr = JSON.stringify(dataObj, function (_k, v) { + if (v.id != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v, editor.game.replacerForSaving); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]); + } + var content = estr; + } + + var strToWrite = `var ${varName} = \n${content}`; + editor.fs.writeFile(`project/${name}.js`, editor.util.encode64(strToWrite), 'base64', function (err, data) { + editor.addUsedFlags(content); + callback(err); + }); + } + + editor_file_proto.prototype.saveCommentJs = function () { + // 无需格式化的写入, 把multi的那部分略微修改 + } + + editor_file_proto.prototype.saveImage = function () { + // 给追加素材使用 + } + + // Mark相关的思路搁置 + editor_file_proto.prototype.addMark = function (name) { + // 把name对应的文件在editor.file.fileMark添加标记 + } + + editor_file_proto.prototype.save = function (callback) { + // 根据 editor.file.fileMark 把游戏对象格式化写入文件 + } +} + +///////////////////////////////////////////////////////////////////////// + + +editor_file = function (editor, callback) { + + var editor_file = new editor_file_proto(); + editor.file=editor_file; + + editor.file.loadCommentjs(callback); + + editor.file.saveFloorFile = function (callback) { + //callback(err:String) + checkCallback(callback); + /* if (!isset(editor.currentFloorId) || !isset(editor.currentFloorData)) { + callback('未选中文件或无数据'); + } */ + if (core.floorIds.indexOf(editor.currentFloorId) >= 0) { + for(var ii=0,name;name=editor.dom.maps[ii];ii++){ + var mapArray=editor[name].map(function (v) { + return v.map(function (v) { + return v.idnum || v || 0 + }) + }); + editor.currentFloorData[name]=mapArray; + } + } + editor.file.saveFloor(editor.currentFloorData, callback) + } + + /////////////////////////////////////////////////////////////////////////// + + editor.file.saveNewFile = function (saveFilename, callback) { + //saveAsFilename不含'/'不含'.js' + checkCallback(callback); + var currData=editor.currentFloorData; + var saveStatus = document.getElementById('newMapStatus').checked; + + var title = saveStatus?currData.title:"新建楼层"; + var name = saveStatus?currData.name:"0"; + if (/^mt\d+$/i.test(saveFilename)) { + name = saveFilename.substring(2); + title = "主塔 "+name+" 层"; + } + + var width = parseInt(document.getElementById('newMapWidth').value); + var height = parseInt(document.getElementById('newMapHeight').value); + var row = [], map = []; + for (var i=0;i= 4) { + var i = faceIds.length - 4; + var down = faceIds[i], left = faceIds[i+1], right = faceIds[i+2], up = faceIds[i+3]; + var obj = {down: down.id, left: left.id, right: right.id, up: up.id}; + if (image.indexOf('enemy')==0) { + templateActions.push(["add", "['" + down.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + left.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + right.id + "']['faceIds']", obj]); + templateActions.push(["add", "['" + up.id + "']['faceIds']", obj]); + } else { + mapActions.push(["add", "['" + down.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + left.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + right.idnum + "']['faceIds']", obj]); + mapActions.push(["add", "['" + up.idnum + "']['faceIds']", obj]); + } + } + } + + if (mapActions.length==0) { + callback("没有要注册的项!"); + return; + } + + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 3) { + if (templist[0] != null || templist[1] != null || templist[2] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '') + '\n' + (templist[2] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + if (iconActions.length>0) + saveSetting('icons', iconActions, tempcallback); + else tempcallback(null); + + saveSetting('maps', mapActions, tempcallback); + + if (image=='items') + saveSetting('items', templateActions, tempcallback); + else if (image.indexOf('enemy')==0) + saveSetting('enemys', templateActions, tempcallback); + else tempcallback(null); + } + + editor.file.registerAutotile = function (filename, callback) { + var idnum = 140; + while (editor.core.maps.blocksInfo[idnum]) idnum++; + + var iconActions = []; + var mapActions = []; + + iconActions.push(["add", "['autotile']['" + filename + "']", 0]); + mapActions.push(["add", "['" + idnum + "']", {'cls': 'autotile', 'id': filename}]); + + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + + saveSetting('icons', iconActions, tempcallback); + saveSetting('maps', mapActions, tempcallback); + } + + editor.file.changeIdAndIdnum = function (id, idnum, info, callback) { + checkCallback(callback); + + var changeOrNew=core.isset(editor_mode.info.id)?'change':'new' + if(changeOrNew=='new'){ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (ii == idnum) { + callback('idnum重复了'); + return; + } + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + var templist = []; + var tempcallback = function (err) { + templist.push(err); + if (templist.length == 2) { + if (templist[0] != null || templist[1] != null) + callback((templist[0] || '') + '\n' + (templist[1] || '')); + //这里如果一个成功一个失败会出严重bug + else + callback(null); + } + } + saveSetting('maps', [["add", "['" + idnum + "']", {'cls': info.images, 'id': id}]], tempcallback); + saveSetting('icons', [["add", "['" + info.images + "']['" + id + "']", info.y]], tempcallback); + if (info.images === 'items') { + saveSetting('items', [["add", "['" + id + "']", editor.file.comment._data.items_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + if (info.images === 'enemys' || info.images === 'enemy48') { + saveSetting('enemys', [["add", "['" + id + "']", editor.file.comment._data.enemys_template]], function (err) { + if (err) { + printe(err); + throw(err) + } + }); + } + + callback(null); + + }else{ + //检查maps中是否有重复的idnum或id + for (var ii in editor.core.maps.blocksInfo) { + if (editor.core.maps.blocksInfo[ii].id == id) { + callback('id重复了'); + return; + } + } + idnum = info.idnum; + + maps_90f36752_8815_4be8_b32b_d7fad1d0542e[idnum].id = id; + + var arr=[icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1,{items: items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a},{enemys: enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80}] + arr.forEach(function (obj) { + for(var jj in obj){ + var ii=obj[jj] + if (ii.hasOwnProperty(info.id)){ + ii[id]=ii[info.id]; + delete(ii[info.id]); + } + } + }); + + editor.file.save_icons_maps_items_enemys(callback) + + } + } + + editor.file.removeMaterial = function (info, callback) { + console.log(info); + + // Step 1: 尝试删除图片 + var _deleteMaterialImage = function (cb) { + if (info.images == 'autotile') return cb(); + var img = core.material.images[info.images]; + if (img == null) return callback('该素材不存在!'); + + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + var width = img.width, height = img.height, per_height = info.images.endsWith('48') ? 48 : 32 + if (height == per_height) return callback('该素材图片只有一个素材,无法删除'); + canvas.width = width; + canvas.height = height - per_height; + ctx.drawImage(img, 0, 0, width, info.y * per_height, 0, 0, width, info.y * per_height); + ctx.drawImage(img, 0, (info.y + 1) * per_height, width, height - (info.y + 1) * per_height, 0, info.y * per_height, width, height - (info.y + 1) * per_height); + var imgbase64 = canvas.toDataURL('image/png'); + fs.writeFile('./project/materials/' + info.images + '.png', imgbase64.split(',')[1], 'base64', function (err, data) { + if (err) return callback(err); + cb(); + }); + } + + _deleteMaterialImage(function () { + // Step 2: 删除图块信息 + if (info.id) { + delete icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1[info.images][info.id]; + delete maps_90f36752_8815_4be8_b32b_d7fad1d0542e[info.idnum]; + if (info.images == 'items') { + delete items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[info.id]; + } + if (info.images == 'enemys' || info.images == 'enemy48') { + delete enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[info.id]; + } + } + + // Step 3: 将所有素材向下移动一格 + if (info.images != 'autotile') { + var value = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1[info.images]; + Object.keys(value).forEach(function (one) { + if (value[one] > info.y) value[one]--; + }); + } + + // Step 4: 保存并删除成功! + editor.file.save_icons_maps_items_enemys(callback); + }); + } + + //callback(err:String) + editor.file.editItem = function (id, actionList, callback) { + /*actionList:[ + ["change","['name']","红宝石的新名字"], + ["add","['新的和name同级的属性']",123], + ["change","['itemEffectTip']","',攻击力+'+editor.core.values.redGem"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['" + id + "']" + value[1]; + }); + saveSetting('items', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.core.items.items[id]); + Object.keys(editor.file.comment._data.items._data).forEach(function (v) { + if (!isset((editor.core.items.items[id]||{})[v])) + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.items, + null]); + } + //只有items.cls是items的才有itemEffect和itemEffectTip,keys和constants和tools只有items + } + //callback([obj,commentObj,err:String]) + editor.file.editEnemy = function (id, actionList, callback) { + /*actionList:[ + ["change","['name']","初级巫师的新名字"], + ["add","['新的和name同级的属性']",123], + ["change","['bomb']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['" + id + "']" + value[1]; + }); + saveSetting('enemys', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.core.enemys.enemys[id]); + Object.keys(editor.file.comment._data.enemys._data).forEach(function (v) { + if (!isset((editor.core.enemys.enemys[id]||{})[v])) + /* locObj[v]=editor.core.enemys.enemys[id][v]; + else */ + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.enemys, + null]); + } + } + //callback([obj,commentObj,err:String]) + + editor.file.editMapBlocksInfo = function (idnum, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,wizard]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + var tempmap=[]; + for(var ii=0;ii=editor.core.icons.tilesetStartOffset && !isset(editor.core.maps.blocksInfo[idnum]) && tempmap.indexOf(idnum)===-1){ + actionList.splice(ii,0,["add","['" + idnum + "']",{"cls": "tileset", "id": "X"+idnum}]); + tempmap.push(idnum); + ii++; + } + value[1] = "['" + idnum + "']" + value[1]; + } + saveSetting('maps', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var sourceobj=editor.core.maps.blocksInfo[idnum]; + if(!isset(sourceobj) && idnum>=editor.core.icons.tilesetStartOffset)sourceobj={"cls": "tileset", "id": "X"+idnum} + var locObj = Object.assign({}, sourceobj); + Object.keys(editor.file.comment._data.maps._data).forEach(function (v) { + if (!isset(sourceobj[v])) + locObj[v] = null; + }); + locObj.idnum = idnum; + return locObj; + })(), + editor.file.comment._data.maps, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editLoc = function (x, y, actionList, callback) { + /*actionList:[ + ["change","['events']",["\t[老人,wizard]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。"]], + ["change","['afterBattle']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + if(/\['autoEvent'\]\['\d+'\]$/.test(value[1]))value[1]=value[1].replace(/\['\d+'\]$/,function(v){return "['" + x + "," + y + "']"+v}) + else value[1] = value[1] + "['" + x + "," + y + "']"; + }); + saveSetting('floorloc', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = {}; + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + if (isset(editor.currentFloorData[v][x + ',' + y])) + locObj[v] = editor.currentFloorData[v][x + ',' + y]; + else + locObj[v] = null; + }); + return locObj; + })(), + editor.file.comment._data.floors._data.loc, + null]); + } + + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editFloor = function (actionList, callback) { + /*actionList:[ + ["change","['title']",'样板 3 层'], + ["change","['color']",null], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('floors', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = Object.assign({}, editor.currentFloorData); + Object.keys(editor.file.comment._data.floors._data.floor._data).forEach(function (v) { + if (!isset(editor.currentFloorData[v])) + /* locObj[v]=editor.currentFloorData[v]; + else */ + locObj[v] = null; + }); + Object.keys(editor.file.comment._data.floors._data.loc._data).forEach(function (v) { + delete(locObj[v]); + }); + editor.dom.maps.forEach(function (one) { delete locObj[one]; }); + return locObj; + })(), + editor.file.comment._data.floors._data.floor, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editTower = function (actionList, callback) { + /*actionList:[ + ["change","['firstData']['version']",'Ver 1.0.1 (Beta)'], + ["change","['values']['lavaDamage']",200], + ] + 为[]时只查询不修改 + */ + var data_obj = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('data', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + //var locObj=Object.assign({'main':{}},editor.core.data); + var locObj = Object.assign({}, data_obj, {'main': {}}); + Object.keys(editor.file.dataComment._data.main._data).forEach(function (v) { + if (isset(editor.main[v])) + locObj.main[v] = data_obj.main[v]; + else + locObj.main[v] = null; + }); + return locObj; + })(), + editor.file.dataComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var fmap = {}; + var fjson = JSON.stringify(functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + fmap[id_] = v.toString(); + return id_; + } else return v + }, 4); + var fobj = JSON.parse(fjson); + editor.file.functionsMap = fmap; + editor.file.functionsJSON = fjson; + var buildlocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildlocobj(locObj[key]); + else locObj[key] = fmap[locObj[key]]; + } + }; + + editor.file.editFunctions = function (actionList, callback) { + /*actionList:[ + ["change","['events']['afterChangeLight']","function(x,y){console.log(x,y)}"], + ["change","['ui']['drawAbout']","function(){...}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('functions', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(fjson); + buildlocobj(locObj); + return locObj; + })(), + editor.file.functionsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + editor.file.editCommonEvent = function (actionList, callback) { + /*actionList:[ + ["change","['test']",['123']], + ] + 为[]时只查询不修改 + */ + var data_obj = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + actionList.forEach(function (value) { + value[1] = "['commonEvent']" + value[1]; + }); + saveSetting('events', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + Object.assign({},data_obj), + editor.file.eventsComment._data.commonEvent, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var plmap = {}; + var pljson = JSON.stringify(plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + plmap[id_] = v.toString(); + return id_; + } else if(v===null){ + var id_ = editor.util.guid(); + plmap[id_] = null; + return id_; + } return v + }, 4); + var plobj = JSON.parse(pljson); + editor.file.pluginsMap = plmap; + editor.file.pluginsObj = plobj; + var buildpllocobj = function (locObj) { + for (var key in locObj) { + if (typeof(locObj[key]) !== typeof('')) buildpllocobj(locObj[key]); + else locObj[key] = plmap[locObj[key]]; + } + }; + + editor.file.editPlugins = function (actionList, callback) { + /*actionList:[ + ["change","['test']","function(x,y){console.log(x,y)}"], + ] + 为[]时只查询不修改 + */ + checkCallback(callback); + if (isset(actionList) && actionList.length > 0) { + saveSetting('plugins', actionList, function (err) { + callback([err]); + }); + } else { + callback([ + (function () { + var locObj = JSON.parse(JSON.stringify(plobj)); + buildpllocobj(locObj); + return locObj; + })(), + editor.file.pluginsComment, + null]); + } + } + //callback([obj,commentObj,err:String]) + + //////////////////////////////////////////////////////////////////// + + var isset = function (val) { + if (val == undefined || val == null) { + return false; + } + return true + } + + var checkCallback=function(callback){ + if (!isset(callback)) { + printe('未设置callback'); + throw('未设置callback') + } + } + + var encode = editor.util.encode64; + + editor.file.save_icons_maps_items_enemys=function(callback){ + var check=[] + saveSetting('icons',[],function(err){ + if(err){callback(err);return;} + check.push('icons') + if(check.length==4)callback(null); + }) + saveSetting('maps',[],function(err){ + if(err){callback(err);return;} + check.push('maps') + if(check.length==4)callback(null); + }) + saveSetting('items',[],function(err){ + if(err){callback(err);return;} + check.push('items') + if(check.length==4)callback(null); + }) + saveSetting('enemys',[],function(err){ + if(err){callback(err);return;} + check.push('enemys') + if(check.length==4)callback(null); + }) + } + + var saveSetting = function (file, actionList, callback) { + var _update = function (name, value) { + if (value[2] === undefined) { + eval("delete " + name + value[1]); + } else { + eval(name + value[1] + "=" + JSON.stringify(value[2])); + } + } + + //console.log(file); + //console.log(actionList); + editor.file.alertWhenCompress(); + + if (file == 'icons') { + actionList.forEach(function (value) { + _update("icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1", value); + }); + var datastr = 'var icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1 = \n'; + datastr += JSON.stringify(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1, null, '\t'); + fs.writeFile('project/icons.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'maps') { + actionList.forEach(function (value) { + _update("maps_90f36752_8815_4be8_b32b_d7fad1d0542e", value); + }); + var datastr = 'var maps_90f36752_8815_4be8_b32b_d7fad1d0542e = \n'; + //datastr+=JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e,null,4); + + var emap = {}; + var estr = JSON.stringify(maps_90f36752_8815_4be8_b32b_d7fad1d0542e, function (k, v) { + if (v.id != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + + fs.writeFile('project/maps.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'items') { + actionList.forEach(function (value) { + _update("items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a", value); + }); + var datastr = 'var items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a = \n'; + var items = core.clone(items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a); + for (var id in items) delete items[id].id; + datastr += JSON.stringify(items, null, '\t'); + fs.writeFile('project/items.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'enemys') { + actionList.forEach(function (value) { + _update("enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80", value); + }); + var datastr = 'var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 = \n'; + var emap = {}; + var enemys = core.clone(enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80); + for (var id in enemys) delete enemys[id].id; + var estr = JSON.stringify(enemys, function (k, v) { + if (v && v.hp != null) { + var id_ = editor.util.guid(); + emap[id_] = JSON.stringify(v); + return id_; + } else return v + }, '\t'); + for (var id_ in emap) { + estr = estr.replace('"' + id_ + '"', emap[id_]) + } + datastr += estr; + fs.writeFile('project/enemys.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'data') { + actionList.forEach(function (value) { + _update("data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d", value); + }); + if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId) < 0) + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.floorId = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds[0]; + var datastr = 'var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = \n'; + datastr += JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, null, '\t'); + fs.writeFile('project/data.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'functions') { + actionList.forEach(function (value) { + eval("fmap[fobj" + value[1] + ']=' + JSON.stringify(value[2])); + }); + var fraw = fjson; + for (var id_ in fmap) { + fraw = fraw.replace('"' + id_ + '"', fmap[id_]) + } + var datastr = 'var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = \n'; + datastr += fraw; + fs.writeFile('project/functions.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'floorloc') { + actionList.forEach(function (value) { + // 检测null/undefined + if(/\['autoEvent'\]\['\d+,\d+'\]\['\d+'\]$/.test(value[1])){ + var tempvalue=value[1].replace(/\['\d+'\]$/,'') + tempvalue="editor.currentFloorData" +tempvalue + tempvalue=tempvalue+'='+tempvalue+'||{}' + eval(tempvalue) + } + if (value[2]==null && value[0]!=='add') + eval("delete editor.currentFloorData" + value[1]); + else + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'floors') { + actionList.forEach(function (value) { + eval("editor.currentFloorData" + value[1] + '=' + JSON.stringify(value[2])); + }); + editor.file.saveFloorFile(callback); + return; + } + if (file == 'events') { + actionList.forEach(function (value) { + _update("events_c12a15a8_c380_4b28_8144_256cba95f760", value); + }); + var datastr = 'var events_c12a15a8_c380_4b28_8144_256cba95f760 = \n'; + datastr += JSON.stringify(events_c12a15a8_c380_4b28_8144_256cba95f760, null, '\t'); + fs.writeFile('project/events.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + if (file == 'plugins') { + actionList.forEach(function (value) { + if(value[0]==='add'){ + eval("plobj" + value[1] + '=' + JSON.stringify(value[2])); + } else { + eval("plmap[plobj" + value[1] + ']=' + JSON.stringify(value[2])); + } + }); + var plraw = JSON.stringify(plobj,null,4); + for (var id_ in plmap) { + plraw = plraw.replace('"' + id_ + '"', plmap[id_]) + } + var datastr = 'var plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1 = \n'; + datastr += plraw; + fs.writeFile('project/plugins.js', encode(datastr), 'base64', function (err, data) { + callback(err); + }); + return; + } + callback('出错了,要设置的文件名不识别'); + } + + editor.file.saveSetting = saveSetting; + + return editor_file; +} +//editor_file = editor_file(editor); \ No newline at end of file diff --git a/_server/editor_game.js b/_server/editor_game.js new file mode 100644 index 0000000..a8b3f65 --- /dev/null +++ b/_server/editor_game.js @@ -0,0 +1,199 @@ +editor_game_wrapper = function (editor, main, core) { + // 原则上重构后只有此文件允许`\s(main|core)`形式的调用, 以及其初始化 editor_game_wrapper(editor, main, core) + + editor_game = function () { + this.replacerRecord = {} + } + + + //////////////////// 修改数据相关 //////////////////// + // 三个 replacer 和 replacerRecord 暂时放在此处 + + editor_game.prototype.replacerForLoading = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (value instanceof Function) { + var guid_ = editor.util.guid() + rmap[guid_] = value.toString() + return guid_ + } else if (value === null) { + // 为了包含plugins的新建 + var guid_ = editor.util.guid() + rmap[guid_] = null + return guid_ + } + return value + } + + editor_game.prototype.replacerForSaving = function (_key, value) { + var rmap = editor.game.replacerRecord; + if (rmap.hasOwnProperty(value)) { + return rmap[value] + } + return value + } + + editor_game.prototype.getValue = function (field) { + var rmap = editor.game.replacerRecord; + var value = eval(field) + if (rmap.hasOwnProperty(oldval)) { + return rmap[value] + } else { + return value + } + } + + editor_game.prototype.setValue = function (field, value) { + var rmap = editor.game.replacerRecord; + var oldval = eval(field) + if (rmap.hasOwnProperty(oldval)) { + rmap[value] = eval(value) + } else { + eval(field + '=' + value) + } + } + + editor_game.prototype.replacerWithoutRecord = function (_key, value) { + if (value instanceof Function) { + return value.toString() + } else return value + } + + editor_game.prototype.fixFunctionInGameData = function () { + var rf = editor.game.replacerWithoutRecord + core.floors = JSON.parse(JSON.stringify(core.floors, rf)); + core.data = JSON.parse(JSON.stringify(core.data, rf)); + data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d = JSON.parse(JSON.stringify(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d, rf)); + } + + //////////////////// 加载游戏数据相关 //////////////////// + + // 初始化数字与地图图块的对应 + editor_game.prototype.idsInit = function (maps, icons) { + editor.ids = [0]; + editor.indexs = []; + var MAX_NUM = 0; + var keys = Object.keys(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); + for (var ii = 0; ii < keys.length; ii++) { + var v = ~~keys[ii]; + if (v > MAX_NUM && v < core.icons.tilesetStartOffset) MAX_NUM = v; + } + editor.MAX_NUM = MAX_NUM; + var getInfoById = function (id) { + var block = maps.initBlock(0, 0, id); + if (Object.prototype.hasOwnProperty.call(block, 'event')) { + return block; + } + } + var point = 0; + for (var i = 0; i <= MAX_NUM; i++) { + var indexBlock = getInfoById(i); + editor.indexs[i] = []; + if (indexBlock) { + var id = indexBlock.event.id; + var indexId = indexBlock.id; + var allCls = Object.keys(icons); + if (i == 17) { + editor.ids.push({ 'idnum': 17, 'id': id, 'images': 'terrains' }); + point++; + editor.indexs[i].push(point); + continue; + } + for (var j = 0; j < allCls.length; j++) { + if (id in icons[allCls[j]]) { + editor.ids.push({ 'idnum': indexId, 'id': id, 'images': allCls[j], 'y': icons[allCls[j]][id] }); + point++; + editor.indexs[i].push(point); + } + } + } + } + editor.indexs[0] = [0]; + + var startOffset = core.icons.tilesetStartOffset; + for (var i in core.tilesets) { + var imgName = core.tilesets[i]; + var img = core.material.images.tilesets[imgName]; + var width = Math.floor(img.width / 32), height = Math.floor(img.height / 32); + if (img.width % 32 || img.height % 32) { + // alert(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); + console.warn(imgName + '的长或宽不是32的整数倍, 请修改后刷新页面'); + } + if (img.width * img.height > 32 * 32 * 3000) { + // alert(imgName + '上的图块数量超过了3000,请修改后刷新页面'); + console.warn(imgName + '上的图块数量超过了3000,请修改后刷新页面'); + } + for (var id = startOffset; id < startOffset + width * height; id++) { + var x = (id - startOffset) % width, y = parseInt((id - startOffset) / width); + var indexBlock = getInfoById(id); + editor.ids.push({ 'idnum': id, 'id': indexBlock.event.id, 'images': imgName, "x": x, "y": y, isTile: true }); + point++; + editor.indexs[id] = [point]; + } + startOffset += core.icons.tilesetStartOffset; + } + } + + // 获取当前地图 + editor_game.prototype.fetchMapFromCore = function () { + var mapArray = core.getMapArray(core.status.floorId); + editor.map = mapArray.map(function (v) { + return v.map(function (v) { + var x = parseInt(v), y = editor.indexs[x]; + if (y == null) { + printe("素材数字" + x + "未定义。是不是忘了注册,或者接档时没有覆盖icons.js和maps.js?"); + y = [0]; + } + return editor.ids[y[0]] + }) + }); + editor.currentFloorId = core.status.floorId; + editor.currentFloorData = core.floors[core.status.floorId]; + // 补出缺省的数据 + editor.currentFloorData.autoEvent = editor.currentFloorData.autoEvent || {}; + // + for (var ii = 0, name; name = editor.dom.canvas[ii]; ii++) { + name += 'map'; + var mapArray = editor.currentFloorData[name]; + if (!mapArray || JSON.stringify(mapArray) == JSON.stringify([])) {//未设置或空数组 + //与editor.map同形的全0 + mapArray = eval('[' + Array(editor.map.length + 1).join('[' + Array(editor.map[0].length + 1).join('0,') + '],') + ']'); + } + mapArray = core.maps._processInvalidMap(mapArray, editor.map[0].length, editor.map.length); + editor[name] = mapArray.map(function (v) { + return v.map(function (v) { + var x = parseInt(v), y = editor.indexs[x]; + if (y == null) { + printe("素材数字" + x + "未定义。是不是忘了注册,或者接档时没有覆盖icons.js和maps.js?"); + y = [0]; + } + return editor.ids[y[0]] + }) + }); + } + } + + // 获取地图列表 + editor_game.prototype.getFloorFileList = function (callback) { + // callback([Array,err:String]) + editor.util.checkCallback(callback); + /* editor.fs.readdir('project/floors',function(err, data){ + callback([data,err]); + }); */ + callback([editor.core.floorIds, null]); + } + + editor_game.prototype.doCoreFunc = function (funcname) { + return core[funcname].apply(core, Array.prototype.slice.call(arguments, 1)); + } + + editor_game.prototype.getEnemy = function (id) { + return core.material.enemys[id]; + } + + editor_game.prototype.getFirstData = function () { + return core.firstData; + } + + editor.constructor.prototype.game = new editor_game(); +} +//editor_game_wrapper(editor); \ No newline at end of file diff --git a/_server/editor_listen.js b/_server/editor_listen.js new file mode 100644 index 0000000..0be858c --- /dev/null +++ b/_server/editor_listen.js @@ -0,0 +1,193 @@ +editor_listen_wrapper = function (editor) { + + editor.constructor.prototype.listen = function () { + + editor.dom.body.onmousedown = editor.uifunctions.body_click; + + editor.dom.eui.oncontextmenu = function (e) { e.preventDefault() } // 自定义了右键菜单, 阻止默认行为 + editor.dom.midMenu.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.eui.ondblclick = editor.uifunctions.map_doubleClick + + editor.dom.eui.onmousedown = editor.uifunctions.map_ondown + editor.dom.eui.onmousemove = editor.uifunctions.map_onmove + editor.dom.eui.onmouseup = editor.uifunctions.map_onup + editor.dom.eui.onmouseout = editor.uifunctions.map_onmoveout + + editor.dom.mid.onmousewheel = editor.uifunctions.map_mousewheel + + editor.uivalues.shortcut = editor.config.get('shortcut', { 48: 0, 49: 0, 50: 0, 51: 0, 52: 0, 53: 0, 54: 0, 55: 0, 56: 0, 57: 0 }); + editor.dom.body.onkeydown = editor.uifunctions.body_shortcut + + editor.uivalues.scrollBarHeight = editor.uifunctions.getScrollBarHeight(); + editor.dom.iconExpandBtn.style.display = 'block'; + editor.dom.iconExpandBtn.innerText = editor.uivalues.folded ? "展开素材区" : "折叠素材区"; + editor.dom.iconExpandBtn.onclick = editor.uifunctions.fold_material_click + + editor.dom.iconLib.onmousedown = editor.uifunctions.material_ondown + editor.dom.iconLib.onmousemove = editor.uifunctions.material_onmove + editor.dom.iconLib.onmouseup = editor.uifunctions.material_onup + editor.dom.iconLib.oncontextmenu = function (e) { e.preventDefault() } + + editor.dom.extraEvent.onmouseup = editor.uifunctions.extraEvent_click + editor.dom.chooseThis.onmouseup = editor.uifunctions.chooseThis_click + editor.dom.chooseInRight.onmouseup = editor.uifunctions.chooseInRight_click + editor.dom.copyLoc.onmouseup = editor.uifunctions.copyLoc_click + editor.dom.pasteLoc.onmouseup = editor.uifunctions.pasteLoc_click + editor.dom.clearEvent.onmouseup = editor.uifunctions.clearEvent_click + editor.dom.clearLoc.onmouseup = editor.uifunctions.clearLoc_click + editor.dom.undoFloor.onclick = editor.uifunctions.undoFloor_click + editor.dom.selectFloorBtn.onclick = editor.uifunctions.selectFloorBtn_click + editor.dom.editorTheme.onchange = editor.uifunctions.editorTheme_onchange + + editor.dom.lastUsed.onmouseup = editor.uifunctions.lastUsed_click; + editor.dom.lastUsed.oncontextmenu = function (e) { e.preventDefault(); } + editor.dom.clearLastUsedBtn.onclick = editor.uifunctions.clearLastUsedBtn_click; + editor.dom.showMovable.onchange = editor.uifunctions.showMovable_onchange; + + editor.dom.brushMod.onchange = editor.uifunctions.brushMod_onchange + if (editor.dom.brushMod2) editor.dom.brushMod2.onchange = editor.uifunctions.brushMod2_onchange; + if (editor.dom.brushMod3) editor.dom.brushMod3.onchange = editor.uifunctions.brushMod3_onchange; + if (editor.dom.brushMod4) editor.dom.brushMod4.onchange = editor.uifunctions.brushMod4_onchange; + + editor.dom.layerMod.onchange = editor.uifunctions.layerMod_onchange + if (editor.dom.layerMod2) editor.dom.layerMod2.onchange = editor.uifunctions.layerMod2_onchange; + if (editor.dom.layerMod3) editor.dom.layerMod3.onchange = editor.uifunctions.layerMod3_onchange; + + editor.uifunctions.viewportButtons_func() + + window.onbeforeunload = function () { + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return '你尚未保存地图,确定退出么?'; + } + return null; + } + } + + editor.constructor.prototype.mobile_listen = function () { + if (!editor.isMobile) return; + + var mobileview = document.getElementById('mobileview'); + var mid = document.getElementById('mid'); + var right = document.getElementById('right'); + // var mobileeditdata = document.getElementById('mobileeditdata'); + + + editor.showdataarea = function (callShowMode) { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = ''; + if (callShowMode) editor.mode.showMode(editor.dom.editModeSelect.value); + editor.uifunctions.hideMidMenu(); + } + mobileview.children[0].onclick = function () { + editor.showdataarea(true) + } + mobileview.children[1].onclick = function () { + mid.style = 'z-index:110'; + right.style = 'z-index:-1;opacity: 0;'; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + mobileview.children[3].onclick = function () { + mid.style = 'z-index:-1;opacity: 0;'; + right.style = 'z-index:110'; + // mobileeditdata.style = 'z-index:-1;opacity: 0;'; + editor.lastClickId = ''; + } + + /* + var gettrbyid = function () { + if (!editor.lastClickId) return false; + thisTr = document.getElementById(editor.lastClickId); + input = thisTr.children[2].children[0].children[0]; + field = thisTr.children[0].getAttribute('title'); + cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + return [thisTr, input, field, cobj]; + } + mobileeditdata.children[0].onclick = function () { + var info = gettrbyid() + if (!info) return; + info[1].ondblclick() + } + mobileeditdata.children[1].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[2]) + } + mobileeditdata.children[2].onclick = function () { + var info = gettrbyid() + if (!info) return; + printf(info[0].children[1].getAttribute('title')) + } + */ + + //===== + + document.body.ontouchstart = document.body.onmousedown; + document.body.onmousedown = null; + + editor.dom.eui.ontouchstart = editor.dom.eui.onmousedown + editor.dom.eui.onmousedown = null + editor.dom.eui.ontouchmove = editor.dom.eui.onmousemove + editor.dom.eui.onmousemove = null + editor.dom.eui.ontouchend = editor.dom.eui.onmouseup + editor.dom.eui.onmouseup = null + + + editor.dom.chooseThis.ontouchstart = editor.dom.chooseThis.onmousedown + editor.dom.chooseThis.onmousedown = null + editor.dom.chooseInRight.ontouchstart = editor.dom.chooseInRight.onmousedown + editor.dom.chooseInRight.onmousedown = null + editor.dom.copyLoc.ontouchstart = editor.dom.copyLoc.onmousedown + editor.dom.copyLoc.onmousedown = null + editor.dom.pasteLoc.ontouchstart = editor.dom.pasteLoc.onmousedown + editor.dom.pasteLoc.onmousedown = null + editor.dom.clearLoc.ontouchstart = editor.dom.clearLoc.onmousedown + editor.dom.clearLoc.onmousedown = null + + // 不使用以下6语句, 会使得素材区手机无法拖动, 手机的框选素材只能放弃, 要通过弹框实现框选 + // editor.dom.iconLib.ontouchstart = editor.dom.iconLib.onmousedown + // editor.dom.iconLib.onmousedown = null + // editor.dom.iconLib.ontouchmove = editor.dom.iconLib.onmousemove + // editor.dom.iconLib.onmousemove = null + // editor.dom.iconLib.ontouchend = editor.dom.iconLib.onmouseup + // editor.dom.iconLib.onmouseup = null + } + + editor.constructor.prototype.mode_listen = function (callback) { + + // 这里的函数还没有写jsdoc, 通过_func()的方式先完成分类 + + editor.uifunctions.newIdIdnum_func() + editor.uifunctions.changeId_func() + editor.uifunctions.copyPasteEnemyItem_func(); + + editor.uifunctions.selectFloor_func() + editor.uifunctions.saveFloor_func() + editor.uifunctions.openDoc_func(); + + editor.uifunctions.newMap_func() + + editor.uifunctions.createNewMaps_func() + + editor.uifunctions.changeFloorId_func() + editor.uifunctions.changeFloorSize_func() + + // editor.uifunctions.fixCtx_func() + editor.uifunctions.appendPic_func(); + /* + editor.uifunctions.selectAppend_func() + editor.uifunctions.selectFileBtn_func() + editor.uifunctions.changeColorInput_func() + editor.uifunctions.picClick_func() + editor.uifunctions.appendConfirm_func() + */ + + editor.dom.editModeSelect.onchange = editor.mode.editModeSelect_onchange + + if (Boolean(callback)) callback(); + } + +} \ No newline at end of file diff --git a/_server/editor_mappanel.js b/_server/editor_mappanel.js new file mode 100644 index 0000000..a65a760 --- /dev/null +++ b/_server/editor_mappanel.js @@ -0,0 +1,1190 @@ +editor_mappanel_wrapper = function (editor) { + + // 暂时先 注释+分类 内部函数未完成重构 + + /** + * 在绘图区格子内画一个随机色块 + */ + editor.uifunctions.fillPos = function (pos) { + editor.dom.euiCtx.fillStyle = '#' + ~~(Math.random() * 8) + ~~(Math.random() * 8) + ~~(Math.random() * 8); + var grid = _getGridByPos({ x: pos.x, y: pos.y }); + editor.dom.euiCtx.fillRect(grid.x + grid.size * 3 / 8, grid.y + grid.size * 3 / 8, grid.size / 4, grid.size / 4); + } + + /** + * 从鼠标点击返回可用的组件内坐标 + */ + editor.uifunctions.eToLoc = function (e) { + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var xx = e.clientX, yy = e.clientY + if (editor.isMobile) { xx = e.touches[0].clientX, yy = e.touches[0].clientY } + editor.loc = { + 'x': scrollLeft + xx - editor.dom.mid.offsetLeft - editor.dom.mapEdit.offsetLeft, + 'y': scrollTop + yy - editor.dom.mid.offsetTop - editor.dom.mapEdit.offsetTop, + 'size': editor.isMobile ? (32 * innerWidth * 0.96 / core.__PIXELS__) : 32 + }; + return editor.loc; + } + + /** + * 组件内坐标转地图位置 + * @param {Boolean} addViewportOffset 是否加上大地图的偏置 + */ + editor.uifunctions.locToPos = function (loc, addViewportOffset) { + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo; + var size = loc.size / 32 * info.size; + return { + x: core.clamp(Math.floor((loc.x - info.left) / size), 0, editor.currentFloorData.width - 1), + y: core.clamp(Math.floor((loc.y - info.top) / size), 0, editor.currentFloorData.height - 1), + } + } + + var offsetX = 0, offsetY = 0; + if (addViewportOffset) { + offsetX = core.bigmap.offsetX / 32; + offsetY = core.bigmap.offsetY / 32; + } + return { 'x': ~~(loc.x / loc.size) + offsetX, 'y': ~~(loc.y / loc.size) + offsetY } + } + + /** + * editor.dom.eui.ondblclick + * 双击地图可以选中素材 + */ + editor.uifunctions.map_doubleClick = function (e) { + if (editor.uivalues.bindSpecialDoor.loc != null) return; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + editor.setSelectBoxFromInfo(editor[editor.layerMod][pos.y][pos.x], true); + return; + } + + /** + * 用于鼠标移出map后清除状态 + */ + editor.uifunctions.clearMapStepStatus = function () { + if (editor.uivalues.mouseOutCheck > 1) { + editor.uivalues.mouseOutCheck--; + setTimeout(editor.uifunctions.clearMapStepStatus, 1000); + return; + } + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + } + + /** + * editor.dom.eui.onmousedown + * + 绑定机关门事件的选择怪物 + * + 右键进入菜单 + * + 非绘图时选中 + * + 绘图时画个矩形在那个位置 + */ + editor.uifunctions.map_ondown = function (e) { + editor.uivalues.selectedArea = null; + editor.uivalues.lastMoveE = e; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + editor.pos = pos; + + if (editor.uivalues.bindSpecialDoor.loc != null) { + var x = pos.x, y = pos.y, id = (editor.map[y][x] || {}).id; + // 检测是否是怪物 + if (id && editor.game.getEnemy(id)) { + var locstr = x + "," + y, index = editor.uivalues.bindSpecialDoor.enemys.indexOf(locstr); + if (index >= 0) editor.uivalues.bindSpecialDoor.enemys.splice(index, 1); + else editor.uivalues.bindSpecialDoor.enemys.push(locstr); + editor.drawEventBlock(); + editor.uifunctions._extraEvent_bindSpecialDoor_doAction(); + } + return false; + } + // if (e.buttons == 2) { // 挪到onup + // editor.uifunctions.showMidMenu(e.clientX, e.clientY); + // return false; + // } + if (!selectBox.isSelected()) { + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + editor.uifunctions.showTips(6); + editor.uivalues.startPos = pos; + editor.dom.euiCtx.strokeStyle = '#FF0000'; + editor.dom.euiCtx.lineWidth = 2; + if (editor.isMobile) editor.uifunctions.showMidMenu(e.clientX, e.clientY); + return false; + } + + editor.uivalues.holdingPath = 1; + editor.uivalues.mouseOutCheck = 2; + setTimeout(editor.uifunctions.clearMapStepStatus); + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.stepPostfix = []; + editor.uivalues.stepPostfix.push(pos); + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + return false; + } + + var _getGridByPos = function (pos) { + if (editor.uivalues.bigmap) { + var info = editor.uivalues.bigmapInfo; + return { x: info.left + info.size * pos.x, y: info.top + info.size * pos.y, size: info.size }; + } else { + return { x: 32 * pos.x - core.bigmap.offsetX, y: 32 * pos.y - core.bigmap.offsetY, size: 32 }; + } + } + + var _setMarksHightlight = function (marks, index) { + for (var i = 0; i < marks.length; ++i) { + if (marks[i].classList.contains('highlight')) { + if (i == index) index = null; + else marks[i].classList.remove('highlight'); + } + } + if (index != null) { + marks[index].classList.add('highlight'); + } + } + + /** + * editor.dom.eui.onmousemove + * + 非绘图模式时维护起止位置并画箭头 + * + 绘图模式时找到与队列尾相邻的鼠标方向的点画个矩形 + */ + editor.uifunctions.map_onmove = function (e) { + editor.uivalues.lastMoveE = e; + if (!editor.uivalues.bigmap && !editor.isMobile && editor.dom.midMenu.style.display == 'none') { + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc); + _setMarksHightlight(Array.from(editor.dom.mapColMark.children[0].rows[0].cells), pos.x); + _setMarksHightlight(Array.from(editor.dom.mapRowMark.children[0].rows).map(function (tr) { return tr.cells[0]; }), pos.y); + } + + if (!selectBox.isSelected()) { + if (editor.uivalues.startPos == null) return; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + if (editor.uivalues.endPos != null && editor.uivalues.endPos.x == pos.x && editor.uivalues.endPos.y == pos.y) return; + var startGrid = _getGridByPos(editor.uivalues.startPos), endGrid; + if (editor.uivalues.endPos != null) { + endGrid = _getGridByPos(editor.uivalues.endPos); + editor.dom.euiCtx.clearRect(Math.min(startGrid.x, endGrid.x), Math.min(startGrid.y, endGrid.y), + (Math.abs(editor.uivalues.startPos.x - editor.uivalues.endPos.x) + 1) * startGrid.size, + (Math.abs(editor.uivalues.startPos.y - editor.uivalues.endPos.y) + 1) * startGrid.size); + } + editor.uivalues.endPos = pos; + endGrid = _getGridByPos(editor.uivalues.endPos); + if (editor.uivalues.startPos != null) { + if (editor.uivalues.startPos.x != editor.uivalues.endPos.x || editor.uivalues.startPos.y != editor.uivalues.endPos.y) { + if (e.buttons == 2) { + // 右键拖拽: 画选的区域 + var x0 = editor.uivalues.startPos.x; + var y0 = editor.uivalues.startPos.y; + var x1 = editor.uivalues.endPos.x; + var y1 = editor.uivalues.endPos.y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + // draw rect + editor.dom.euiCtx.clearRect(0, 0, editor.dom.euiCtx.canvas.width, editor.dom.euiCtx.canvas.height); + editor.dom.euiCtx.fillStyle = 'rgba(0, 127, 255, 0.4)'; + var grid = _getGridByPos({ x: x0, y: y0 }); + editor.dom.euiCtx.fillRect(grid.x, grid.y, grid.size * (x1 - x0 + 1), grid.size * (y1 - y0 + 1)); + } else { + // 左键拖拽: 画箭头 + core.drawArrow('eui', startGrid.x + startGrid.size / 2, startGrid.y + startGrid.size / 2, endGrid.x + endGrid.size / 2, endGrid.y + endGrid.size / 2); + } + } + } + // editor_mode.onmode('nextChange'); + // editor_mode.onmode('loc'); + //editor_mode.loc(); + return false; + } + + if (editor.uivalues.holdingPath == 0) { + return false; + } + editor.uivalues.mouseOutCheck = 2; + var loc = editor.uifunctions.eToLoc(e); + var pos = editor.uifunctions.locToPos(loc, true); + var pos0 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1] + var directionDistance = [pos.y - pos0.y, pos0.x - pos.x, pos0.y - pos.y, pos.x - pos0.x] + var max = 0, index = 4; + for (var i = 0; i < 4; i++) { + if (directionDistance[i] > max) { + index = i; + max = directionDistance[i]; + } + } + var pos = [{ 'x': 0, 'y': 1 }, { 'x': -1, 'y': 0 }, { 'x': 0, 'y': -1 }, { 'x': 1, 'y': 0 }, false][index] + if (pos) { + pos.x += pos0.x; + pos.y += pos0.y; + if (editor.brushMod == 'line') editor.uifunctions.fillPos(pos); + else { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = pos.x; + var y1 = pos.y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + // draw rect + editor.dom.euiCtx.clearRect(0, 0, editor.dom.euiCtx.canvas.width, editor.dom.euiCtx.canvas.height); + editor.dom.euiCtx.fillStyle = 'rgba(0, 127, 255, 0.4)'; + var grid = _getGridByPos({ x: x0, y: y0 }); + editor.dom.euiCtx.fillRect(grid.x, grid.y, + grid.size * (x1 - x0 + 1), grid.size * (y1 - y0 + 1)); + } + editor.uivalues.stepPostfix.push(pos); + } + return false; + } + + editor.uifunctions.map_onmoveout = function () { + if (!editor.uivalues.bigmap && !editor.isMobile) { + _setMarksHightlight(Array.from(editor.dom.mapColMark.children[0].rows[0].cells)); + _setMarksHightlight(Array.from(editor.dom.mapRowMark.children[0].rows).map(function (tr) { return tr.cells[0]; })); + } + } + + /** + * editor.dom.eui.onmouseup + * + 非绘图模式时, 交换首末点的内容 + * + 绘图模式时, 根据画线/画矩形/画tileset 做对应的绘制 + */ + editor.uifunctions.map_onup = function (ee) { + editor.uivalues.selectedArea = null; + ee.preventDefault(); + ee.stopPropagation(); + var e = editor.uivalues.lastMoveE; + if (e.buttons == 2 && (editor.uivalues.endPos == null || (editor.uivalues.startPos.x == editor.uivalues.endPos.x && editor.uivalues.startPos.y == editor.uivalues.endPos.y))) { + editor.uifunctions.showMidMenu(e.clientX, e.clientY); + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + return false; + } + if (!selectBox.isSelected()) { + if (e.buttons == 2) { + // 右键拖拽: 选中区域 + printf('已经选中该区域') + editor.uivalues.selectedArea = Object.assign({}, editor.uivalues.startPos, { x1: editor.uivalues.endPos.x, y1: editor.uivalues.endPos.y }); + // 后续的处理 + } else { + // 左键拖拽: 交换 + editor.savePreMap(); + // editor.movePos(editor.uivalues.startPos, editor.uivalues.endPos); + editor.exchangePos(editor.uivalues.startPos, editor.uivalues.endPos); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + } + editor.uivalues.startPos = editor.uivalues.endPos = null; + return false; + } + + editor.uivalues.holdingPath = 0; + if (editor.uivalues.stepPostfix && editor.uivalues.stepPostfix.length) { + editor.savePreMap(); + if (editor.brushMod !== 'line') { + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var x1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].x; + var y1 = editor.uivalues.stepPostfix[editor.uivalues.stepPostfix.length - 1].y; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj <= y1; jj++) { + for (var ii = x0; ii <= x1; ii++) { + editor.uivalues.stepPostfix.push({ x: ii, y: jj }) + } + } + } + var useBrushMode = editor.brushMod == 'tileset'; + if (editor.uivalues.stepPostfix.length == 1 && (editor.uivalues.tileSize[0] > 1 || editor.uivalues.tileSize[1] > 1)) { + useBrushMode = true; + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + editor.uivalues.stepPostfix = []; + for (var jj = y0; jj < y0 + editor.uivalues.tileSize[1]; ++jj) { + for (var ii = x0; ii < x0 + editor.uivalues.tileSize[0]; ++ii) { + if (jj >= editor[editor.layerMod].length || ii >= editor[editor.layerMod][0].length) continue; + editor.uivalues.stepPostfix.push({ x: ii, y: jj }); + } + } + } + if (useBrushMode && core.tilesets.indexOf(editor.info.images) !== -1) { + var imgWidth = ~~(core.material.images.tilesets[editor.info.images].width / 32); + var x0 = editor.uivalues.stepPostfix[0].x; + var y0 = editor.uivalues.stepPostfix[0].y; + var idnum = editor.info.idnum; + var pmod = function (a, b) { return (a % b + b) % b; } + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) { + var dx = pmod(editor.uivalues.stepPostfix[ii].x - x0, editor.uivalues.tileSize[0]); + var dy = pmod(editor.uivalues.stepPostfix[ii].y - y0, editor.uivalues.tileSize[1]); + editor[editor.layerMod][editor.uivalues.stepPostfix[ii].y][editor.uivalues.stepPostfix[ii].x] = editor.ids[editor.indexs[idnum + dx + dy * imgWidth]]; + } + } else { + // 检测是否是填充模式 + if (editor.uivalues.stepPostfix.length == 1 && editor.brushMod == 'fill') { + editor.uivalues.stepPostfix = editor.uifunctions._fillMode_bfs(editor[editor.layerMod], editor.uivalues.stepPostfix[0].x, editor.uivalues.stepPostfix[0].y, + editor[editor.layerMod][0].length, editor[editor.layerMod].length); + } + for (var ii = 0; ii < editor.uivalues.stepPostfix.length; ii++) { + var currx = editor.uivalues.stepPostfix[ii].x, curry = editor.uivalues.stepPostfix[ii].y; + editor[editor.layerMod][curry][currx] = editor.info; + // 检查上下楼梯绑定 + if (editor.layerMod == 'map' && editor.info && editor.info.id == 'upFloor') { + editor.currentFloorData.changeFloor[currx + "," + curry] = { "floorId": ":next", "stair": "downFloor" }; + editor.drawEventBlock(); + } + if (editor.layerMod == 'map' && editor.info && editor.info.id == 'downFloor') { + editor.currentFloorData.changeFloor[currx + "," + curry] = { "floorId": ":before", "stair": "upFloor" }; + editor.drawEventBlock(); + } + } + + } + // console.log(editor.map); + if (editor.info.y != null) { + var found = false; + editor.uivalues.lastUsed.forEach(function (one) { + if (one.id == editor.info.id) { + found = true; + one.recent = new Date().getTime(); + one.frequent = (one.frequent || 0) + 1; + } + }) + if (!found) { + editor.uivalues.lastUsed.push(Object.assign({}, editor.info, { recent: new Date().getTime(), frequent: 1 })); + } + editor.config.set("lastUsed", editor.uivalues.lastUsed); + } + editor.updateMap(); + editor.uivalues.holdingPath = 0; + editor.uivalues.stepPostfix = []; + editor.dom.euiCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uifunctions.highlightSaveFloorButton(); + } + return false; + } + + /** + * bfs找寻和某点相连的全部相同图块坐标 + */ + editor.uifunctions._fillMode_bfs = function (array, x, y, maxWidth, maxHeight) { + var _getNumber = function (x, y) { + if (x < 0 || y < 0 || x >= maxWidth || y >= maxHeight) return null; + return array[y][x].idnum || array[y][x] || 0; + } + var number = _getNumber(x, y) || 0; + var visited = {}, result = []; + var list = [{ x: x, y: y }]; + while (list.length != 0) { + var next = list.shift(), key = next.x + "," + next.y; + if (visited[key]) continue; + visited[key] = true; + result.push(next); + [[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(function (dir) { + var nx = next.x + dir[0], ny = next.y + dir[1]; + if (_getNumber(nx, ny) == number) { + list.push({ x: nx, y: ny }); + } + }); + } + return result; + } + + /** + * editor.dom.mid.onmousewheel + * 在地图编辑区域滚轮切换楼层 + */ + editor.uifunctions.map_mousewheel = function (e) { + var wheel = function (direct) { + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var toId = editor.currentFloorId; + + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + if (direct > 0 && index < editor.core.floorIds.length - 1) + toId = editor.core.floorIds[index + 1]; + else if (direct < 0 && index > 0) + toId = editor.core.floorIds[index - 1]; + else return; + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = toId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(toId); + } + + try { + if (e.wheelDelta) + wheel(Math.sign(e.wheelDelta)) + else if (e.detail) + wheel(Math.sign(e.detail)); + } + catch (e) { + console.error(e); + } + return false; + } + + editor.uifunctions.undoFloor_click = function () { + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + var toId = editor.uivalues.recentFloors.pop(); + if (toId == null || toId == editor.currentFloorId) return; + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = toId; + editor.changeFloor(toId); + } + + editor.uifunctions.selectFloorBtn_click = function () { + editor.uievent.selectFloor(null, '选择楼层', function (floorId) { + if (!floorId || floorId == editor.currentFloorId) return; + + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + printe('请先保存地图!'); + return; + } + + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = floorId; + editor.changeFloor(floorId); + }) + } + + editor.uifunctions.editorTheme_onchange = function () { + var theme = editor.dom.editorTheme.value; + editor.config.set('theme', theme); + document.getElementById('color_css').href = '_server/css/' + theme + '.css'; + } + + /** + * 显示右键菜单 + */ + editor.uifunctions.showMidMenu = function (x, y) { + // --- copy + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + + // 检测是否是上下楼 + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + var extraEvent = editor.dom.extraEvent, parent = extraEvent.parentElement; + if (thisevent == 0) { + parent.removeChild(extraEvent); + parent.appendChild(extraEvent); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定出生点为此点'; + } else if (editor.currentFloorData.changeFloor[editor.pos.x + "," + editor.pos.y]) { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '跳转到目标传送点'; + } else if (thisevent.id == 'upFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定上楼事件'; + } + else if (thisevent.id == 'downFloor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定下楼事件'; + } + else if (['leftPortal', 'rightPortal', 'downPortal', 'upPortal'].indexOf(thisevent.id) >= 0) { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定楼传事件'; + } + else if (thisevent.id == 'specialDoor') { + parent.removeChild(extraEvent); + parent.insertBefore(extraEvent, parent.firstChild); + editor.dom.extraEvent.style.display = 'block'; + editor.dom.extraEvent.children[0].innerHTML = '绑定机关门事件'; + } + else editor.dom.extraEvent.style.display = 'none'; + + editor.dom.chooseThis.children[0].innerHTML = '选中此点' + '(' + editor.pos.x + ',' + editor.pos.y + ')' + editor.dom.copyLoc.children[0].innerHTML = '复制此事件'; + editor.dom.pasteLoc.children[0].innerHTML = '粘贴到此事件'; + editor.dom.midMenu.style = 'top:' + (y + scrollTop) + 'px;left:' + (x + scrollLeft) + 'px;'; + } + + /** + * 隐藏右键菜单 + */ + editor.uifunctions.hideMidMenu = function () { + editor.uivalues.lastMoveE = { buttons: 0, clientX: 0, clientY: 0 }; + editor.dom.midMenu.style = 'display:none'; + } + + /** + * editor.dom.extraEvent.onmousedown + * 菜单 附加点操作 + */ + editor.uifunctions.extraEvent_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + + var thisevent = editor.map[editor.pos.y][editor.pos.x]; + return editor.uifunctions._extraEvent_bindStartPoint(thisevent) + || editor.uifunctions._extraEvent_changeFloor() + || editor.uifunctions._extraEvent_bindStair(thisevent) + || editor.uifunctions._extraEvent_bindSpecialDoor(thisevent); + } + + /** + * 绑定该空地点为起始点 + */ + editor.uifunctions._extraEvent_bindStartPoint = function (thisevent) { + if (thisevent != 0) return false; + if (!confirm('再次确认,你想绑定此点为出生点吗?')) return false; + editor.mode.onmode('tower'); + editor.mode.addAction(["change", "['firstData']['floorId']", editor.currentFloorId]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['x']", editor.pos.x]); + editor.mode.addAction(["change", "['firstData']['hero']['loc']['y']", editor.pos.y]); + editor.mode.onmode('save', function () { + core.firstData.floorId = editor.currentFloorId; + core.firstData.hero.loc.x = editor.pos.x; + core.firstData.hero.loc.y = editor.pos.y; + editor.drawPosSelection(); + editor.drawEventBlock(); + editor.mode.tower(); + printf('绑定初始点成功'); + }); + } + + editor.uifunctions._extraEvent_changeFloor = function () { + var changeFloor = editor.currentFloorData.changeFloor[editor.pos.x + "," + editor.pos.y]; + if (!changeFloor) return false; + core.status.hero.loc = { x: editor.pos.x, y: editor.pos.y, direction: "up" }; + var targetLoc = changeFloor.loc ? { x: changeFloor.loc[0], y: changeFloor.loc[1] } : null; + var info = core.events._changeFloor_getInfo(changeFloor.floorId, changeFloor.stair, targetLoc); + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.dom.selectFloor.value = info.floorId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(info.floorId, function () { + editor.pos.x = info.heroLoc.x; + editor.pos.y = info.heroLoc.y; + editor.setViewport(32 * (editor.pos.x - core.__HALF_SIZE__), 32 * (editor.pos.y - core.__HALF_SIZE__)); + editor.drawPosSelection(); + }); + return true; + } + + /** + * 绑定该楼梯的楼传事件 + */ + editor.uifunctions._extraEvent_bindStair = function (thisevent) { + if (['upFloor', 'downFloor', 'leftPortal', 'rightPortal', 'upPortal', 'downPortal'].indexOf(thisevent.id) < 0) + return false; + var loc = editor.pos.x + "," + editor.pos.y; + if (thisevent.id == 'upFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": "downFloor" }; + } + else if (thisevent.id == 'downFloor') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":before", "stair": "upFloor" }; + } + else if (thisevent.id == 'leftPortal' || thisevent.id == 'rightPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_x" } + } + else if (thisevent.id == 'upPortal' || thisevent.id == 'downPortal') { + editor.currentFloorData.changeFloor[loc] = { "floorId": ":next", "stair": ":symmetry_y" } + } + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawPosSelection(); + editor.drawEventBlock(); + editor_mode.showMode('loc'); + printf('添加楼梯事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + return true; + } + + /** + * 绑定该机关门的事件 + */ + editor.uifunctions._extraEvent_bindSpecialDoor = function (thisevent) { + if (thisevent.id != 'specialDoor') return false; + var number = parseInt(prompt("请输入该机关门的怪物数量", "0")) || 0; + if (number <= 0) return true; + editor.uivalues.bindSpecialDoor.n = number; + editor.uivalues.bindSpecialDoor.loc = editor.pos.x + ',' + editor.pos.y; + editor.uivalues.bindSpecialDoor.enemys = []; + printf("请点击选择" + number + "个怪物;切换楼层或刷新页面取消操作。"); + } + + /** + * 确定绑定该机关门的事件 + * cancel:是否取消此模式 + */ + editor.uifunctions._extraEvent_bindSpecialDoor_doAction = function (cancel) { + var bindSpecialDoor = editor.uivalues.bindSpecialDoor; + if (cancel) { + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + editor.drawEventBlock(); + printf(""); + return; + } + if (bindSpecialDoor.loc == null || bindSpecialDoor.enemys.length != bindSpecialDoor.n) return; + // 添加机关门自动事件 + var doorFlag = "flag:door_" + editor.currentFloorId + "_" + bindSpecialDoor.loc.replace(',', '_'); + editor.currentFloorData.autoEvent[bindSpecialDoor.loc] = { + '0': { + "condition": doorFlag + "==" + bindSpecialDoor.n, + "currentFloor": true, + "priority": 0, + "delayExecute": false, + "multiExecute": false, + "data": [ + { "type": "openDoor" }, + { "type": "setValue", "name": doorFlag, "operator": "=", "value": "null" }, + ] + } + }; + bindSpecialDoor.enemys.forEach(function (loc) { + if (!editor.currentFloorData.afterBattle[loc]) + editor.currentFloorData.afterBattle[loc] = []; + editor.currentFloorData.afterBattle[loc].push({ "type": "setValue", "name": doorFlag, "operator": "+=", "value": "1" }); + }); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + editor.drawEventBlock(); + editor.drawPosSelection(); + editor_mode.showMode('loc'); + printf('绑定机关门事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + bindSpecialDoor.loc = null; + bindSpecialDoor.enemys = []; + bindSpecialDoor.n = 0; + } + + /** + * editor.dom.chooseThis.onmousedown + * 菜单 选中此点 + */ + editor.uifunctions.chooseThis_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + selectBox.isSelected(false); + + editor_mode.onmode('nextChange'); + editor_mode.onmode('loc'); + //editor_mode.loc(); + if (editor.isMobile) editor.showdataarea(false); + return false; + } + + /** + * editor.dom.chooseInRight.onmousedown + * 菜单 在素材区选中此图块 + */ + editor.uifunctions.chooseInRight_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + var thisevent = editor[editor.layerMod][editor.pos.y][editor.pos.x]; + editor.setSelectBoxFromInfo(thisevent, true); + return false; + } + + /** + * editor.dom.copyLoc.onmousedown + * 菜单 复制此事件 + */ + editor.uifunctions.copyLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor_mode.onmode(''); + editor.uivalues.copyedInfo = editor.copyFromPos(); + printf('该点事件已复制'); + return false; + } + + /** + * editor.dom.pasteLoc.onmousedown + * 菜单 移动此事件 + */ + editor.uifunctions.pasteLoc_click = function (e) { + editor.uifunctions.hideMidMenu(); + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + if (!editor.uivalues.copyedInfo) { + printe("没有复制的事件"); + return false; + } + editor.savePreMap(); + editor_mode.onmode(''); + editor.pasteToPos(editor.uivalues.copyedInfo); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('粘贴到事件成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.drawPosSelection(); + }); + return false; + } + + /** + * editor.dom.clearEvent.onmousedown + * 菜单 仅清空此点事件 + */ + editor.uifunctions.clearEvent_click = function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor.clearPos(false); + editor.uifunctions.unhighlightSaveFloorButton(); + return false; + } + + /** + * editor.dom.clearLoc.onmousedown + * 菜单 清空此点及事件 + */ + editor.uifunctions.clearLoc_click = function (e) { + e.stopPropagation(); + e.stopImmediatePropagation(); + e.preventDefault(); + editor.clearPos(true); + editor.uifunctions.unhighlightSaveFloorButton(); + return false; + } + + /** + * editor.dom.showMovable.onchange + * 点击【】 + */ + editor.uifunctions.showMovable_onchange = function () { + printf('此模式下将显示每个点的不可通行状态。
请注意,修改了图块属性的不可出入方向后需要刷新才会正确显示在地图上。'); + editor.uivalues.showMovable = editor.dom.showMovable.checked; + editor.drawEventBlock(); + } + + /** + * editor.dom.brushMod.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod_onchange = function () { + editor.brushMod = editor.dom.brushMod.value; + if (editor.brushMod == 'fill') { + printf('填充模式下,将会用选中的素材替换所有和目标点联通的相同素材'); + } + } + + /** + * editor.dom.brushMod2.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod2_onchange = function () { + editor.brushMod = editor.dom.brushMod2.value; + } + + /** + * editor.dom.brushMod3.onchange + * 切换画笔模式 + */ + editor.uifunctions.brushMod3_onchange = function () { + if (!editor.config.get('alertTileModeV2.7') && + !confirm("从V2.7开始,请直接素材区拖框进行绘制区域。\n\n点取消将不再显示此提示。")) { + editor.config.set('alertTileModeV2.7', true); + } + printf('tileset平铺模式下可以按选中tileset素材,并在地图上拖动来一次绘制一个区域'); + editor.brushMod = editor.dom.brushMod3.value; + } + + editor.uifunctions.brushMod4_onchange = function () { + printf('填充模式下,将会用选中的素材替换所有和目标点联通的相同素材'); + editor.brushMod = editor.dom.brushMod4.value; + } + + editor.uifunctions.setLayerMod = function (layer) { + editor.layerMod = layer; + var canvas = ['ev', 'ev2'].concat(editor.dom.canvas); + canvas.forEach(function (one) { + editor.dom[one + 'c'].style.opacity = 1; + }); + if (layer != 'map') { + canvas.filter(function (one) { + return one + 'map' != editor.layerMod + }).forEach(function (one) { + editor.dom[one + 'c'].style.opacity = 0.3; + }); + } + } + + /** + * editor.dom.layerMod.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod_onchange = function () { + editor.uifunctions.setLayerMod(editor.dom.layerMod.value); + } + + /** + * editor.dom.layerMod2.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod2_onchange = function () { + editor.uifunctions.setLayerMod('bgmap'); + } + + /** + * editor.dom.layerMod3.onchange + * 切换编辑的层 + */ + editor.uifunctions.layerMod3_onchange = function () { + editor.uifunctions.setLayerMod('fgmap'); + } + + editor.uifunctions.triggerBigmap = function () { + editor.uivalues.bigmap = !editor.uivalues.bigmap; + if (editor.uivalues.bigmap) { + editor.dom.bigmapBtn.classList.add('highlight'); + printf("已进入大地图模式"); + } else { + editor.dom.bigmapBtn.classList.remove('highlight'); + editor.setViewport(32 * (editor.pos.x - core.__HALF_SIZE__), 32 * (editor.pos.y - core.__HALF_SIZE__)); + printf("已退出大地图模式"); + } + editor.dom.ebmCtx.clearRect(0, 0, core.__PIXELS__, core.__PIXELS__); + editor.uivalues.startPos = editor.uivalues.endPos = null; + editor.updateMap(); + editor.drawPosSelection(); + } + + /** + * 移动大地图可视窗口的绑定 + */ + editor.uifunctions.viewportButtons_func = function () { + var pressTimer = null; + for (var ii = 0, node; node = editor.dom.viewportButtons.children[ii]; ii++) { + if (ii == 4) { + // 大地图 + node.onclick = function () { + editor.uifunctions.triggerBigmap(); + } + continue; + } + (function (x, y) { + var move = function () { + editor.moveViewport(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + } + + editor.uifunctions.selectFloor_func = function () { + var selectFloor = document.getElementById('selectFloor'); + editor.game.getFloorFileList(function (floors) { + var outstr = []; + floors[0].forEach(function (floor) { + outstr.push(["\n'].join('')); + }); + selectFloor.innerHTML = outstr.join(''); + selectFloor.value = core.status.floorId; + selectFloor.onchange = function () { + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(selectFloor.value); + } + }); + } + + editor.uifunctions.highlightSaveFloorButton = function () { + var saveFloor = document.getElementById('saveFloor'); + saveFloor.classList.add('highlight'); + } + + editor.uifunctions.unhighlightSaveFloorButton = function () { + var saveFloor = document.getElementById('saveFloor'); + saveFloor.classList.remove('highlight'); + } + + editor.uifunctions.saveFloor_func = function () { + var saveFloor = document.getElementById('saveFloor'); + editor_mode.saveFloor = function () { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('保存成功'); + editor.uifunctions.unhighlightSaveFloorButton() + }); + } + saveFloor.onclick = editor_mode.saveFloor; + } + + editor.uifunctions.openDoc_func = function () { + var openDoc = document.getElementById('openDoc'); + openDoc.onclick = function () { + if (editor.isMobile) { + if (!confirm('你确定要打开帮助文档吗?')) return; + window.location = '/_docs/'; + } else { + window.open('/_docs/', '_blank'); + } + } + } + + editor.uifunctions.lastUsed_click = function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + e.stopPropagation(); + if (editor.isMobile) return false; + editor.uivalues.tileSize = [1, 1]; + + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop + var px = scrollLeft + e.clientX - editor.dom.mid2.offsetLeft - editor.dom.lastUsedDiv.offsetLeft + editor.dom.lastUsedDiv.scrollLeft, + py = scrollTop + e.clientY - editor.dom.mid2.offsetTop - editor.dom.lastUsedDiv.offsetTop + editor.dom.lastUsedDiv.scrollTop; + var x = parseInt(px / 32), y = parseInt(py / 32); + if (x == core.__SIZE__ - 1) return false; + var index = x + (core.__SIZE__ - 1) * y; + if (index >= editor.uivalues.lastUsed.length) return; + var lastUsed = editor.uivalues.lastUsed.sort(function (a, b) { + if ((a.istop || 0) != (b.istop || 0)) return (b.istop || 0) - (a.istop || 0); + return (b[editor.uivalues.lastUsedType] || 0) - (a[editor.uivalues.lastUsedType] || 0); + }); + + if (e.button == 2) { + lastUsed[index].istop = lastUsed[index].istop ? 0 : 1; + printf("已" + (lastUsed[index].istop ? '置顶' : '取消置顶') + "该图块"); + editor.config.set('lastUsed', editor.uivalues.lastUsed); + editor.updateLastUsedMap(); + return false; + } + var one = Object.assign({}, lastUsed[index]); + delete one['recent']; + delete one['frequent']; + delete one['istop']; + editor.setSelectBoxFromInfo(one); + return false; + } + + editor.uifunctions.clearLastUsedBtn_click = function () { + if (editor.isMobile) return; + + if (confirm("你确定要清理全部最近使用图块么?\n所有最近使用和最常使用图块(含置顶图块)都将被清除;此过程不可逆!")) { + editor.uivalues.lastUsed = []; + editor.config.set('lastUsed', []); + editor.updateLastUsedMap(); + editor.dom.lastUsedDiv.scroll(0, 0); + } + } + + ///////////////////////////////////////////////////////////////////////////// + + + editor.constructor.prototype.copyFromPos = function (pos) { + editor.uivalues.tileSize = [1, 1]; + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var x0 = pos.x, y0 = pos.y, x1 = pos.x1, y1 = pos.y1; + if (x1 == null) x1 = x0; + if (y1 == null) y1 = y0; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + var result = { w: x1 - x0 + 1, h: y1 - y0 + 1, layer: editor.layerMod, data: [] }; + for (var i = x0; i <= x1; ++i) { + for (var j = y0; j <= y1; ++j) { + var map = core.clone(editor[editor.layerMod][j][i]); + var events = {}; + fields.forEach(function (v) { + events[v] = core.clone(editor.currentFloorData[v][i + ',' + j]); + }) + result.data.push({ map: map, events: events }); + } + } + return result; + } + + editor.constructor.prototype.pasteToPos = function (info, pos) { + editor.uivalues.tileSize = [1, 1]; + if (info == null) return; + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var w = info.w || 1, h = info.h || 1, layer = info.layer || 'map'; + var data = core.clone(info.data || []); + for (var i = pos.x; i < pos.x + w; ++i) { + for (var j = pos.y; j < pos.y + h; ++j) { + var one = data.shift(); + if (j >= editor[editor.layerMod].length || i >= editor[editor.layerMod][0].length) continue; + editor[editor.layerMod][j][i] = core.clone(one.map); + if (layer == 'map' && editor.layerMod == 'map') { + fields.forEach(function (v) { + if (one.events[v] == null) delete editor.currentFloorData[v][i + "," + j]; + else editor.currentFloorData[v][i + "," + j] = core.clone(one.events[v]); + }); + } + } + } + } + + editor.constructor.prototype.movePos = function (startPos, endPos, callback) { + editor.uivalues.tileSize = [1, 1]; + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var copyed = editor.copyFromPos(startPos); + editor.pasteToPos({ w: 1, h: 1, layer: 'map', data: [{ map: 0, events: {} }] }, startPos); + editor.pasteToPos(copyed, endPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('移动事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.exchangePos = function (startPos, endPos, callback) { + editor.uivalues.tileSize = [1, 1]; + if (!startPos || !endPos) return; + if (startPos.x == endPos.x && startPos.y == endPos.y) return; + var startInfo = editor.copyFromPos(startPos); + var endInfo = editor.copyFromPos(endPos); + editor.pasteToPos(startInfo, endPos); + editor.pasteToPos(endInfo, startPos); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('交换事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + editor.constructor.prototype.savePreMap = function () { + if (editor.map.length * editor.map[0].length >= 4096) return; + var dt = {}; + editor.dom.maps.forEach(function (one) { + dt[one] = editor[one]; + }); + if (editor.uivalues.preMapData.length == 0 + || !core.same(editor.uivalues.preMapData[editor.uivalues.preMapData.length - 1], dt)) { + editor.uivalues.preMapData.push(core.clone(dt)); + if (editor.uivalues.preMapData.length > editor.uivalues.preMapMax) { + editor.uivalues.preMapData.shift(); + } + } + } + + editor.constructor.prototype.clearPos = function (clearPos, pos, callback) { + var fields = Object.keys(editor.file.comment._data.floors._data.loc._data); + pos = pos || editor.pos; + var x0 = pos.x, y0 = pos.y, x1 = pos.x1, y1 = pos.y1; + if (x1 == null) x1 = x0; + if (y1 == null) y1 = y0; + if (x0 > x1) { x0 ^= x1; x1 ^= x0; x0 ^= x1; }//swap + if (y0 > y1) { y0 ^= y1; y1 ^= y0; y0 ^= y1; }//swap + editor.uifunctions.hideMidMenu(); + editor.savePreMap(); + editor.info = 0; + editor_mode.onmode(''); + for (var i = x0; i <= x1; ++i) { + for (var j = y0; j <= y1; ++j) { + if (j >= editor[editor.layerMod].length || i >= editor[editor.layerMod][0].length) continue; + if (clearPos) + editor[editor.layerMod][j][i] = 0; + if (editor.layerMod == 'map') { + fields.forEach(function (v) { + delete editor.currentFloorData[v][i + "," + j]; + }); + } + } + } + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf(clearPos ? '清空该点和事件成功' : '只清空该点事件成功'); + editor.drawPosSelection(); + if (callback) callback(); + }); + } + + + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/_server/editor_materialpanel.js b/_server/editor_materialpanel.js new file mode 100644 index 0000000..3ff0f5d --- /dev/null +++ b/_server/editor_materialpanel.js @@ -0,0 +1,243 @@ +editor_materialpanel_wrapper = function (editor) { + + // 由于历史遗留原因, 以下变量作为全局变量使用 + // selectBox + + window.selectBox=document.getElementById('selectBox') + selectBox._isSelected=false + selectBox.isSelected=function(value){ + if(value!=null){ + selectBox._isSelected=value; + editor.dom.dataSelection.style.display=value?'':'none' + } + return selectBox._isSelected + } + + var locToPos = function (loc) { + return { 'x': ~~(loc.x / loc.size), 'y': ~~(loc.y / loc.size) }; + } + + editor.uifunctions.getScrollBarHeight = function () { + var outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.width = "100px"; + outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps + + document.body.appendChild(outer); + + var widthNoScroll = outer.offsetWidth; + // force scrollbars + outer.style.overflow = "scroll"; + + // add innerdiv + var inner = document.createElement("div"); + inner.style.width = "100%"; + outer.appendChild(inner); + + var widthWithScroll = inner.offsetWidth; + + // remove divs + outer.parentNode.removeChild(outer); + + return widthNoScroll - widthWithScroll; + } + + /** + * editor.dom.iconExpandBtn.onclick + */ + editor.uifunctions.fold_material_click = function () { + if (editor.uivalues.folded) { + if (confirm("你想要展开素材吗?\n展开模式下将显示全素材内容。")) { + editor.config.set('folded', false, function() { + window.location.reload(); + }); + } + } else { + var perCol = parseInt(prompt("请输入折叠素材模式下每列的个数:", "50")) || 0; + if (perCol > 0) { + editor.config.set('foldPerCol', perCol, false); + editor.config.set('folded', true, function() { + window.location.reload(); + }); + } + } + } + + /** + * editor.dom.iconLib.onmousedown + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_ondown = function (e) { + e.stopPropagation(); + e.preventDefault(); + editor.uivalues.lastMoveMaterE=e; + if (!editor.isMobile && e.clientY >= editor.dom.iconLib.offsetHeight - editor.uivalues.scrollBarHeight) return; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + editor.uivalues.startLoc={ + 'x': scrollLeft + e.clientX + editor.dom.iconLib.scrollLeft - right.offsetLeft - editor.dom.iconLib.offsetLeft, + 'y': scrollTop + e.clientY + editor.dom.iconLib.scrollTop - right.offsetTop - editor.dom.iconLib.offsetTop, + 'px': e.clientX, + 'py': e.clientY, + 'size': 32 + }; + } + + /** + * editor.dom.iconLib.onmousemove + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_onmove = function (e) { + e.stopPropagation(); + e.preventDefault(); + editor.uivalues.lastMoveMaterE=e; + if (!editor.uivalues.startLoc) return; + var pos0 = locToPos(editor.uivalues.startLoc); + + editor.dom.dataSelection.style.left = 32 * pos0.x + 'px'; + editor.dom.dataSelection.style.top = 32 * pos0.y + 'px'; + editor.dom.dataSelection.style.width = e.clientX - editor.uivalues.startLoc.px + 'px'; + editor.dom.dataSelection.style.height = e.clientY - editor.uivalues.startLoc.py + 'px'; + editor.dom.dataSelection.style.display = 'block'; + } + + /** + * editor.dom.iconLib.onmouseup + * 素材区的单击/拖拽事件 + */ + editor.uifunctions.material_onup = function (ee) { + var startLoc = editor.uivalues.startLoc; + editor.uivalues.startLoc = null; + + var e=editor.uivalues.lastMoveMaterE; + if (!editor.isMobile && e.clientY >= editor.dom.iconLib.offsetHeight - editor.uivalues.scrollBarHeight) return; + var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + var loc = { + 'x': scrollLeft + e.clientX + editor.dom.iconLib.scrollLeft - right.offsetLeft - editor.dom.iconLib.offsetLeft, + 'y': scrollTop + e.clientY + editor.dom.iconLib.scrollTop - right.offsetTop - editor.dom.iconLib.offsetTop, + 'size': 32 + }; + editor.loc = loc; + editor.uivalues.tileSize = [1,1]; + var pos0 = locToPos(startLoc); + var pos = locToPos(loc); + for (var spriter in editor.widthsX) { + if (pos.x >= editor.widthsX[spriter][1] && pos.x < editor.widthsX[spriter][2]) { + var ysize = spriter.endsWith('48') ? 48 : 32; + loc.ysize = ysize; + pos.images = editor.widthsX[spriter][0]; + pos.y = ~~(loc.y / loc.ysize); + if (!editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) pos.x = editor.widthsX[spriter][1]; + var autotiles = core.material.images['autotile']; + if (pos.images == 'autotile') { + var imNames = Object.keys(autotiles); + if (editor.uivalues.folded) { + pos.y = Math.min(pos.y, imNames.length - 1); + pos.images = imNames[pos.y]; + } else { + if ((pos.y + 1) * ysize > editor.widthsX[spriter][3]) + pos.y = ~~(editor.widthsX[spriter][3] / ysize) - 4; + else { + for (var i = 0; i < imNames.length; i++) { + if (pos.y >= 4 * i && pos.y < 4 * (i + 1)) { + pos.images = imNames[i]; + pos.y = 4 * i; + } + } + } + } + } + else { + var height = editor.widthsX[spriter][3], col = height / ysize; + if (spriter == 'terrains') col += 2; + if (editor.uivalues.folded && core.tilesets.indexOf(pos.images) == -1) { + col = (pos.x == editor.widthsX[spriter][2] - 1) ? ((col - 1) % editor.uivalues.foldPerCol + 1) : editor.uivalues.foldPerCol; + } + pos.y = Math.min(pos.y, col - 1); + } + + selectBox.isSelected(true); + // console.log(pos,core.material.images[pos.images].height) + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize - 6 + 'px'; + editor.dom.dataSelection.style.width = 32 - 6 + 'px'; + + if (pos.x == 0 && pos.y == 0) { + // editor.info={idnum:0, id:'empty','images':'清除块', 'y':0}; + editor.info = 0; + } else if (pos.x == 0 && pos.y == 1) { + editor.info = editor.ids[editor.indexs[17]]; + } else { + if (autotiles[pos.images]) editor.info = { 'images': pos.images, 'y': 0 }; + else if (core.tilesets.indexOf(pos.images) != -1) editor.info = { 'images': pos.images, 'y': pos.y, 'x': pos.x - editor.widthsX[spriter][1] }; + else { + var y = pos.y; + if (editor.uivalues.folded) { + y += editor.uivalues.foldPerCol * (pos.x - editor.widthsX[spriter][1]); + } + if (pos.images == 'terrains') y -= 2; + editor.info = { 'images': pos.images, 'y': y } + } + + for (var idindex = 0; idindex < editor.ids.length; idindex++) { + if ((core.tilesets.indexOf(pos.images) != -1 && editor.info.images == editor.ids[idindex].images + && editor.info.y == editor.ids[idindex].y && editor.info.x == editor.ids[idindex].x) + || (Object.prototype.hasOwnProperty.call(autotiles, pos.images) && editor.info.images == editor.ids[idindex].id + && editor.info.y == editor.ids[idindex].y) + || (core.tilesets.indexOf(pos.images) == -1 && editor.info.images == editor.ids[idindex].images + && editor.info.y == editor.ids[idindex].y) + ) { + + editor.info = editor.ids[idindex]; + break; + } + } + + if (editor.info.isTile && (editor.isMobile || e.button == 2)) { //这段改一改之类的应该能给手机用,就不删了 + var v = prompt("请输入该额外素材区域绑定宽高,以逗号分隔", "1,1"); + if (v != null && /^\d+,\d+$/.test(v)) { + v = v.split(","); + var x = parseInt(v[0]), y = parseInt(v[1]); + var widthX = editor.widthsX[editor.info.images]; + if (x <= 0 || y <= 0 || editor.info.x + x > widthX[2] - widthX[1] || 32*(editor.info.y + y) > widthX[3]) { + alert("不合法的输入范围,已经越界"); + } else { + editor.uivalues.tileSize = [x, y]; + editor.dom.dataSelection.style.left = pos.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize*y - 6 + 'px'; + editor.dom.dataSelection.style.width = 32*x - 6 + 'px'; + } + } + } + if (editor.info.isTile && !editor.isMobile && e.button != 2) { //左键拖拽框选 + + var x = pos.x-pos0.x+1, y = pos.y-pos0.y+1; + var widthX = editor.widthsX[editor.info.images]; + // 懒得仔细处理了, 只允许左上往右下拉 + if (x <= 0 || y <= 0 || pos0.x < widthX[1]){ + + } else { + editor.info = editor.ids[idindex-(x-1)-(y-1)*(widthX[2]-widthX[1])]; + editor.uivalues.tileSize = [x, y]; + editor.dom.dataSelection.style.left = pos0.x * 32 + 'px'; + editor.dom.dataSelection.style.top = pos0.y * ysize + 'px'; + editor.dom.dataSelection.style.height = ysize*y - 6 + 'px'; + editor.dom.dataSelection.style.width = 32*x - 6 + 'px'; + } + + } + + } + editor.uifunctions.showBlockInfo(JSON.parse(JSON.stringify(editor.info))); + editor_mode.onmode('nextChange'); + editor_mode.onmode('enemyitem'); + editor.updateLastUsedMap(); + //editor_mode.enemyitem(); + } + } + } + +} \ No newline at end of file diff --git a/_server/editor_mode.js b/_server/editor_mode.js new file mode 100644 index 0000000..151a3b8 --- /dev/null +++ b/_server/editor_mode.js @@ -0,0 +1,371 @@ +editor_mode = function (editor) { + var core = editor.core; + + function editor_mode() { + this.ids = { + 'loc': 'left2', + 'enemyitem': 'left3', + 'floor': 'left4', + 'tower': 'left5', + 'functions': 'left8', + + 'map': 'left', + 'appendpic': 'left1', + + 'commonevent': 'left9', + 'plugins': 'left10', + } + this._ids = {} + this.dom = {} + this.actionList = []; + this.mode = 'tower'; // 初始默认显示全塔属性 + this.info = {}; + this.appendPic = {}; + this.doubleClickMode = 'change'; + } + + editor_mode.prototype.init = function (callback) { + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.init_dom_ids = function (callback) { + + Object.keys(editor_mode.ids).forEach(function (v) { + editor_mode.dom[v] = document.getElementById(editor_mode.ids[v]); + editor_mode._ids[editor_mode.ids[v]] = v; + }); + + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.indent = function (field) { + var num = '\t'; + if (field.indexOf("['main']") === 0) return 0; + if (field === "['special']") return 0; + return num; + } + + editor_mode.prototype.addAction = function (action) { + editor_mode.actionList.push(action); + } + + editor_mode.prototype.doActionList = function (mode, actionList, callback) { + if (actionList.length == 0) return; + printf('修改中...'); + var cb = function (objs_) { + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; + var str = '修改成功!'; + if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.name == 'template') + str += '
请注意:全塔属性的name尚未修改,请及时予以设置。'; + if (mode == 'enemyitem') { + if (editor.info && editor.info.idnum) { + var block = editor.core.maps.blocksInfo[editor.info.idnum]; + if (block.doorInfo != null && block.doorInfo.keys != null && Object.keys(block.doorInfo.keys).length > 0 + && block.trigger != 'openDoor') { + str += "
你修改了门信息,但触发器未改成openDoor,请修改否则无法撞击开门。" + } + } + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + if (core.getFaceDownId(editor_mode.info.id) != editor_mode.info.id) { + str += "
绑定行走图朝向后只需要对应设置朝下怪物的属性,会自动同步而无需修改其他朝向的属性。" + } + } + } + printf(str); + if (callback) callback(); + } + switch (mode) { + case 'loc': + editor.file.editLoc(editor_mode.pos.x, editor_mode.pos.y, actionList, function (objs_) { + cb(objs_); + editor.drawPosSelection(); + }); + break; + case 'enemyitem': + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + editor.file.editEnemy(editor_mode.info.id, actionList, cb); + } else if (editor_mode.info.images == 'items') { + editor.file.editItem(editor_mode.info.id, actionList, cb); + } else { + editor.file.editMapBlocksInfo(editor_mode.info.idnum, actionList, cb); + } + break; + case 'floor': + editor.file.editFloor(actionList, cb); + break; + case 'tower': + editor.file.editTower(actionList, cb); + break; + case 'functions': + editor.file.editFunctions(actionList, cb); + break; + case 'commonevent': + editor.file.editCommonEvent(actionList, cb); + break; + case 'plugins': + editor.file.editPlugins(actionList, cb); + break; + default: + break; + } + } + + editor_mode.prototype.onmode = function (mode, callback) { + if (editor_mode.mode != mode) { + if (mode === 'save') editor_mode.doActionList(editor_mode.mode, editor_mode.actionList, callback); + if (editor_mode.mode === 'nextChange' && mode) editor_mode.showMode(mode); + if (mode !== 'save') editor_mode.mode = mode; + editor_mode.actionList = []; + } + } + + editor_mode.prototype.showMode = function (mode) { + for (var name in this.dom) { + editor_mode.dom[name].style = 'z-index:-1;opacity: 0;'; + } + editor_mode.dom[mode].style = ''; + editor_mode.doubleClickMode = 'change'; + // clear + editor.drawEventBlock(); + if (editor_mode[mode]) editor_mode[mode](); + editor.dom.editModeSelect.value = mode; + if (!selectBox.isSelected()) editor.uifunctions.showTips(); + } + + editor_mode.prototype.change = function (value) { + editor_mode.onmode('nextChange'); + editor_mode.onmode(value); + if (editor.isMobile) editor.showdataarea(false); + } + + + editor_mode.prototype.checkUnique = function (thiseval) { + if (!(thiseval instanceof Array)) return false; + var map = {}; + for (var i = 0; i < thiseval.length; ++i) { + if (map[thiseval[i]]) { + alert("警告:存在重复定义!"); + return false; + } + map[thiseval[i]] = true; + } + return true; + } + + editor_mode.prototype.checkFloorIds = function (thiseval) { + if (!editor_mode.checkUnique(thiseval)) return false; + var oldvalue = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds; + fs.readdir('project/floors', function (err, data) { + if (err) { + printe(err); + throw Error(err); + } + var newfiles = thiseval.map(function (v) { return v + '.js' }); + var notExist = ''; + for (var name, ii = 0; name = newfiles[ii]; ii++) { + if (data.indexOf(name) === -1) notExist = name; + } + if (notExist) { + var discard = confirm('文件' + notExist + '不存在, 保存会导致工程无法打开, 是否放弃更改'); + if (discard) { + editor.file.editTower([['change', "['main']['floorIds']", oldvalue]], function (objs_) {//console.log(objs_); + if (objs_.slice(-1)[0] != null) { + printe(objs_.slice(-1)[0]); + throw (objs_.slice(-1)[0]) + } + ; printe('已放弃floorIds的修改,请F5进行刷新'); + }); + } + } + }); + return true + } + + editor_mode.prototype.checkImages = function (thiseval, directory) { + if (!directory) return true; + if (!editor_mode.checkUnique(thiseval)) return false; + fs.readdir(directory, function (err, data) { + if (err) { + printe(err); + throw Error(err); + } + var notExist = null; + thiseval.map(function (v) { + var name = v.indexOf('.') < 0 ? (v+'.png') : v; + if (data.indexOf(name) < 0) notExist = name; + return name; + }); + if (notExist) { + alert('警告!图片' + notExist + '不存在!保存可能导致工程无法打开,请及时修改!'); + } + }); + return true; + } + + editor_mode.prototype.changeDoubleClickModeByButton = function (mode) { + ({ + delete: function () { + printf('下一次双击表格的项删除,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + }, + add: function () { + printf('下一次双击表格的项则在同级添加新项,切换下拉菜单可取消;编辑后需刷新浏览器生效。'); + editor_mode.doubleClickMode = mode; + } + }[mode])(); + } + + ///////////////////////////////////////////////////////////////////////////// + + editor_mode.prototype.loc = function (callback) { + //editor.pos={x: 0, y: 0}; + if (!core.isset(editor.pos)) return; + editor_mode.pos = editor.pos; + document.getElementById('pos_a6771a78_a099_417c_828f_0a24851ebfce').innerText = editor_mode.pos.x + ',' + editor_mode.pos.y; + + var objs = []; + editor.file.editLoc(editor_mode.pos.x, editor_mode.pos.y, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_3d846fc4_7644_44d1_aa04_433d266a73df').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + editor.drawPosSelection(); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.enemyitem = function (callback) { + //editor.info=editor.ids[editor.indexs[201]]; + if (!core.isset(editor.info)) return; + + if (Object.keys(editor.info).length !== 0 && editor.info.idnum != 17) editor_mode.info = editor.info;//避免editor.info被清空导致无法获得是物品还是怪物 + + if (!core.isset(editor_mode.info.id)) { + // document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = ''; + document.getElementById('newIdIdnum').style.display = 'block'; + document.getElementById('enemyItemTable').style.display = 'none'; + document.getElementById('changeId').style.display = 'none'; + return; + } + + document.getElementById('newIdIdnum').style.display = 'none'; + document.getElementById('enemyItemTable').style.display = 'block'; + document.getElementById('changeId').style.display = 'block'; + + var objs = []; + if (editor_mode.info.images == 'enemys' || editor_mode.info.images == 'enemy48') { + editor.file.editEnemy(editor_mode.info.id, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } else if (editor_mode.info.images == 'items') { + editor.file.editItem(editor_mode.info.id, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } else { + /* document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML=''; + return; */ + editor.file.editMapBlocksInfo(editor_mode.info.idnum, [], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + } + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_a3f03d4c_55b8_4ef6_b362_b345783acd72').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.floor = function (callback) { + var objs = []; + editor.file.editFloor([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_4a3b1b09_b2fb_4bdf_b9ab_9f4cdac14c74').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.tower = function (callback) { + var objs = []; + editor.file.editTower([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_b6a03e4c_5968_4633_ac40_0dfdd2c9cde5').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.functions = function (callback) { + var objs = []; + editor.file.editFunctions([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_e260a2be_5690_476a_b04e_dacddede78b3').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.commonevent = function (callback) { + var objs = []; + editor.file.editCommonEvent([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_b7bf0124_99fd_4af8_ae2f_0017f04a7c7d').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + editor_mode.prototype.plugins = function (callback) { + var objs = []; + editor.file.editPlugins([], function (objs_) { + objs = objs_; + //console.log(objs_) + }); + //只查询不修改时,内部实现不是异步的,所以可以这么写 + var tableinfo = editor.table.objToTable(objs[0], objs[1]); + document.getElementById('table_e2c034ec_47c6_48ae_8db8_4f8f32fea2d6').innerHTML = tableinfo.HTML; + tableinfo.listen(tableinfo.guids); + if (Boolean(callback)) callback(); + } + + ///////////////////////////////////////////////////////////////////////////// + + /** + * editor.dom.editModeSelect.onchange + */ + editor_mode.prototype.editModeSelect_onchange = function () { + editor_mode.change(editor.dom.editModeSelect.value); + } + + editor_mode.prototype.listen = function (callback) { + // 移动至 editor_listen.js -> editor.constructor.prototype.mode_listen + } + + var editor_mode = new editor_mode(); + editor_mode.init_dom_ids(); + + return editor_mode; +} +//editor_mode = editor_mode(editor); \ No newline at end of file diff --git a/_server/editor_multi.js b/_server/editor_multi.js new file mode 100644 index 0000000..30d8250 --- /dev/null +++ b/_server/editor_multi.js @@ -0,0 +1,515 @@ + + +editor_multi = function () { + + var editor_multi = {}; + + var extraKeys = { + "Ctrl-/": function (cm) { cm.toggleComment(); }, + "Ctrl-B": function (cm) { ternServer.jumpToDef(cm); }, + "Ctrl-Q": function (cm) { ternServer.rename(cm); }, + "Cmd-F": CodeMirror.commands.findPersistent, + "Ctrl-F": CodeMirror.commands.findPersistent, + "Ctrl-R": CodeMirror.commands.replaceAll, + "Ctrl-D": function (cm) { cm.foldCode(cm.getCursor()); }, + "Ctrl-O": function () { editor_multi.openUrl('/_docs/#/api'); }, + "Ctrl-P": function () { editor_multi.openUrl('https://h5mota.com/plugins/'); } + }; + + var codeEditor = CodeMirror.fromTextArea(document.getElementById("multiLineCode"), { + lineNumbers: true, + matchBrackets: true, + indentUnit: 4, + tabSize: 4, + indentWithTabs: true, + smartIndent: true, + mode: { name: "javascript", globalVars: true, localVars: true }, + lineWrapping: true, + continueComments: "Enter", + gutters: ["CodeMirror-lint-markers", "CodeMirror-linenumbers", "CodeMirror-foldgutter"], + lint: true, + autocomplete: true, + autoCloseBrackets: true, + styleActiveLine: true, + extraKeys: extraKeys, + foldGutter: true, + inputStyle: "textarea", + highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true } + }); + + var commandsName = { + 'Ctrl-/': '注释当前选中行(Ctrl+/)', + 'Ctrl-B': '跳转到定义(Ctrl+B)', + 'Ctrl-Q': '重命名变量(Ctrl+Q)', + 'Ctrl-F': '查找(Ctrl+F)', + 'Ctrl-R': '全部替换(Ctrl+R)', + 'Ctrl-D': '折叠或展开块(Ctrl+D)', + 'Ctrl-O': '打开API列表(Ctrl+O)', + 'Ctrl-P': '打开在线插件列表(Ctrl+P)' + }; + + document.getElementById('codemirrorCommands').innerHTML = + "" + + Object.keys(commandsName).map(function (name) { + return "" + }).join(''); + + var coredef = terndefs_f6783a0a_522d_417e_8407_94c67b692e50[2]; + Object.keys(core.material.enemys).forEach(function (name) { + coredef.core.material.enemys[name] = { + "!type": "enemy", + "!doc": core.material.enemys[name].name || "怪物" + } + }); + Object.keys(core.material.bgms).forEach(function (name) { + coredef.core.material.bgms[name] = { + "!type": "audio", + "!doc": "背景音乐" + } + }); + Object.keys(core.material.sounds).forEach(function (name) { + coredef.core.material.sounds[name] = { + "!type": "audio", + "!doc": "音效" + } + }); + Object.keys(core.material.animates).forEach(function (name) { + coredef.core.material.animates[name] = { + "!type": "animate", + "!doc": "动画" + } + }); + Object.keys(core.material.images).forEach(function (name) { + if (core.material.images[name] instanceof Image) { + coredef.core.material.images[name] = { + "!type": "image", + "!doc": "系统图片" + } + } else { + coredef.core.material.images[name] = { + "!doc": name == 'autotile' ? '自动元件' : name == 'tilesets' ? '额外素材' : name == 'images' ? '自定义图片' : '系统图片' + } + for (var v in core.material.images[name]) { + coredef.core.material.images[name][v] = { + "!type": "image", + } + } + } + + }) + Object.keys(core.material.items).forEach(function (name) { + coredef.core.material.items[name] = { + "!type": "item", + "!doc": core.material.items[name].name || "道具" + } + }); + functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys.getSpecials().forEach(function (one) { + var name = one[1]; + if (name instanceof Function) name = name({}); + coredef.core.enemys.hasSpecial["!doc"] += name + "(" + one[0] + "); "; + }); + Object.keys(core.canvas).forEach(function (name) { + coredef.core.canvas[name] = { + "!type": "CanvasRenderingContext2D", + "!doc": "系统画布" + } + }); + Object.keys(core.status.maps).forEach(function (name) { + coredef.core.status.maps[name] = { + "!type": "floor", + "!doc": core.status.maps[name].title || '' + } + coredef.core.status.bgmaps[name] = { + "!type": "[[number]]", + "!doc": core.status.maps[name].title || '' + } + coredef.core.status.fgmaps[name] = { + "!type": "[[number]]", + "!doc": core.status.maps[name].title || '' + } + }); + Object.keys(core.status.shops).forEach(function (id) { + coredef.core.status.shops[id] = { + "!doc": core.status.shops[id].textInList || "全局商店" + } + }); + Object.keys(core.status.textAttribute).forEach(function (id) { + coredef.core.status.textAttribute[id] = {}; + }); + // --- 转发函数 + for (var name in coredef.core) { + if (typeof coredef.core[name] === 'object') { + for (var funcname in coredef.core[name]) { + var one = coredef.core[name][funcname] || {}; + var type = one["!type"] || ""; + if (type.startsWith("fn(")) { + var forwardname = (functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a[name] || {})[funcname] ? '脚本编辑' : name; + coredef.core[funcname] = { + "!type": one["!type"], + "!doc": one["!doc"] + "
(转发到" + forwardname + "中)" + }; + if (one["!url"]) coredef.core[funcname]["!url"] = one["!url"]; + } + } + for (var funcname in core[name]) { + if (!(core[name][funcname] instanceof Function) || funcname.charAt(0) == '_' || coredef.core[name][funcname]) continue; + var parameterInfo = /^\s*function\s*[\w_$]*\(([\w_,$\s]*)\)\s*\{/.exec(core[name][funcname].toString()); + var parameters = (parameterInfo == null ? "" : parameterInfo[1]) + .replace(/\s*/g, '').replace(/,/g, ', ').split(', ') + .filter(function (one) { return one.trim() != ''; }) + .map(function (one) { return one.trim() + ': ?'; }).join(', '); + coredef.core[funcname] = coredef.core[name][funcname] = { + "!type": "fn(" + parameters + ")" + } + } + } + } + + Object.keys(core.values).forEach(function (id) { + var one = data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.values._data[id]; + if (!one) return; + coredef.core.values[id] = { + "!type": "number", + "!doc": one._data, + } + }); + Object.keys(core.flags).forEach(function (id) { + var one = data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc._data.flags._data[id]; + if (!one) return; + coredef.core.flags[id] = { + "!type": id == 'statusBarItems' ? '[string]' : 'bool', + "!doc": one._data, + } + }); + + var ternServer = new CodeMirror.TernServer({ + defs: terndefs_f6783a0a_522d_417e_8407_94c67b692e50, + plugins: { + doc_comment: true, + complete_strings: true, + }, + useWorker: false + }); + + editor_multi.ternServer = ternServer; + editor_multi.codeEditor = codeEditor; + + codeEditor.on("cursorActivity", function (cm) { + var cursor = cm.getCursor(); + if (codeEditor.getOption("autocomplete") && !(cursor.line == 0 && cursor.ch == 0)) { + ternServer.updateArgHints(cm); + ternServer.showDocs(cm); + } + }); + + var ctrlRelease = new Date(); + codeEditor.on("keyup", function (cm, event) { + var date = new Date(); + if (event.keyCode == 17 || event.keyCode == 91) { // ctrl, cmd + ctrlRelease = date; + } + else if (codeEditor.getOption("autocomplete") && !event.ctrlKey && date - ctrlRelease >= 1000 && ( + (event.keyCode >= 65 && event.keyCode <= 90) || + (!event.shiftKey && event.keyCode == 190) || (event.shiftKey && event.keyCode == 189))) { + try { + ternServer.complete(cm); + } catch (e) { + } + } + }); + + editor_multi.id = ''; + editor_multi.isString = false; + editor_multi.lintAutocomplete = false; + + var lastOffset = {}; + + editor_multi.show = function () { + if (typeof (selectBox) !== typeof (undefined)) selectBox.isSelected(false); + var valueNow = codeEditor.getValue(); + //try{eval('function _asdygakufyg_() { return '+valueNow+'\n}');editor_multi.lintAutocomplete=true;}catch(ee){} + if (valueNow.slice(0, 8) === 'function') editor_multi.lintAutocomplete = true; + editor_multi.setLint(); + document.getElementById('left7').style = ''; + } + editor_multi.hide = function () { + document.getElementById('left7').style = 'z-index:-1;opacity: 0;'; + } + editor_multi.setLint = function () { + if (editor_multi.lintAutocomplete) { + codeEditor.setOption("lint", { + options: { + esversion: 2021 + } + }); + } else { + codeEditor.setOption("lint", false); + } + codeEditor.setOption("autocomplete", editor_multi.lintAutocomplete); + document.getElementById("lintCheckbox").checked = editor_multi.lintAutocomplete; + } + editor_multi.toggerLint = function () { + editor_multi.lintAutocomplete = document.getElementById("lintCheckbox").checked; + editor_multi.setLint(); + } + + editor_multi.indent = function (field) { + if (typeof (editor) !== typeof (undefined) && editor && editor.mode && editor.mode.indent) return editor.mode.indent(field); + return '\t'; + } + + var _format = function () { + if (!editor_multi.lintAutocomplete) return; + var offset = (codeEditor.getScrollInfo() || {}).top || 0; + _setValue(beautifier.js(codeEditor.getValue(), { + brace_style: "collapse-preserve-inline", + indent_with_tabs: true, + jslint_happy: true + })); + codeEditor.scrollTo(0, offset); + } + + var _setValue = function (val) { + codeEditor.setValue(val || ''); + ternServer.delDoc('doc'); + ternServer.addDoc('doc', new CodeMirror.Doc(val || '', 'javascript')); + } + + editor_multi.format = function () { + if (!editor_multi.lintAutocomplete) { + alert("只有代码才能进行格式化操作!"); + return; + } + _format(); + } + + editor_multi.hasError = function () { + if (!editor_multi.lintAutocomplete) return false; + return JSHINT.errors.filter(function (e) { + return e.code.startsWith("E") + }).length > 0; + } + + var _previewButton = document.getElementById('editor_multi_preview'); + + _previewButton.onclick = function () { + if (!editor_multi.preview) return; + _format(); + if (editor_multi.hasError()) { + alert("当前好像存在严重的语法错误,请处理后再预览。"); + return; + } + editor.uievent.previewEditorMulti(editor_multi.preview, codeEditor.getValue()); + } + + editor_multi.import = function (id_, args) { + var thisTr = document.getElementById(id_); + if (!thisTr) return false; + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var comment = thisTr.children[1].getAttribute('title'); + if (!input.type || input.type !== 'textarea') return false; + editor_multi.id = id_; + editor_multi.isString = false; + editor_multi.lintAutocomplete = false; + editor_multi.preview = args.preview; + _previewButton.style.display = editor_multi.preview ? 'inline' : 'none'; + if (args.lint === true) editor_multi.lintAutocomplete = true; + if ((!input.value || input.value == 'null') && args.template) + input.value = '"' + args.template + '"'; + if ((!input.value || input.value == 'null') && editor_mode.mode == 'plugins') + input.value = '"function () {\\n\\t// 在此增加新插件\\n\\t\\n}"'; + // if ((!input.value || input.value == 'null') && args) + if (input.value.slice(0, 1) === '"' || args.string) { + editor_multi.isString = true; + _setValue(JSON.parse(input.value) || ''); + } else { + var num = editor_multi.indent(field); + eval('var tobj=' + (input.value || 'null')); + var tmap = {}; + var tstr = JSON.stringify(tobj, function (k, v) { + if (typeof (v) === typeof ('') && v.slice(0, 8) === 'function') { + var id_ = editor.util.guid(); + tmap[id_] = v.toString(); + return id_; + } else return v + }, num); + for (var id_ in tmap) { + tstr = tstr.replace('"' + id_ + '"', tmap[id_]) + } + _setValue(tstr || ''); + } + editor_multi.show(); + codeEditor.scrollTo(0, lastOffset[editor_multi.id] || 0); + return true; + } + + editor_multi.cancel = function () { + if (editor_multi.id && editor_multi.id != 'callFromBlockly' && editor_multi.id != 'importFile') { + lastOffset[editor_multi.id] = (codeEditor.getScrollInfo() || {}).top; + } + editor_multi.hide(); + editor_multi.id = ''; + multiLineArgs = [null, null, null]; + } + + editor_multi.confirm = function (keep) { + if (editor_multi.hasError()) { + alert("当前好像存在严重的语法错误,请处理后再保存。\n严重的语法错误可能会导致整个编辑器的崩溃。"); + return; + } + + if (!editor_multi.id) { + editor_multi.id = ''; + return; + } + + if (editor_multi.id === 'callFromBlockly') { + // ----- 自动格式化 + _format(); + editor_multi.multiLineDone(keep); + return; + } + + if (editor_multi.id === 'importFile') { + _format(); + editor_multi.writeFileDone(keep); + return; + } + + var setvalue = function (value) { + var thisTr = document.getElementById(editor_multi.id); + var input = thisTr.children[2].children[0].children[0]; + if (editor_multi.isString) { + input.value = JSON.stringify(value); + } else { + eval('var tobj=' + (value || 'null')); + var tmap = {}; + var tstr = JSON.stringify(tobj, function (k, v) { + if (v instanceof Function) { + var id_ = editor.util.guid(); + tmap[id_] = v.toString(); + return id_; + } else return v + }, 4); + for (var id_ in tmap) { + tstr = tstr.replace('"' + id_ + '"', JSON.stringify(tmap[id_])) + } + input.value = tstr; + } + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + input.onchange(); + } + lastOffset[editor_multi.id] = (codeEditor.getScrollInfo() || {}).top; + // ----- 自动格式化 + _format(); + setvalue(codeEditor.getValue() || ''); + } + + editor_multi.doCommand = function (select) { + var value = select.value; + select.selectedIndex = 0; + if (extraKeys[value]) { + extraKeys[value](codeEditor); + } + } + + editor_multi.openUrl = function (url) { + if (editor.isMobile && !confirm('你确定要离开本页面么?')) return; + window.open(url, '_blank'); + } + + var multiLineArgs = [null, null, null]; + editor_multi.multiLineEdit = function (value, b, f, args, callback) { + editor_multi.id = 'callFromBlockly'; + _setValue(value.split('\\n').join('\n') || ''); + multiLineArgs[0] = b; + multiLineArgs[1] = f; + multiLineArgs[2] = callback; + editor_multi.lintAutocomplete = Boolean(args.lint); + editor_multi.show(); + } + editor_multi.multiLineDone = function (keep) { + if (!multiLineArgs[0] || !multiLineArgs[1] || !multiLineArgs[2]) return; + var newvalue = codeEditor.getValue() || ''; + multiLineArgs[2](newvalue, multiLineArgs[0], multiLineArgs[1]) + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + } + + var _fileValues = [''] + editor_multi.importFile = function (filename) { + editor_multi.id = 'importFile' + _fileValues[0] = filename + _setValue('loading') + editor_multi.show(); + fs.readFile(filename, 'base64', function (e, d) { + if (e) { + _setValue('加载文件失败:\n' + e) + editor_multi.id = '' + return; + } + var str = editor.util.decode64(d) + _setValue(str) + _fileValues[1] = str + }) + } + + editor_multi.writeFileDone = function (keep) { + fs.writeFile(_fileValues[0], editor.util.encode64(codeEditor.getValue() || ''), 'base64', function (err, data) { + if (err) printe('文件写入失败,请手动粘贴至' + _fileValues[0] + '\n' + err); + else { + if (!keep) { + editor_multi.id = ''; + editor_multi.hide(); + } else { + alert('写入成功!'); + } + printf(_fileValues[0] + " 写入成功,F5刷新后生效"); + } + }); + } + + editor_multi.editCommentJs = function (mod) { + var dict = { + loc: '_server/table/comment.js', + enemyitem: '_server/table/comment.js', + floor: '_server/table/comment.js', + tower: '_server/table/data.comment.js', + functions: '_server/table/functions.comment.js', + commonevent: '_server/table/events.comment.js', + plugins: '_server/table/plugins.comment.js', + } + editor_multi.lintAutocomplete = true + editor_multi.setLint() + editor_multi.importFile(dict[mod]) + } + + // 字体大小 + { + const CONFIG_KEY = "editor_multi.fontSize"; + let fontsize = editor.config.get(CONFIG_KEY, 14); + const input = document.getElementById("editor_multi_fontsize"); + const check = document.getElementById("editor_multi_fontweight") + input.value = fontsize; + editor_multi.setFontSize = function () { + const value = Number(input.value); + editor.config.set(CONFIG_KEY, value); + const ele = codeEditor.getWrapperElement() + ele.style.fontSize = `${value}px`; + ele.style.fontWeight = `${check.checked ? 'bold' : 'normal'}` + } + } + + return editor_multi; +} +//editor_multi=editor_multi(); \ No newline at end of file diff --git a/_server/editor_table.js b/_server/editor_table.js new file mode 100644 index 0000000..1e7fcf3 --- /dev/null +++ b/_server/editor_table.js @@ -0,0 +1,602 @@ +editor_table_wrapper = function (editor) { + + editor_table = function () { + + } + + ///////////////////////////////////////////////////////////////////////////// + // HTML模板 + + editor_table.prototype.select = function (value, values) { + if (values.indexOf(value) < 0) values = [value].concat(values); + var content = values.map(function (v) { + return editor.table.option(v, v == value) + }).join('') + return /* html */`\n` + } + editor_table.prototype.option = function (value, selected) { + return /* html */`\n` + } + editor_table.prototype.text = function (value) { + return /* html */`\n` + } + editor_table.prototype.checkbox = function (value) { + return /* html */`\n` + } + editor_table.prototype.textarea = function (value, indent, disable) { + return /* html */`\n` + } + editor_table.prototype.checkboxSet = function (value, keys, prefixStrings) { + if (value == null) value = []; + if (!(value instanceof Array)) { + if (value == 0) value = []; + else value = [value]; + } + keys=Array.from(keys) + prefixStrings=Array.from(prefixStrings) + for (var index = 0; index < value.length; index++) { + if (keys.indexOf(value[index])==-1) { + keys.push(value[index]) + prefixStrings.push('
'+value[index]+': ') + } + } + var content=[] + for (var index = 0; index < keys.length; index++) { + content.push(editor.table.checkboxSetMember(value.indexOf(keys[index])!=-1,keys[index],prefixStrings[index])) + } + return /* html */`
${content.join('')}
\n`; + } + editor_table.prototype.checkboxSetMember = function (value,key,prefixString) { + return /* html */`${prefixString}\n`; + } + editor_table.prototype.editGrid = function (showComment, type) { + var list = []; + if (showComment) list.push(""); + if (type != 'select' && type != 'checkbox' && type != 'checkboxSet' && type != 'popCheckboxSet' && type != 'disable') + list.push(""); + if (type == 'popCheckboxSet') + list.push(""); + if (type == 'disable') list.push(""); + return list.join(' '); + } + + editor_table.prototype.title = function () { + return /* html */`\n条目注释值操作\n` + } + + editor_table.prototype.gap = function (field) { + var tokenlist = field.slice(2, -2).split("']['"); + var rule = tokenlist.join("-"); + tokenlist.pop(); + var self = tokenlist.join("-"); + var status = !!tokenPool[rule]; + return /* html */` + ---- + ---- + ${field} + + \n` + } + + editor_table.prototype.tr = function (guid, field, shortField, commentHTMLescape, cobjstr, shortComment, tdstr, type) { + return /* html */` + ${shortField} + ${shortComment || commentHTMLescape} +
${tdstr}
+ ${editor.table.editGrid(shortComment, type)} + \n` + } + + + /** + * checkboxset中checkbox的onchange + * 这个函数本质是模板editor_table.prototype.checkboxSetMember的一部分 + * 故放在HTML模板分类下 + */ + editor_table.prototype.checkboxSetMemberOnchange = function (onemember) { + var thisset=onemember.parentNode + var inputs=thisset.children + var value=[] + for (var i in inputs) { + if (inputs[i].nodeName == 'INPUT') { + if (inputs[i].checked) { + var one = inputs[i].getAttribute('key'); + if (inputs[i].getAttribute('ctype') == 'number') one = parseFloat(one); + value.push(one); + } + } + } + thiseval = value; + // if (value.length == 0) thiseval = null; + thisset.value=JSON.stringify(thiseval) + thisset.onchange() + } + + + ///////////////////////////////////////////////////////////////////////////// + // 表格生成的控制 + + /** + * 注释对象的默认值 + */ + editor_table.prototype.defaultcobj = { + // 默认是文本域 + _type: 'textarea', + _data: '', + _string: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + return (typeof (thiseval) === typeof ('')) && thiseval[0] === '"'; + }, + // 默认情况下 非对象和数组的视为叶节点 + _leaf: function (args) {//object~[field,cfield,vobj,cobj] + var thiseval = args.vobj; + if (thiseval == null || thiseval == undefined) return true;//null,undefined + if (typeof (thiseval) === typeof ('')) return true;//字符串 + if (Object.keys(thiseval).length === 0) return true;//数字,true,false,空数组,空对象 + return false; + }, + } + + /** + * 把来自数据文件的obj和来自*comment.js的commentObj组装成表格 + * commentObj在无视['_data']的意义下与obj同形 + * 即: commentObj['_data']['a']['_data']['b'] 与 obj['a']['b'] 是对应的 + * 在此意义下, 两者的结构是一致的 + * 在commentObj没有被定义的obj的分支, 会取defaultcobj作为默认值 + * 因此在深度优先遍历时,维护 + * field="['a']['b']" + * cfield="['_data']['a']['_data']['b']" + * vobj=obj['a']['b'] + * cobj=commentObj['_data']['a']['_data']['b'] + * cobj + * cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]) + * 每一项若未定义,就从defaultcobj中取 + * 当其是函数不是具体值时,把args = {field: field, cfield: cfield, vobj: vobj, cobj: cobj}代入算出该值 + * 得到的叶节点的结构如下 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 返回结果 + * 返回一个对象, 假设被命名为tableinfo + * 在把一个 table 的 innerHTML 赋值为 tableinfo.HTML 后 + * 再调 tableinfo.listen(tableinfo.guids) 进行绑定事件 + * @param {Object} obj + * @param {Object} commentObj + * @returns {{"HTML":String,"guids":String[],"listen":Function}} + */ + editor_table.prototype.objToTable = function (obj, commentObj) { + // 表格抬头 + var outstr = [editor.table.title()]; + var guids = []; + var defaultcobj = this.defaultcobj + /** + * 深度优先遍历, p*即为父节点的四个属性 + * @param {String} pfield + * @param {String} pcfield + * @param {Object} pvobj + * @param {Object} pcobj + */ + var recursionParse = function (pfield, pcfield, pvobj, pcobj) { + var keysForTableOrder = {}; + var voidMark = {}; + // 1. 按照pcobj排序生成 + if (pcobj && pcobj['_data']) { + for (var ii in pcobj['_data']) keysForTableOrder[ii] = voidMark; + } + // 2. 对每个pvobj且不在pcobj的,再添加到最后 + keysForTableOrder = Object.assign(keysForTableOrder, pvobj) + for (var ii in keysForTableOrder) { + // 3. 对于pcobj有但是pvobj中没有的, 弹出提示, (正常情况下editor_file会补全成null) + // 事实上能执行到这一步工程没崩掉打不开,就继续吧.. + if (keysForTableOrder[ii] === voidMark) { + if (typeof id_815975ad_ee6f_4684_aac7_397b7e392702 === "undefined") { + // alert('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + console.error('comment和data不匹配,请在群 HTML5造塔技术交流群 959329661 内反馈') + id_815975ad_ee6f_4684_aac7_397b7e392702 = 1; + } + pvobj[ii] = null; + } + var field = pfield + "['" + ii + "']"; + var cfield = pcfield + "['_data']['" + ii + "']"; + var vobj = pvobj[ii]; + var cobj = null; + if (pcobj && pcobj['_data'] && pcobj['_data'][ii]) { + // cobj存在时直接取 + cobj = Object.assign({}, defaultcobj, pcobj['_data'][ii]); + } else { + // 当其函数时代入参数算出cobj, 不存在时只取defaultcobj + if (pcobj && (pcobj['_data'] instanceof Function)) cobj = Object.assign({}, defaultcobj, pcobj['_data'](ii)); + else cobj = Object.assign({}, defaultcobj); + } + var args = { field: field, cfield: cfield, vobj: vobj, cobj: cobj } + // 当cobj的参数为函数时,代入args算出值 + for (var key in cobj) { + if (key === '_data') continue; + if (cobj[key] instanceof Function) cobj[key] = cobj[key](args); + } + pvobj[ii] = vobj = args.vobj; + // 标记为_hide的属性不展示 + if (cobj._hide) continue; + if (!cobj._leaf) { + // 不是叶节点时, 插入展开的标记并继续遍历, 此处可以改成按钮用来添加新项或折叠等 + outstr.push(editor.table.gap(field)); + recursionParse(field, cfield, vobj, cobj); + } else { + // 是叶节点时, 调objToTr_渲染 + var leafnode = editor.table.objToTr(obj, commentObj, field, cfield, vobj, cobj); + outstr.push(leafnode[0]); + guids.push(leafnode[1]); + } + } + } + // 开始遍历 + recursionParse("", "", obj, commentObj); + + var listen = function (guids) { + // 每个叶节点的事件绑定 + var tableid = editor.util.guid(); + editor.mode.currentTable=tableid; + guids.forEach(function (guid) { + editor.table.guidListen(guid, tableid, obj, commentObj) + }); + } + return { "HTML": outstr.join(''), "guids": guids, "listen": listen }; + } + + /** + * 返回叶节点形如 + * tr>td[title=field] + * >td[title=comment,cobj=cobj:json] + * >td>div>input[value=thiseval] + * 参数意义在 objToTable 中已解释 + * @param {Object} obj + * @param {Object} commentObj + * @param {String} field + * @param {String} cfield + * @param {Object} vobj + * @param {Object} cobj + */ + editor_table.prototype.objToTr = function (obj, commentObj, field, cfield, vobj, cobj) { + var guid = editor.util.guid(); + var thiseval = vobj; + var comment = String(cobj._data); + + // var charlength = 15; + // "['a']['b']" => "b" + var shortField = field.split("']").slice(-2)[0].split("['").slice(-1)[0]; + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + // shortField = (shortField.length < charlength ? shortField : shortField.slice(0, charlength) + '...'); + + // 完整的内容转义后供悬停查看 + var commentHTMLescape = editor.util.HTMLescape(comment); + // 把长度超过 charlength 的字符改成 固定长度+...的形式 + // var shortCommentHTMLescape = (comment.length < charlength ? commentHTMLescape : editor.util.HTMLescape(comment.slice(0, charlength)) + '...'); + + var cobjstr = Object.assign({}, cobj); + delete cobjstr._data; + // 把cobj塞到第二个td的[cobj]中, 方便绑定事件时取 + cobjstr = editor.util.HTMLescape(JSON.stringify(cobjstr)); + + var tdstr = editor.table.objToTd(obj, commentObj, field, cfield, vobj, cobj) + var outstr = editor.table.tr(guid, field, shortField, commentHTMLescape, cobjstr, cobj._docs, tdstr, cobj._type) + return [outstr, guid]; + } + + editor_table.prototype.objToTd = function (obj, commentObj, field, cfield, vobj, cobj) { + var thiseval = vobj; + switch (cobj._type) { + case 'select': + return editor.table.select(thiseval, cobj._select.values); + case 'checkbox': + return editor.table.checkbox(thiseval); + case 'checkboxSet': + return editor.table.checkboxSet(thiseval, cobj._checkboxSet.key, cobj._checkboxSet.prefix); + default: + return editor.table.textarea(thiseval, cobj.indent || 0, cobj._type == 'disable'); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // 表格的用户交互 + + /** + * 检查一个值是否允许被设置为当前输入 + * @param {Object} cobj + * @param {*} thiseval + */ + editor_table.prototype.checkRange = function (cobj, thiseval) { + if (cobj._range) { + return eval(cobj._range); + } + if (cobj._select) { + return cobj._select.values.indexOf(thiseval) !== -1; + } + if (cobj._bool) { + return [true, false].indexOf(thiseval) !== -1; + } + return true; + } + + /** + * 监听一个guid对应的表格项 + * @param {String} guid + */ + editor_table.prototype.guidListen = function (guid, tableid, obj, commentObj) { + // tr>td[title=field] + // >td[title=comment,cobj=cobj:json] + // >td>div>input[value=thiseval] + var thisTr = document.getElementById(guid); + var input = thisTr.children[2].children[0].children[0]; + var field = thisTr.children[0].getAttribute('title'); + var cobj = JSON.parse(thisTr.children[1].getAttribute('cobj')); + var modeNode = thisTr.parentNode; + thisTr.setAttribute('tableid',tableid) + while (!editor_mode._ids.hasOwnProperty(modeNode.getAttribute('id'))) { + modeNode = modeNode.parentNode; + } + input.onchange = function () { + editor.table.onchange(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + // 用检测两次单击的方式来实现双击(以支持手机端的双击) + var doubleClickCheck = [0]; + thisTr.onclick = function () { + var newClick = new Date().getTime(); + var lastClick = doubleClickCheck.shift(); + doubleClickCheck.push(newClick); + if (newClick - lastClick < 500) { + editor.table.dblclickfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + } + + /** + * 表格的值变化时 + */ + editor_table.prototype.onchange = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + if (editor.mode.currentTable!=thisTr.getAttribute('tableid')) return; + var thiseval = null; + if (input.checked != null) input.value = input.checked; + try { + if (input.value == '') input.value = 'null'; + thiseval = JSON.parse(input.value); + } catch (ee) { + printe(field + ' : ' + ee); + throw ee; + } + if (editor.table.checkRange(cobj, thiseval)) { + editor_mode.addAction(['change', field, thiseval]); + editor_mode.onmode('save');//自动保存 删掉此行的话点保存按钮才会保存 + } else { + printe(field + ' : 输入的值不合要求,请鼠标放置在注释上查看说明'); + } + } + + var tokenPool = {}; + var tokenstyle = document.createElement("style"); + document.body.appendChild(tokenstyle); + + var tokenPoolRender = function() { + var content = ""; + Object.keys(tokenPool).forEach(function(k) { + content += /* CSS */`[data-field|=${k}]{ display: none }`; + }) + tokenstyle.innerHTML = content; + } + + /** + * 当"折叠"被按下时 + */ + editor_table.prototype.onFoldBtnClick = function (button) { + var tr = button.parentNode.parentNode; + if (button.dataset.fold == "true") { + delete tokenPool[tr.dataset.gap]; + tokenPoolRender(); + button.dataset.fold = "false"; + button.innerText = "折叠"; + } else { + tokenPool[tr.dataset.gap] = true; + tokenPoolRender(); + button.dataset.fold = "true"; + button.innerText = "展开"; + } + } + + /** + * 当"显示完整注释"被按下时 + */ + editor_table.prototype.onCommentBtnClick = function (button) { + var tr = button.parentNode.parentNode; + printf(tr.children[1].getAttribute('title')); + } + + /** + * 当"编辑表格内容"被按下时 + */ + editor_table.prototype.onEditBtnClick = function (button) { + var tr = button.parentNode.parentNode; + var guid = tr.getAttribute('id'); + var cobj = JSON.parse(tr.children[1].getAttribute('cobj')); + var input = tr.children[2].children[0].children[0]; + if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); + if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string, template: cobj._template, preview: cobj._preview }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); + if (cobj._type === 'color') editor.table.selectColor(input); + if (cobj._type === 'point') editor.table.selectPoint(input); + if (cobj._type === 'popCheckboxSet') editor.table.popCheckboxSet(input, cobj); + } + + editor_table.prototype.onCopyBtnClick = function (button) { + var tr = button.parentNode.parentNode; + var input = tr.children[2].children[0].children[0]; + var value = JSON.parse(input.value); + if (value == null) { + printe('没有赋值的内容'); + return; + } + if (core.copy(value.toString())) { + printf('复制成功!'); + } else { + printe('无法复制此内容,请手动选择复制'); + } + } + + /** + * 双击表格时 + * 正常编辑: 尝试用事件编辑器或多行文本编辑器打开 + * 添加: 在该项的同一级创建一个内容为null新的项, 刷新后生效并可以继续编辑 + * 删除: 删除该项, 刷新后生效 + * 在点击按钮 添加/删除 后,下一次双击将被视为 添加/删除 + */ + editor_table.prototype.dblclickfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + if (editor_mode.doubleClickMode === 'change') { + if (cobj._type === 'event') editor_blockly.import(guid, { type: cobj._event }); + if (cobj._type === 'textarea') editor_multi.import(guid, { lint: cobj._lint, string: cobj._string, template: cobj._template, preview: cobj._preview }); + if (cobj._type === 'material') editor.table.selectMaterial(input, cobj); + if (cobj._type === 'color') editor.table.selectColor(input); + if (cobj._type === 'point') editor.table.selectPoint(input); + if (cobj._type === 'popCheckboxSet') editor.table.popCheckboxSet(input, cobj); + } else if (editor_mode.doubleClickMode === 'add') { + editor_mode.doubleClickMode = 'change'; + editor.table.addfunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } else if (editor_mode.doubleClickMode === 'delete') { + editor_mode.doubleClickMode = 'change'; + editor.table.deletefunc(guid, obj, commentObj, thisTr, input, field, cobj, modeNode) + } + } + + editor_table.prototype.selectMaterial = function (input, cobj) { + editor.uievent.selectMaterial(input.value, cobj._docs || cobj._data || '请选择素材', cobj._directory, function (one) { + if (!/^[-A-Za-z0-9_.]+$/.test(one)) return null; + if (cobj._transform) return eval("("+cobj._transform+")(one)"); + return one; + }, function (data) { + input.value = JSON.stringify(cobj._onconfirm ? eval("("+cobj._onconfirm+")(JSON.parse(input.value), data)") : data); + input.onchange(); + }) + } + + editor_table.prototype.selectColor = function (input) { + if (input.value != null) { + var str = input.value.toString().replace(/[^\d.,]/g, ''); + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(str)) { + document.getElementById('colorPicker').value = str; + } + } + var boundingBox = input.getBoundingClientRect(); + openColorPicker(boundingBox.x, boundingBox.y + boundingBox.height, function (value) { + value = value.replace(/[^\d.,]/g, ''); + input.value = '[' + value +']'; + input.onchange(); + }) + } + + editor_table.prototype.selectPoint = function (input) { + var x = 0, y = 0, value = input.value; + if (value != null) { + try { + var loc = JSON.parse(value); + if (loc instanceof Array && loc.length == 2) { + x = loc[0]; + y = loc[1]; + } + } catch (e) {} + } + editor.uievent.selectPoint(editor.currentFloorId, x, y, false, function (floorId, x, y) { + input.value = '['+x+','+y+']'; + input.onchange(); + }) + } + + editor_table.prototype.popCheckboxSet = function (input, cobj) { + editor.uievent.popCheckboxSet(JSON.parse(input.value), cobj._checkboxSet, cobj._docs || cobj._data || '请选择多选项', function (value) { + input.value = JSON.stringify(value); + input.onchange(); + }) + } + + /** + * 删除表格项 + */ + editor_table.prototype.deletefunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + if (editor.table.checkRange(cobj, null)) { + editor_mode.addAction(['delete', field, undefined]); + editor_mode.onmode('save', function () { + printf('删除成功,刷新后生效。') + }); + } else { + printe(field + ' : 该值不允许为null,无法删除'); + } + } + + /** + * 添加表格项 + */ + editor_table.prototype.addfunc = function (guid, obj, commentObj, thisTr, input, field, cobj, modeNode) { + if (modeNode) { + editor_mode.onmode(editor_mode._ids[modeNode.getAttribute('id')]); + } + + var mode = editor.dom.editModeSelect.value; + var supportText = mode === 'commonevent' || mode === 'plugins'; + + if (obj == null) { + if (mode === 'commonevent') obj = events_c12a15a8_c380_4b28_8144_256cba95f760; + else if (mode === 'plugins') obj = plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1; + else return; + } + + // 1.输入id + var newid = '2'; + if (mode == 'loc') { + var ae = editor.currentFloorData.autoEvent[editor_mode.pos.x + ',' + editor_mode.pos.y]; + if (ae != null) { + var testid; + for (testid = 2; Object.hasOwnProperty.call(ae, testid); testid++); // 从3开始是因为comment中设置了始终显示012 + newid = testid + ''; + } + } else { + newid = prompt(supportText ? '请输入新项的ID(支持中文)' : '请输入新项的ID(数字字母下划线)'); + if (newid == null || newid.length == 0) { + return; + } + } + + // 2.检查id是否符合规范或与已有id重复 + if (!supportText) { + if (!/^[a-zA-Z0-9_]+$/.test(newid)) { + printe('id不符合规范, 请使用大小写字母数字下划线来构成'); + return; + } + } + + var conflict = true; + var basefield = (field || "").replace(/\[[^\[]*\]$/, ''); + if (basefield === "['main']") { + printe("全塔属性 ~ ['main'] 不允许添加新值"); + return; + } + try { + var baseobj = eval('obj' + basefield); + conflict = newid in baseobj; + } catch (ee) { + // 理论上这里不会发生错误 + printe(ee); + throw ee; + } + if (conflict) { + printe('id已存在, 请直接修改该项的值'); + return; + } + // 3.添加 + editor_mode.addAction(['add', basefield + "['" + newid + "']", null]); + editor_mode.onmode('save', function () { + printf('添加成功,刷新后生效;也可以继续新增其他项目。') + });//自动保存 删掉此行的话点保存按钮才会保存 + } + + ///////////////////////////////////////////////////////////////////////////// + editor.constructor.prototype.table = new editor_table(); +} +//editor_table_wrapper(editor); \ No newline at end of file diff --git a/_server/editor_ui.js b/_server/editor_ui.js new file mode 100644 index 0000000..f3431eb --- /dev/null +++ b/_server/editor_ui.js @@ -0,0 +1,339 @@ +editor_ui_wrapper = function (editor) { + + var tip=document.getElementById('tip'); + var print = function (msg, cls) { + if (msg == '') { + tip.innerHTML = ''; + return; + } + tip.innerHTML = '

' + msg + "

"; + } + + window.printf = function (msg) { + selectBox.isSelected(false); + print(msg, 'successText'); + } + window.printe = function (msg) { + selectBox.isSelected(false); + print(msg, 'warnText'); + } + window.printi = function (msg) { + print(msg, 'infoText'); + } + + editor.uifunctions.showBlockInfo = function (value) { + if (value == 0) { + printi("当前选择为清除块,可擦除地图上块"); + return; + } + var hasId = 'id' in value; + if (hasId && value.idnum == 17) { + printi("当前选择为空气墙, 在编辑器中可视, 在游戏中隐藏的墙, 用来配合前景/背景的贴图"); + return; + } + var isAutotile = hasId && value.images == "autotile"; + tip.innerHTML = (hasId?`

图块编号:${ value['idnum'] }

+

图块ID:${ value['id'] }

`:` +

该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!

`)+` +

图块所在素材:${ value['images'] + (isAutotile ? '( '+value['id']+' )' : '') } +

+

图块索引:${ value['y'] }

`; + } + + editor.uifunctions.showTips = function (value) { + var tips = [ + '表格的文本域可以双击进行编辑', + '双击地图可以选中素材,右键可以弹出菜单', + '双击事件编辑器的图块可以进行长文本编辑/脚本编辑/地图选点/UI绘制预览等操作', + 'ESC或点击空白处可以自动保存当前修改', + 'H键可以打开操作帮助哦', + 'tileset平铺模式可以在地图上拖动来平铺框选的图形', + '可以拖动地图上的图块和事件;或按Ctrl+C, Ctrl+X和Ctrl+V进行复制,剪切和粘贴,Delete删除;右键也可以拉框选择区域', + 'Alt+数字键保存图块,数字键读取保存的图块', + ]; + if (value == null) value = Math.floor(Math.random() * tips.length); + printf('tips: ' + tips[value]) + } + + /** + * 根据鼠标点击, 得到从元素向上到body的所有id + */ + editor.uifunctions.getClickpath = function (e) { + //console.log(e); + var clickpath = []; + var getpath = function (e) { + var path = []; + var currentElem = e.target; + while (currentElem) { + path.push(currentElem); + currentElem = currentElem.parentElement; + } + if (path.indexOf(window) === -1 && path.indexOf(document) === -1) + path.push(document); + if (path.indexOf(window) === -1) + path.push(window); + return path; + } + getpath(e).forEach(function (node) { + if (!node.getAttribute) return; + var id_ = node.getAttribute('id'); + if (id_) { + if (['left', 'left1', 'left2', 'left3', 'left4', 'left5', 'left8', 'mobileview'].indexOf(id_) !== -1) clickpath.push('edit'); + clickpath.push(id_); + } + }); + return clickpath; + } + + /** + * editor.dom.body.onmousedown + * 检测鼠标点击, + * + 如果选中了绘图区域之外, 就保存地图 + * + 维护绘图区的菜单的隐藏 + * + 记录最后一次点击的id(主要为了数据区服务) + */ + editor.uifunctions.body_click = function (e) { + var clickpath = editor.uifunctions.getClickpath(e); + + var unselect = true; + for (var ii = 0, thisId; thisId = ['edit', 'tip', 'brushMod', 'brushMod2', 'brushMod3', 'brushMode4', 'layerMod', 'layerMod2', 'layerMod3', 'viewportButtons'][ii]; ii++) { + if (clickpath.indexOf(thisId) !== -1) { + unselect = false; + break; + } + } + if (unselect && !editor.uivalues.lockMode) { + if (clickpath.indexOf('eui') === -1 && clickpath.indexOf('lastUsed') === -1) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + editor.uifunctions.unhighlightSaveFloorButton(); + }); + } + selectBox.isSelected(false); + editor.info = {}; + } + } + //editor.mode.onmode(''); + if (e.button != 2 && !editor.isMobile && clickpath.indexOf('midMenu') === -1) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.indexOf('down') !== -1 && clickpath.indexOf('midMenu') === -1 && editor.isMobile) { + editor.uifunctions.hideMidMenu(); + } + if (clickpath.length >= 2 && clickpath[0].indexOf('id_') === 0) { editor.lastClickId = clickpath[0] } + } + + /** + * editor.dom.body.onkeydown + * 绑定快捷键 + */ + editor.uifunctions.body_shortcut = function (e) { + editor.uivalues.tileSize = [1,1]; + + // UI预览 & 地图选点 + if (editor.uievent && editor.uievent.isOpen) { + editor.uievent.onKeyDown(e); + return; + } + + // 监听Ctrl+S保存 + if (e.ctrlKey && e.keyCode == 83) { + e.preventDefault(); + if (editor_multi.id != "") { + editor_multi.confirm(); // 保存脚本编辑器 + } + else if (editor_blockly.id != "") { + editor_blockly.confirm(); // 保存事件编辑器 + } + else { + editor_mode.saveFloor(); + } + return; + } + + // 如果是开启事件/脚本编辑器状态,则忽略 + if (editor_multi.id != "" || editor_blockly.id != "") + return; + + // PGUP和PGDOWN切换楼层 + if (e.keyCode == 33 || e.keyCode == 34) { + e.preventDefault(); + var saveFloor = document.getElementById('saveFloor'); + if (saveFloor && saveFloor.classList.contains('highlight')) { + return; + } + + var index = editor.core.floorIds.indexOf(editor.currentFloorId); + var nextIndex = index + (e.keyCode == 33 ? 1 : -1); + if (nextIndex >= 0 && nextIndex < editor.core.floorIds.length) { + var toId = editor.core.floorIds[nextIndex]; + editor_mode.onmode('nextChange'); + editor_mode.onmode('floor'); + document.getElementById('selectFloor').value = toId; + editor.uivalues.recentFloors.push(editor.currentFloorId); + editor.changeFloor(toId); + } + return; + } + + var focusElement = document.activeElement; + if (!focusElement || focusElement.tagName.toLowerCase() == 'body' + || focusElement.id == 'selectFloor' || focusElement.id == 'bigmapBtn' + || focusElement.id.startsWith('layerMod')) { + + //Ctrl+z 撤销上一步undo + if (e.keyCode == 90 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.preMapData.length > 0) { + var data = editor.uivalues.preMapData.pop(); + editor.dom.maps.forEach(function (one) { + editor[one] = JSON.parse(JSON.stringify(data[one])); + }); + editor.updateMap(); + editor.uivalues.postMapData.push(data); + editor.uifunctions.highlightSaveFloorButton(); + printf("已撤销此操作,你可能需要重新保存地图。"); + } + return; + } + //Ctrl+y 重做一步redo + if (e.keyCode == 89 && e.ctrlKey) { + e.preventDefault(); + if (editor.uivalues.postMapData.length > 0) { + var data = editor.uivalues.postMapData.pop(); + editor.dom.maps.forEach(function (one) { + editor[one] = JSON.parse(JSON.stringify(data[one])); + }); + editor.updateMap(); + editor.uivalues.preMapData.push(data); + editor.uifunctions.highlightSaveFloorButton(); + printf("已重做此操作,你可能需要重新保存地图。"); + } + return; + } + + // Ctrl+C, Ctrl+X, Ctrl+V + if (e.ctrlKey && e.keyCode == 67 && !selectBox.isSelected()) { + e.preventDefault(); + editor.uivalues.copyedInfo = editor.copyFromPos(editor.uivalues.selectedArea); + printf('该点事件已复制;请注意右键地图拉框可以复制一个区域;若有时复制失灵请多点几下空白处'); + return; + } + if (e.ctrlKey && e.keyCode == 88 && !selectBox.isSelected()) { + e.preventDefault(); + editor.savePreMap(); + editor.uivalues.copyedInfo = editor.copyFromPos(editor.uivalues.selectedArea); + editor.clearPos(true, editor.uivalues.selectedArea, function () { + printf('该点事件已剪切;请注意右键地图拉框可以剪切一个区域;若有时剪切失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + }) + return; + } + if (e.ctrlKey && e.keyCode == 86 && !selectBox.isSelected()) { + e.preventDefault(); + if (!editor.uivalues.copyedInfo) { + printe("没有复制的事件"); + return; + } + editor.savePreMap(); + editor.pasteToPos(editor.uivalues.copyedInfo); + editor.updateMap(); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('粘贴事件成功;若有时粘贴失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + editor.drawPosSelection(); + }); + return; + } + // DELETE + if (e.keyCode == 46 && !selectBox.isSelected()) { + editor.savePreMap(); + editor.clearPos(true, editor.uivalues.selectedArea, function () { + printf('该点事件已删除;请注意右键地图拉框可以删除一个区域;;若有时删除失灵请多点几下空白处'); + editor.uifunctions.unhighlightSaveFloorButton(); + }) + return; + } + // ESC + if (e.keyCode == 27) { + if (selectBox.isSelected()) { + editor_mode.onmode(''); + editor.file.saveFloorFile(function (err) { + if (err) { + printe(err); + throw (err) + } + ; printf('地图保存成功'); + }); + } + selectBox.isSelected(false); + editor.info = {}; + return; + } + //alt + 0~9 改变快捷图块 + if (e.altKey && [48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + var infoToSave = JSON.stringify(editor.info || 0); + if (infoToSave == JSON.stringify({})) return; + editor.uivalues.shortcut[e.keyCode] = JSON.parse(infoToSave); + printf('已保存该快捷图块, 数字键 ' + (e.keyCode - 48) + ' 使用.') + editor.config.set('shortcut', editor.uivalues.shortcut); + return; + } + //ctrl + 0~9 切换到快捷图块 + if ([48, 49, 50, 51, 52, 53, 54, 55, 56, 57].indexOf(e.keyCode) !== -1) { + editor.setSelectBoxFromInfo(JSON.parse(JSON.stringify(editor.uivalues.shortcut[e.keyCode] || 0))); + return; + } + switch (e.keyCode) { + // WASD + case 87: editor.moveViewport(0, -1); break; + case 65: editor.moveViewport(-1, 0); break; + case 83: editor.moveViewport(0, 1); break; + case 68: editor.moveViewport(1, 0); break; + // F + case 70: editor.uifunctions.triggerBigmap(); break; + // Z~. + case 90: editor_mode.change('map'); break; // Z + case 88: editor_mode.change('loc'); break; // X + case 67: editor_mode.change('enemyitem'); break; // C + case 86: editor_mode.change('floor'); break; // V + case 66: editor_mode.change('tower'); break; // B + case 78: editor_mode.change('functions'); break; // N + case 77: editor_mode.change('appendpic'); break; // M + case 188: editor_mode.change('commonevent'); break; // , + case 190: editor_mode.change('plugins'); break; // . + // H + case 72: editor.uifunctions.showHelp(); break; + } + return; + } + } + + editor.uifunctions.showHelp = function () { + alert( + "快捷操作帮助:\n" + + "ESC / 点击空白处:自动保存当前修改\n" + + "F:切换大地图\n" + + "WASD / 长按箭头:平移大地图\n" + + "PgUp, PgDn / 鼠标滚轮:上下切换楼层\n" + + "Z~.(键盘的第三排):快捷切换标签\n" + + "双击地图:选中对应点的素材\n" + + "右键地图:弹出菜单栏\n" + + "Alt+0~9:保存当前使用的图块\n" + + "0~9:选中保存的图块\n" + + "Ctrl+Z / Ctrl+Y:撤销/重做上次绘制\n" + + "Ctrl+S:事件与脚本编辑器的保存并退出\n" + + "双击事件编辑器:长文本编辑/脚本编辑/地图选点/UI绘制预览" + ); + } +} \ No newline at end of file diff --git a/_server/editor_uievent.js b/_server/editor_uievent.js new file mode 100644 index 0000000..6f064fb --- /dev/null +++ b/_server/editor_uievent.js @@ -0,0 +1,1068 @@ +editor_uievent_wrapper = function (editor) { + + // ------ UI预览 & 地图选点相关 ------ // + + var uievent = { + elements: {}, + values: {}, + isOpen: false, + mode: "" + }; + + uievent.elements.div = document.getElementById('uieventDiv'); + uievent.elements.title = document.getElementById('uieventTitle'); + uievent.elements.yes = document.getElementById('uieventYes'); + uievent.elements.no = document.getElementById('uieventNo'); + uievent.elements.select = document.getElementById('uieventSelect'); + uievent.elements.selectPoint = document.getElementById('selectPoint'); + uievent.elements.selectFloor = document.getElementById('selectPointFloor'); + uievent.elements.selectPointBox = document.getElementById('selectPointBox'); + uievent.elements.body = document.getElementById('uieventBody'); + uievent.elements.selectPointButtons = document.getElementById('selectPointButtons'); + uievent.elements.canvas = document.getElementById('uievent'); + uievent.elements.extraBody = document.getElementById('uieventExtraBody'); + + uievent.close = function () { + uievent.isOpen = false; + uievent.elements.div.style.display = 'none'; + if (uievent.values.interval) { + clearTimeout(uievent.values.interval); + clearInterval(uievent.values.interval); + } + uievent.values = {}; + } + uievent.elements.no.onclick = uievent.close; + + uievent.drawPreviewUI = function () { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + core.setFilter('uievent', null); + + // 绘制UI + var background = uievent.elements.select.value; + if (background == 'thumbnail') { + core.drawThumbnail(editor.currentFloorId, null, { ctx: 'uievent' }); + } + else { + core.fillRect('uievent', 0, 0, core.__PIXELS__, core.__PIXELS__, background); + } + + if (uievent.values.list instanceof Array) { + uievent.values.list.forEach(function (data) { + if (typeof data == 'string') data = { "type": "text", "text": data }; + var type = data.type; + if (type == "text") { + data.ctx = 'uievent'; + core.saveCanvas('uievent'); + core.drawTextBox(data.text, data); + core.loadCanvas('uievent'); + return; + } + else if (type == "choices") { + for (var i = 0; i < data.choices.length; i++) { + if (typeof data.choices[i] === 'string') + data.choices[i] = { "text": data.choices[i] }; + data.choices[i].text = core.replaceText(data.choices[i].text); + } + core.saveCanvas('uievent'); + core.status.event.selection = data.selected || 0; + core.drawChoices(core.replaceText(data.text), data.choices, data.width, 'uievent'); + core.status.event.selection = null; + core.loadCanvas('uievent'); + return; + } else if (type == "confirm") { + core.saveCanvas('uievent'); + core.drawConfirmBox(data.text, null, null, 'uievent'); + core.loadCanvas('uievent'); + } else if (core.ui["_uievent_" + type]) + core.ui["_uievent_" + type](data); + }) + } + } + + uievent.previewUI = function (list) { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'previewUI'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = 'UI绘制预览'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.extraBody.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + + uievent.elements.select.innerHTML = + '' + + '' + + ''; + uievent.elements.select.onchange = function () { + uievent.drawPreviewUI(); + } + + uievent.values.list = list; + uievent.drawPreviewUI(); + } + + uievent.selectPoint = function (floorId, x, y, bigmap, callback) { + uievent.values.bigmap = bigmap; + uievent.values.size = editor.isMobile ? window.innerWidth / core.__SIZE__ : 32 * 540 / core.__PIXELS__; + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectPoint'; + uievent.elements.selectPoint.style.display = 'block'; + uievent.elements.yes.style.display = 'inline'; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'inline'; + uievent.elements.selectPointBox.style.display = 'block'; + uievent.elements.canvas.style.display = 'block'; + uievent.elements.extraBody.style.display = 'none'; + uievent.elements.body.style.overflow = "hidden"; + uievent.elements.yes.onclick = function () { + var floorId = uievent.values.floorId, x = uievent.values.x, y = uievent.values.y; + var multipoints = uievent.values.multipoints || []; + uievent.close(); + if (callback) { + if (multipoints.length > 0) { + callback(floorId, multipoints.map(function (one) { return one.split(',')[0] }).join(','), + multipoints.map(function (one) { return one.split(',')[1] }).join(',')); + } else { + callback(floorId, x, y); + } + } + } + + // Append children + var floors = ""; + core.floorIds.forEach(function (f) { + floors += ""; + }) + uievent.elements.selectFloor.innerHTML = floors; + // 检查多选点 + if (/^\d+(,\d+)+$/.test(x) && /^\d+(,\d+)+$/.test(y)) { + var xx = x.split(','), yy = y.split(','); + uievent.values.multipoints = []; + for (var i = 0; i < xx.length; ++i) { + uievent.values.multipoints.push(xx[i] + "," + yy[i]); + } + x = xx[xx.length - 1]; + y = yy[yy.length - 1]; + } + this.setPoint(floorId || editor.currentFloorId, core.calValue(x) || 0, core.calValue(y) || 0); + } + + uievent.updateSelectPoint = function (redraw) { + uievent.elements.title.innerText = '地图选点【右键多选】 (' + uievent.values.x + "," + uievent.values.y + ')'; + // 计算size + uievent.values.boxSize = uievent.values.size * + (uievent.values.bigmap ? (core.__SIZE__ / Math.max(uievent.values.width, uievent.values.height)) : 1); + uievent.values.boxLeft = uievent.values.bigmap ? + (core.__PIXELS__ * Math.max(0, (1 - uievent.values.width / uievent.values.height) / 2)) : 0; + uievent.values.boxTop = uievent.values.bigmap ? + (core.__PIXELS__ * Math.max(0, (1 - uievent.values.height / uievent.values.width) / 2)) : 0; + + if (uievent.values.bigmap) { + uievent.elements.selectPointBox.style.left = uievent.values.boxSize * uievent.values.x + uievent.values.boxLeft + "px"; + uievent.elements.selectPointBox.style.top = uievent.values.boxSize * uievent.values.y + uievent.values.boxTop + "px"; + } else { + uievent.elements.selectPointBox.style.left = uievent.values.boxSize * (uievent.values.x - uievent.values.left) + "px"; + uievent.elements.selectPointBox.style.top = uievent.values.boxSize * (uievent.values.y - uievent.values.top) + "px"; + } + uievent.elements.selectPointBox.style.width = uievent.values.boxSize - 6 + "px"; + uievent.elements.selectPointBox.style.height = uievent.values.boxSize - 6 + "px"; + + if (redraw) { + core.setAlpha('uievent', 1); + core.clearMap('uievent'); + core.drawThumbnail(uievent.values.floorId, null, { + ctx: 'uievent', centerX: uievent.values.left + core.__HALF_SIZE__, + centerY: uievent.values.top + core.__HALF_SIZE__, all: uievent.values.bigmap + }); + uievent.values.multipoints = uievent.values.multipoints || []; + core.setTextAlign('uievent', 'right'); + for (var i = 0; i < uievent.values.multipoints.length; ++i) { + var xy = uievent.values.multipoints[i].split(","), x = parseInt(xy[0]), y = parseInt(xy[1]); + core.fillBoldText('uievent', i + 1, + 32 * (x - uievent.values.left) + 28, 32 * (y - uievent.values.top) + 26, '#FF7F00', null, '14px Verdana'); + } + core.setTextAlign('uievent', 'left'); + } + } + + uievent.setPoint = function (floorId, x, y) { + if (core.floorIds.indexOf(floorId) == -1) floorId = editor.currentFloorId; + uievent.values.floorId = floorId; + uievent.elements.selectFloor.value = floorId; + uievent.values.x = x != null ? x : (uievent.values.x || 0); + uievent.values.y = y != null ? y : (uievent.values.y || 0); + uievent.values.width = core.floors[uievent.values.floorId].width || core.__SIZE__; + uievent.values.height = core.floors[uievent.values.floorId].height || core.__SIZE__; + uievent.values.left = core.clamp(uievent.values.x - core.__HALF_SIZE__, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.y - core.__HALF_SIZE__, 0, uievent.values.height - core.__SIZE__); + uievent.updateSelectPoint(true); + } + + uievent.elements.selectFloor.onchange = function () { + uievent.values.multipoints = []; + uievent.setPoint(uievent.elements.selectFloor.value); + } + + uievent.elements.selectPointBox.onclick = function (e) { + e.preventDefault(); + e.stopPropagation(); + return false; + } + + uievent.elements.body.onclick = function (e) { + if (uievent.mode != 'selectPoint') return; + if (uievent.values.bigmap) { + uievent.values.x = core.clamp(Math.floor((e.offsetX - uievent.values.boxLeft) / uievent.values.boxSize), 0, uievent.values.width - 1); + uievent.values.y = core.clamp(Math.floor((e.offsetY - uievent.values.boxTop) / uievent.values.boxSize), 0, uievent.values.height - 1); + } else { + uievent.values.x = uievent.values.left + Math.floor(e.offsetX / uievent.values.size); + uievent.values.y = uievent.values.top + Math.floor(e.offsetY / uievent.values.size); + } + uievent.updateSelectPoint(false); + } + + uievent.elements.body.oncontextmenu = function (e) { + e.preventDefault(); + e.stopPropagation(); + if (uievent.mode != 'selectPoint' || uievent.values.bigmap) return; + var x = uievent.values.left + Math.floor(e.offsetX / uievent.values.size); + var y = uievent.values.top + Math.floor(e.offsetY / uievent.values.size); + uievent.values.multipoints = uievent.values.multipoints || []; + if (uievent.values.multipoints.indexOf(x + "," + y) >= 0) { + uievent.values.multipoints = uievent.values.multipoints.filter(function (o) { return o != x + "," + y; }) + } else { + uievent.values.multipoints.push(x + "," + y); + } + uievent.values.x = x; + uievent.values.y = y; + uievent.updateSelectPoint(true); + return false; + } + + uievent.move = function (dx, dy) { + if (uievent.mode != 'selectPoint') return; + if (uievent.values.bigmap) return; + uievent.values.left = core.clamp(uievent.values.left + dx, 0, uievent.values.width - core.__SIZE__); + uievent.values.top = core.clamp(uievent.values.top + dy, 0, uievent.values.height - core.__SIZE__); + this.updateSelectPoint(true); + }; + + uievent.triggerBigmap = function () { + if (uievent.mode != 'selectPoint') return; + uievent.values.bigmap = !uievent.values.bigmap; + uievent.values.multipoints = []; + uievent.setPoint(uievent.values.floorId); + }; + + (function () { + + var viewportButtons = uievent.elements.selectPointButtons; + var pressTimer = null; + for (var ii = 0, node; node = viewportButtons.children[ii]; ii++) { + if (ii == 4) { + node.onclick = uievent.triggerBigmap; + continue; + } + if (ii == 5) { + node.onclick = function () { + alert(core.copy(uievent.values.floorId) ? ('楼层ID ' + uievent.values.floorId + ' 已成功复制到剪切板') : '无法复制楼层ID'); + } + } + (function (x, y) { + var move = function () { + uievent.move(x, y); + } + node.onmousedown = function () { + clearTimeout(pressTimer); + pressTimer = setTimeout(function () { + pressTimer = -1; + var f = function () { + if (pressTimer != null) { + move(); + setTimeout(f, 150); + } + } + f(); + }, 500); + }; + node.onmouseup = function () { + if (pressTimer > 0) { + clearTimeout(pressTimer); + move(); + } + pressTimer = null; + } + })([-1, 0, 0, 1][ii], [0, -1, 1, 0][ii]); + } + })(); + + uievent.elements.div.onmousewheel = function (e) { + if (uievent.mode != 'selectPoint') return; + var index = core.floorIds.indexOf(uievent.values.floorId); + try { + if (e.wheelDelta) + index += Math.sign(e.wheelDelta); + else if (e.detail) + index += Math.sign(e.detail); + } catch (ee) { console.error(ee) } + index = core.clamp(index, 0, core.floorIds.length - 1); + uievent.values.multipoints = []; + uievent.setPoint(core.floorIds[index]); + } + + uievent.onKeyDown = function (e) { + if (e.keyCode == 27) editor.uievent.close(); + if (uievent.mode == 'selectPoint') { + if (e.keyCode == 87) editor.uievent.move(0, -1) + if (e.keyCode == 65) editor.uievent.move(-1, 0) + if (e.keyCode == 83) editor.uievent.move(0, 1); + if (e.keyCode == 68) editor.uievent.move(1, 0); + } + } + + // ------ 搜索变量出现的位置,也放在uievent好了 ------ // + + uievent.searchUsedFlags = function () { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'searchUsedFlags'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = '搜索变量'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + // build flags + var html = ""; + Object.keys(editor.used_flags).sort().forEach(function (v) { + v = "flag:" + v; + html += ""; + }); + uievent.elements.select.innerHTML = html; + uievent.elements.select.onchange = uievent.doSearchUsedFlags; + + uievent.doSearchUsedFlags(); + } + + uievent.doSearchUsedFlags = function () { + var flag = uievent.elements.select.value; + + var html = "

该变量出现的所有位置如下:

    "; + var list = uievent._searchUsedFlags(flag); + list.forEach(function (x) { + html += "
  • " + x + "
  • "; + }); + html += "
"; + uievent.elements.extraBody.innerHTML = html; + } + + var hasUsedFlags = function (obj, flag) { + if (obj == null) return false; + if (typeof obj != 'string') return hasUsedFlags(JSON.stringify(obj), flag); + + var index = -1, length = flag.length; + while (true) { + index = obj.indexOf(flag, index + 1); + if (index < 0) return false; + if (!/^[a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]$/.test(obj.charAt(index + length))) return true; + } + } + + uievent._searchUsedFlags = function (flag) { + var list = []; + // 每个点的事件 + var events = ["events", "autoEvent", "changeFloor", "beforeBattle", "afterBattle", "afterGetItem", "afterOpenDoor"] + for (var floorId in core.floors) { + var floor = core.floors[floorId]; + if (hasUsedFlags(floor.firstArrive, flag)) list.push([floorId, "firstArrive"]); + if (hasUsedFlags(floor.eachArrive, flag)) list.push([floorId, "eachArrive"]); + events.forEach(function (e) { + if (floor[e]) { + for (var loc in floor[e]) { + if (hasUsedFlags(floor[e][loc], flag)) { + list.push(floorId + " 层 " + e + " 的 (" + loc + ") 点"); + } + } + } + }); + } + // 公共事件 + for (var name in events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent) { + if (hasUsedFlags(events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent[name], flag)) + list.push("公共事件 " + name); + } + // 道具 & 装备属性 + for (var id in items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a) { + var item = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a[id]; + // 装备属性 + if (hasUsedFlags(item.equip, flag)) { + list.push("道具 " + (item.name || id) + " 的装备属性"); + } + // 使用事件 + if (hasUsedFlags(item.useItemEvent, flag)) { + list.push("道具 " + (item.name || id) + " 的使用事件"); + } + } + // 怪物战前 & 战后 + for (var id in enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80) { + var enemy = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80[id]; + if (hasUsedFlags(enemy.beforeBattle, flag)) { + list.push("怪物 " + (enemy.name || id) + " 的战前事件"); + } + if (hasUsedFlags(enemy.afterBattle, flag)) { + list.push("怪物 " + (enemy.name || id) + " 的战后事件"); + } + } + // 图块的碰触 & 门信息 + for (var id in maps_90f36752_8815_4be8_b32b_d7fad1d0542e) { + var mapInfo = maps_90f36752_8815_4be8_b32b_d7fad1d0542e[id]; + if (hasUsedFlags(mapInfo.doorInfo, flag)) + list.push("图块 " + (mapInfo.name || mapInfo.id) + " 的门信息"); + if (hasUsedFlags(mapInfo.event, flag)) + list.push("图块 " + (mapInfo.name || mapInfo.id) + " 碰触事件"); + } + // 难度 & 标题事件 & 开场剧情 & 等级提升 + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.levelChoose, flag)) + list.push("难度分歧"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.startCanvas, flag)) + list.push("标题事件"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.startText, flag)) + list.push("开场剧情"); + if (hasUsedFlags(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.levelUp, flag)) + list.push("等级提升"); + // 全局商店 + (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.shops || []).forEach(function (shop) { + if (hasUsedFlags(shop, flag)) list.push("商店 " + shop.id); + }); + + return list; + } + + // ------ 选择楼层 ------ // + uievent.selectFloor = function (floorId, title, callback) { + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectFloor'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var floorId = uievent.values.floorId; + uievent.close(); + if (callback) callback(floorId); + } + + if (floorId instanceof Array) floorId = floorId[0]; + if (!floorId) floorId = editor.currentFloorId; + uievent.values.floorId = floorId; + + var html = "

"; + html += "搜索楼层:"; + html += "" + + one + '(' + floor.title + ')' + ""; + html += ""; + html += ""; + html += '
'; + }); + floorList.innerHTML = html; + } + + uievent._selectFloor_preview = function (button) { + var span = button.nextElementSibling; + while (span.firstChild) span.removeChild(span.lastChild); + var floorId = span.getAttribute('key'); + + if (span.style.display == 'none') { + button.innerText = '收起'; + span.style.display = 'inline'; + if (!uievent.values.dom) { + var canvas = document.createElement('canvas'); + canvas.style.position = 'relative'; + canvas.style.marginLeft = "-10px"; + canvas.style.marginTop = '5px'; + canvas.width = canvas.height = core.__PIXELS__; + uievent.values.dom = canvas; + uievent.values.ctx = canvas.getContext('2d'); + } + span.appendChild(uievent.values.dom); + core.clearMap(uievent.values.ctx); + core.drawThumbnail(floorId, null, { ctx: uievent.values.ctx, all: true }); + } else { + button.innerText = '预览'; + span.style.display = 'none'; + } + } + + // ------ 素材选择框 ------ // + uievent.selectMaterial = function (value, title, directory, transform, callback) { + var one = directory.split(':'); + if (one.length > 1) directory = one[0]; + var appendedImages = one[1] == 'images' ? core.material.images.images : {}; + + fs.readdir(directory, function (err, data) { + if (err) { + printe(directory + '不存在!'); + throw (directory + '不存在!'); + } + if (!(data instanceof Array)) { + printe('没有可显示的内容') + return; + } + value = value || []; + data = (transform ? data.map(transform) : data).filter(function (one) { return one; }).sort(); + var data2 = Object.keys(appendedImages); + data2 = (transform ? data2.map(transform) : data2).filter(function (one) { + return one && data.indexOf(one) < 0; + }).sort(); + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'selectMaterial'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var list = Array.from(document.getElementsByClassName('materialCheckbox')).filter(function (one) { + return one.checked; + }).map(function (one) { return one.getAttribute('key'); }); + uievent.close(); + if (callback) callback(list); + } + + var _isTileset = directory.indexOf('project/tilesets') >= 0; + + // 显示每一项内容 + var html = "

"; + html += "" + + "
"; + if (_isTileset) { + html += "警告!额外素材一旦注册成功将不可删除,否则可能会导致素材错位风险!如果你不再想用某个额外素材," + + "但又不想让它出现在素材区,可以考虑使用空气墙同名替换该额外素材文件。
" + } + data.forEach(function (one) { + var checked = value.indexOf(one) >= 0 ? 'checked' : ''; + var disabled = _isTileset && value.indexOf(one) >= 0 ? 'disabled' : '' + html += ` ${one}`; + // 预览图片 + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || one.endsWith('.gif')) { + html += ""; + html += '
'; + } + // 试听音频 + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) { + html += "" + html += " 音调:"; + html += `0:00 / 0:00
+ + `; + } + // 预览动画 + if (directory.indexOf('animates') >= 0) { + html += ""; + html += ""; + } + html += '
'; + }); + data2.forEach(function (one) { + var checked = value.indexOf(one) >= 0 ? 'checked' : ''; + var disabled = _isTileset && value.indexOf(one) >= 0 ? 'disabled' : ''; + html += ` ${one}`; + // 预览图片 + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || one.endsWith('.gif')) { + html += ""; + html += '

'; + } + }) + html += "

"; + html += "

如果文件未在此列表显示,请检查文件名是否合法(只能由数字字母下划线横线和点组成),后缀名是否正确。

"; + uievent.elements.extraBody.innerHTML = html; + }); + } + + uievent._selectAllMaterial = function (checked) { + Array.from(document.getElementsByClassName('materialCheckbox')).forEach(function (one) { + if (!one.disabled) one.checked = checked; + }) + } + + uievent._previewMaterialImage = function (button) { + var br = button.nextElementSibling; + var img = br.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '折叠'; + br.style.display = 'block'; + img.style.display = 'block'; + img.src = img.getAttribute('key'); + } else { + button.innerText = '预览'; + br.style.display = 'none'; + img.style.display = 'none'; + } + } + + uievent._previewMaterialImage2 = function (button) { + var br = button.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '折叠'; + br.style.display = 'block'; + br.parentElement.insertBefore(core.material.images.images[br.getAttribute('key')], br.nextElementSibling); + } else { + button.innerText = '预览'; + br.style.display = 'none'; + br.parentElement.removeChild(core.material.images.images[br.getAttribute('key')]); + } + } + + uievent._previewMaterialAudio = function (button) { + var span = button.nextElementSibling.nextElementSibling; + var br = span.nextElementSibling; + var audio = br.nextElementSibling; + var progress = audio.nextElementSibling; + if (br.style.display == 'none') { + button.innerText = '暂停'; + br.style.display = 'block'; + progress.style.display = 'block'; + span.style.display = 'inline'; + audio.play(); + } else { + button.innerText = '播放'; + br.style.display = 'none'; + progress.style.display = 'none'; + span.style.display = 'none'; + audio.pause(); + } + } + + uievent._previewMaterialAudio_onPitchChange = function (input) { + var audio = input.parentElement.nextElementSibling.nextElementSibling.nextElementSibling; + audio.preservesPitch = false; + audio.playbackRate = core.clamp((parseInt(input.value) || 100) / 100, 0.3, 3.0); + } + + uievent._previewMaterialAudio_onTimeUpdate = function (audio) { + var _format = function (time) { return parseInt(time / 60) + ":" + core.setTwoDigits(parseInt(time) % 60); } + if (audio.duration > 0) { + audio.previousElementSibling.previousElementSibling.innerText = _format(audio.currentTime) + " / " + _format(audio.duration); + audio.nextElementSibling.setAttribute('value', audio.currentTime / audio.duration); + } + } + + uievent._previewMaterialAudio_seek = function (element, event) { + var audio = element.previousElementSibling; + var value = event.offsetX * element.max / element.offsetWidth; + element.setAttribute("value", value); + audio.currentTime = audio.duration * value; + if (audio.paused) audio.play(); + } + + var _previewMaterialAnimate = function (span, content) { + _previewMaterialAnimate_buildSounds(span, content); + + // 创建dom + if (!uievent.values.dom) { + var dom = document.createElement('span'); + dom.style.position = "relative"; + dom.style.marginLeft = "-10px"; + var canvas = document.createElement('canvas'); + canvas.width = canvas.height = core.__PIXELS__; + canvas.style.position = 'absolute'; + core.drawThumbnail(editor.currentFloorId, null, { ctx: canvas.getContext('2d') }); + dom.appendChild(canvas); + var canvas2 = document.createElement('canvas'); + canvas2.style.position = 'absolute'; + canvas2.width = canvas2.height = core.__PIXELS__; + uievent.values.ctx = canvas2.getContext('2d'); + dom.appendChild(canvas2); + var canvas3 = document.createElement('canvas'); + canvas3.width = canvas3.height = core.__PIXELS__; + dom.appendChild(canvas3); + uievent.values.dom = dom; + } + + span.appendChild(uievent.values.dom); + clearInterval(uievent.values.interval); + var frame = 0; + uievent.values.interval = setInterval(function () { + if (span.style.display == 'none') { + clearInterval(uievent.values.interval); + uievent.values.interval = null; + return; + } + core.clearMap(uievent.values.ctx); + core.maps._drawAnimateFrame(uievent.values.ctx, content, core.__PIXELS__ / 2, core.__PIXELS__ / 2, frame++); + }, 50); + } + + var _previewMaterialAnimate_buildSounds = function (span, content) { + var sounds = content.se || {}; + if (typeof sounds == 'string') sounds = { 1: sounds }; + var pitch = content.pitch || {}; + + span.appendChild(document.createElement('br')); + var dom = document.createElement('span'); + dom.setAttribute('frames', content.frame); + var html = ""; + Object.keys(sounds).forEach(function (frame) { + html += "" + _previewMaterialAnimate_buildSoundRow(frame, sounds[frame], content.frame, pitch[frame]) + ""; + }); + html += ''; + html += ''; + html += "

"; + dom.innerHTML = html; + span.appendChild(dom); + _previewMaterialAnimate_awesomplete(span); + } + + var _previewMaterialAnimate_buildSoundRow = function (index, se, frames, pitch) { + var audios = Object.keys(core.material.sounds).sort().join(","); + var html = ""; + html += "第 帧:"; + html += ''; + html += ''; + html += " 音调:"; + html += ''; + html += '
'; + return html; + } + + var _previewMaterialAnimate_awesomplete = function (span) { + var inputs = span.getElementsByClassName("_audio"); + for (var i = 0; i < inputs.length; ++i) { + var input = inputs[i]; + if (!input.hasAttribute('awesomplete')) { + input.setAttribute('awesomplete', '1'); + new Awesomplete(input); + } + } + } + + uievent._previewMaterialAnimate = function (button) { + var span = button.nextElementSibling; + while (span.firstChild) span.removeChild(span.lastChild); + var filename = span.getAttribute("key"); + uievent.values.animates = uievent.values.animates || {}; + if (span.style.display == 'none') { + button.innerText = '收起'; + span.style.display = 'inline'; + if (uievent.values.animates[filename]) { + _previewMaterialAnimate(span, uievent.values.animates[filename]); + } else { + fs.readFile(filename, 'utf-8', function (e, d) { + if (e) { + alert('无法打开动画文件!' + e); return; + } + uievent.values.animates[filename] = core.loader._loadAnimate(d); + if (uievent.values.animates[filename]) { + uievent.values.animates[filename + ':raw'] = JSON.parse(d); + _previewMaterialAnimate(span, uievent.values.animates[filename]); + } + }) + } + } else { + button.innerText = '预览'; + span.style.display = 'none'; + } + } + + uievent._previewMaterialAnimate_previewSound = function (button) { + var input = button.previousElementSibling; + if (input.tagName == 'DIV') input = input.firstChild; + if (!input.value) return; + if (!uievent.values.audio) + uievent.values.audio = new Audio(); + uievent.values.audio.src = './project/sounds/' + input.value; + uievent.values.audio.preservesPitch = false; + uievent.values.audio.playbackRate = core.clamp((parseInt(button.nextElementSibling.children[0].value) || 100) / 100, 0.3, 3.0); + uievent.values.audio.play(); + } + + uievent._previewMaterialAnimate_addSound = function (button) { + var parent = button.parentElement; + var span = document.createElement("span"); + span.innerHTML = _previewMaterialAnimate_buildSoundRow(1, "", parseInt(parent.getAttribute("frames"))); + parent.insertBefore(span, button); + _previewMaterialAnimate_awesomplete(parent); + } + + uievent._previewMaterialAnimate_deleteSound = function (button) { + var element = button.parentElement; + element.parentElement.removeChild(element); + } + + uievent._previewMaterialAnimate_saveSound = function (button) { + var span = button.parentElement; + var filename = span.parentElement.getAttribute("key"); + if (!filename || !uievent.values.animates[filename]) return; + var se = {}; + var pitch = {}; + + var audios = span.getElementsByClassName("_audio"); + for (var i = 0; i < audios.length; ++i) { + var audio = audios[i]; + var select = audio.parentElement.previousElementSibling; + if (audio.value && select.tagName == 'SELECT') { + se[select.value] = audio.value; + var p = audio.parentElement.nextElementSibling.nextElementSibling.children[0]; + pitch[select.value] = core.clamp(parseInt(p.value) || 100, 30, 300); + } + } + uievent.values.animates[filename].se = se; + uievent.values.animates[filename + ':raw'].se = se; + uievent.values.animates[filename].pitch = pitch; + uievent.values.animates[filename + ':raw'].pitch = pitch; + fs.writeFile(filename, JSON.stringify(uievent.values.animates[filename + ':raw']), 'utf-8', function (e, d) { + if (e) alert('无法修改音效文件!' + e); + else { + alert('动画音效修改成功!别忘了在全塔属性中注册音效哦!'); + } + }) + } + + // ------ 多选框 ------ // + uievent.popCheckboxSet = function (value, comments, title, callback) { + if (value == null) value = []; + if (!(value instanceof Array)) { + if (value == 0) value = []; + else value = [value]; + } + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'popCheckboxSet'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'block'; + uievent.elements.title.innerText = title; + uievent.elements.select.style.display = 'none'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.yes.onclick = function () { + var list = Array.from(document.getElementsByClassName('uieventCheckboxSet')).filter(function (one) { + return one.checked; + }).map(function (one) { + var value = one.getAttribute('key'); + if (one.getAttribute('_type') == 'number') value = parseFloat(value); + return value; + }); + uievent.close(); + if (callback) callback(list); + } + + var keys = Array.from(comments.key) + var prefixStrings = Array.from(comments.prefix) + for (var index = 0; index < value.length; index++) { + if (keys.indexOf(value[index]) == -1) { + prefixStrings.push(value[index] + ': ') + keys.push(value[index]) + } + } + var table = ''; + + for (var index = 0; index < keys.length; index++) { + var one = keys[index]; + if (index % 3 == 0) { + table += ''; + } + table += ``; + if (index % 3 == 2) { + table += ''; + } + } + if (keys.length % 3 != 0) table += ''; + table += '
${prefixStrings[index]}= 0 ? 'checked' : ''}/>
'; + + uievent.elements.extraBody.innerHTML = "

" + table + "

"; + } + + uievent.previewEditorMulti = function (mode, code) { + if (mode == 'statusBar') return uievent.previewStatusBar(code); + } + + // ------ 状态栏预览 ------ // + uievent.previewStatusBar = function (code) { + if (!/^function\s*\(\)\s*{/.test(code)) return; + + uievent.isOpen = true; + uievent.elements.div.style.display = 'block'; + uievent.mode = 'previewStatusBar'; + uievent.elements.selectPoint.style.display = 'none'; + uievent.elements.yes.style.display = 'none'; + uievent.elements.title.innerText = '状态栏自绘预览'; + uievent.elements.select.style.display = 'inline'; + uievent.elements.selectFloor.style.display = 'none'; + uievent.elements.selectPointBox.style.display = 'none'; + uievent.elements.canvas.style.display = 'none'; + uievent.elements.extraBody.style.display = 'block'; + uievent.elements.body.style.overflow = "auto"; + + uievent.elements.select.innerHTML = '' + uievent.elements.select.onchange = uievent._previewStatusBar; + + // 计算在自绘状态栏中使用到的所有flag + var flags = {}; + code.replace(/flag:([a-zA-Z0-9_\u4E00-\u9FCC\u3040-\u30FF\u2160-\u216B\u0391-\u03C9]+)/g, function (s0, s1) { + flags[s1] = 0; return s0; + }); + code.replace(/(core\.)?flags.([a-zA-Z0-9_]+)/g, function (s0, s1, s2) { + if (!s1) flags[s2] = 0; return s0; + }); + code.replace(/core\.(has|get|set|add|remove)Flag\('(.*?)'/g, function (s0, s1, s2) { + flags[s2] = 0; return s0; + }); + code.replace(/core\.(has|get|set|add|remove)Flag\("(.*?)"/g, function (s0, s1, s2) { + flags[s2] = 0; return s0; + }); + + var html = ''; + html += "

注:此处预览效果与实际游戏内效果会有所出入,仅供参考,请以游戏内实际效果为准。

"; + html += "

"; + html += ""; + html += "属性设置:
" + html += "名称: 生命: 上限: 攻击: 防御: 护盾: "; + html += "魔力: 上限: 金币: 经验: 等级: "; + html += "
当前道具ID(以逗号分隔):
"; + html += "
当前装备ID(以逗号分隔):
"; + html += "
当前变量值(JSON格式):
"; + html += "

" + uievent.elements.extraBody.innerHTML = html; + + var inputs = document.querySelectorAll('#_previewStatusBarP input'); + for (var i = 0; i < inputs.length; ++i) inputs[i].style.width = '50px'; + + uievent.values.code = code; + uievent._previewStatusBar(); + } + + uievent._previewStatusBar = function () { + var domStyle = core.clone(core.domStyle); + var hero = core.clone(core.status.hero); + core.status.hero.flags.__statistics__ = true; + + var statusCanvasCtx = core.dom.statusCanvasCtx; + var enable = core.flags.statusCanvas; + + core.domStyle.showStatusBar = true; + core.flags.statusCanvas = true; + core.domStyle.isVertical = uievent.elements.select.value == 'vertical'; + + var canvas = document.getElementById('_previewStatusBarCanvas'); + var canvas2 = document.createElement('canvas'); + + document.getElementById('_previewStatusBarP').style.flexWrap = core.domStyle.isVertical ? 'wrap' : 'nowrap'; + + var values = Array.from(document.getElementById('_previewStatusBarValue').children).filter(function (one) { + return one.tagName == 'INPUT' || one.tagName == 'TEXTAREA'; + }).map(function (one) { return one.value; }); + core.status.hero.name = values[0]; + core.status.hero.hp = parseFloat(values[1]); + core.status.hero.hpmax = parseFloat(values[2]); + core.status.hero.atk = parseFloat(values[3]); + core.status.hero.def = parseFloat(values[4]); + core.status.hero.mdef = parseFloat(values[5]); + core.status.hero.mana = parseFloat(values[6]); + core.status.hero.manamax = parseFloat(values[7]); + core.status.hero.money = parseFloat(values[8]); + core.status.hero.exp = parseFloat(values[9]); + core.status.hero.lv = parseFloat(values[10]); + + values[11].split(',').forEach(function (itemId) { + if (!core.material.items[itemId]) return; + var itemCls = core.material.items[itemId].cls; + if (itemCls == 'items') return; + core.status.hero.items[itemCls][itemId] = (core.status.hero.items[itemCls][itemId] || 0) + 1; + }); + core.status.hero.equipment = values[12].split(','); + try { + var flags = JSON.parse(values[13]); + for (var flag in flags) { + core.status.hero.flags[flag] = flags[flag]; + } + } catch (e) { } + + var ctx = canvas2.getContext('2d'); + + if (core.domStyle.isVertical) { + canvas.width = canvas2.width = core.__PIXELS__; + canvas.height = canvas2.height = 32 * (core.values.statusCanvasRowsOnMobile || 3) + 9; + } else if (data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.flags.extendToolbar) { + canvas.width = canvas2.width = Math.round(core.__PIXELS__ * 0.31); + canvas.height = canvas2.height = core.__PIXELS__ + 3 + 38; + } else { + canvas.width = canvas2.width = Math.round(core.__PIXELS__ * 0.31); + canvas.height = canvas2.height = core.__PIXELS__; + } + + core.dom.statusCanvasCtx = ctx; + + try { + eval('(' + uievent.values.code + ')()'); + } catch (e) { + console.error(e); + } + + var toCtx = canvas.getContext('2d'); + core.fillRect(toCtx, 0, 0, canvas.width, canvas.height, 'black'); + core.drawImage(toCtx, canvas2, 0, 0); + + core.dom.statusCanvasCtx = statusCanvasCtx; + core.domStyle = domStyle; + core.flags.statusCanvas = enable; + core.status.hero = hero; + window.hero = hero; + window.flags = core.status.hero.flags; + } + + editor.constructor.prototype.uievent = uievent; +} \ No newline at end of file diff --git a/_server/editor_util.js b/_server/editor_util.js new file mode 100644 index 0000000..e8b35de --- /dev/null +++ b/_server/editor_util.js @@ -0,0 +1,173 @@ +editor_util_wrapper = function (editor) { + + editor_util = function () { + + } + + editor_util.prototype.guid = function () { + return 'id_' + 'xxxxxxxx_xxxx_4xxx_yxxx_xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + editor_util.prototype.HTMLescape = function (str_) { + return String(str_).split('').map(function (v) { + return '&#' + v.charCodeAt(0) + ';' + }).join(''); + } + + editor_util.prototype.getPixel = function (imgData, x, y) { + var offset = (x + y * imgData.width) * 4; + var r = imgData.data[offset + 0]; + var g = imgData.data[offset + 1]; + var b = imgData.data[offset + 2]; + var a = imgData.data[offset + 3]; + return [r, g, b, a]; + } + + editor_util.prototype.setPixel = function (imgData, x, y, rgba) { + var offset = (x + y * imgData.width) * 4; + imgData.data[offset + 0] = rgba[0]; + imgData.data[offset + 1] = rgba[1]; + imgData.data[offset + 2] = rgba[2]; + imgData.data[offset + 3] = rgba[3]; + } + + // rgbToHsl hue2rgb hslToRgb from https://github.com/carloscabo/colz.git + //-------------------------------------------- + // The MIT License (MIT) + // + // Copyright (c) 2014 Carlos Cabo + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in all + // copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + // SOFTWARE. + //-------------------------------------------- + // https://github.com/carloscabo/colz/blob/master/public/js/colz.class.js + var round = Math.round; + var rgbToHsl = function (rgba) { + var arg, r, g, b, h, s, l, d, max, min; + + arg = rgba; + + if (typeof arg[0] === 'number') { + r = arg[0]; + g = arg[1]; + b = arg[2]; + } else { + r = arg[0][0]; + g = arg[0][1]; + b = arg[0][2]; + } + + r /= 255; + g /= 255; + b /= 255; + + max = Math.max(r, g, b); + min = Math.min(r, g, b); + l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + //CARLOS + h = round(h * 360); + s = round(s * 100); + l = round(l * 100); + + return [h, s, l]; + } + // + var hue2rgb = function (p, q, t) { + if (t < 0) { t += 1; } + if (t > 1) { t -= 1; } + if (t < 1 / 6) { return p + (q - p) * 6 * t; } + if (t < 1 / 2) { return q; } + if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; } + return p; + } + var hslToRgb = function (hsl) { + var arg, r, g, b, h, s, l, q, p; + + arg = hsl; + + if (typeof arg[0] === 'number') { + h = arg[0] / 360; + s = arg[1] / 100; + l = arg[2] / 100; + } else { + h = arg[0][0] / 360; + s = arg[0][1] / 100; + l = arg[0][2] / 100; + } + + if (s === 0) { + r = g = b = l; // achromatic + } else { + + q = l < 0.5 ? l * (1 + s) : l + s - l * s; + p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + return [round(r * 255), round(g * 255), round(b * 255)]; + } + editor_util.prototype.rgbToHsl = rgbToHsl + editor_util.prototype.hue2rgb = hue2rgb + editor_util.prototype.hslToRgb = hslToRgb + + editor_util.prototype.encode64 = function (str) { + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { + return String.fromCharCode(parseInt(p1, 16)) + })) + } + + editor_util.prototype.decode64 = function (str) { + return decodeURIComponent(atob(str.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')).split('').map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + }).join('')) + } + + editor_util.prototype.isset = function (val) { + return val != null && !(typeof val == 'number' && isNaN(val)); + } + + editor_util.prototype.checkCallback = function (callback) { + if (!editor.util.isset(callback)) { + editor.printe('未设置callback'); + throw ('未设置callback') + } + } + + editor.constructor.prototype.util = new editor_util(); +} +//editor_util_wrapper(editor); \ No newline at end of file diff --git a/_server/fs.js b/_server/fs.js new file mode 100644 index 0000000..3e741cd --- /dev/null +++ b/_server/fs.js @@ -0,0 +1,186 @@ +(function () { + window.fs = {}; + + + var _isset = function (val) { + if (val == undefined || val == null || (typeof val == 'number' && isNaN(val))) { + return false; + } + return true + } + + var _http = function (type, url, formData, success, error, mimeType, responseType) { + var xhr = new XMLHttpRequest(); + xhr.open(type, url, true); + if (_isset(mimeType)) + xhr.overrideMimeType(mimeType); + if (_isset(responseType)) + xhr.responseType = responseType; + xhr.onload = function (e) { + if (xhr.status == 200) { + if (_isset(success)) { + success(xhr.response); + } + } + else { + if (_isset(error)) + error("HTTP " + xhr.status); + } + }; + xhr.onabort = function () { + if (_isset(error)) + error("Abort"); + } + xhr.ontimeout = function () { + if (_isset(error)) + error("Timeout"); + } + xhr.onerror = function () { + if (_isset(error)) + error("Error on Connection"); + } + if (_isset(formData)) + xhr.send(formData); + else xhr.send(); + } + + + var postsomething = function (data, _ip, callback) { + if (typeof (data) == typeof ([][0]) || data == null) data = JSON.stringify({ 1: 2 }); + + _http("POST", _ip, data, function (data) { + if (data.slice(0, 6) == 'error:') { + callback(data, null); + } + else { + callback(null, data); + } + }, function (e) { + if (window.main != null) console.log(e); + else console.log(e); + callback(e + ":请检查启动服务是否处于正常运行状态。"); + }, "text/plain; charset=x-user-defined"); + } + + fs.readFile = function (filename, encoding, callback) { + if (typeof (filename) != typeof ('')) + throw 'Type Error in fs.readFile'; + if (encoding == 'utf-8') { + //读文本文件 + //filename:支持"/"做分隔符 + //callback:function(err, data) + //data:字符串 + var data = ''; + data += 'type=utf8&'; + data += 'name=' + filename; + postsomething(data, '/readFile', callback); + return; + } + if (encoding == 'base64') { + //读二进制文件 + //filename:支持"/"做分隔符 + //callback:function(err, data) + //data:base64字符串 + var data = ''; + data += 'type=base64&'; + data += 'name=' + filename; + postsomething(data, '/readFile', callback); + return; + } + throw 'Type Error in fs.readFile'; + } + + fs.writeFile = function (filename, datastr, encoding, callback) { + if (typeof (filename) != typeof ('') || typeof (datastr) != typeof ('')) + throw 'Type Error in fs.writeFile'; + if (encoding == 'utf-8') { + //写文本文件 + //filename:支持"/"做分隔符 + //callback:function(err) + //datastr:字符串 + var data = ''; + data += 'type=utf8&'; + data += 'name=' + filename; + data += '&value=' + datastr; + postsomething(data, '/writeFile', callback); + return; + } + if (encoding == 'base64') { + //写二进制文件 + //filename:支持"/"做分隔符 + //callback:function(err) + //datastr:base64字符串 + var data = ''; + data += 'type=base64&'; + data += 'name=' + filename; + data += '&value=' + datastr; + postsomething(data, '/writeFile', callback); + return; + } + throw 'Type Error in fs.writeFile'; + } + + fs.writeMultiFiles = function (filenames, datastrs, callback) { + postsomething('name=' + filenames.join(';') + '&value=' + datastrs.join(';'), '/writeMultiFiles', callback); + } + + fs.readdir = function (path, callback) { + //callback:function(err, data) + //path:支持"/"做分隔符,不以"/"结尾 + //data:[filename1,filename2,..] filename是字符串,只包含文件不包含目录 + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/listFile', function (err, data) { + try { + data = JSON.parse(data); + } catch (e) { + err = "Invalid /listFile"; + data = null; + } + callback(err, data); + }); + return; + } + + /** + * @param {string} path 支持"/"做分隔符 + * @param {() => {err: string, data}} callback + */ + fs.mkdir = function (path, callback) { + //callback:function(err, data) + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/makeDir', callback); + return; + } + + /** + * @param {string} path 支持"/"做分隔符, 不以"/"结尾 + * @param {() => {err: string, data}} callback + */ + fs.moveFile = function (src, dest, callback) { + if (typeof (src) != typeof ('') || typeof (dest) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'src=' + src + "&dest=" + dest; + postsomething(data, '/moveFile', callback); + return; + } + + /** + * @param {string} path 支持"/"做分隔符, 不以"/"结尾 + * @param {() => {err: string, data}} callback + */ + fs.deleteFile = function (path, callback) { + if (typeof (path) != typeof ('')) + throw 'Type Error in fs.readdir'; + var data = ''; + data += 'name=' + path; + postsomething(data, '/deleteFile', callback); + return; + } +})(); \ No newline at end of file diff --git a/_server/fsTest_cs.html b/_server/fsTest_cs.html new file mode 100644 index 0000000..acfcd52 --- /dev/null +++ b/_server/fsTest_cs.html @@ -0,0 +1,70 @@ + + + + + + + + + + \ No newline at end of file diff --git a/_server/refactoring.md b/_server/refactoring.md new file mode 100644 index 0000000..1a86719 --- /dev/null +++ b/_server/refactoring.md @@ -0,0 +1,136 @@ +# 重构 + +> 目前状态: 按功能分类, 维持稳定状态, 在3.0中重写 + +总体思路 ++ 按功能拆分文件 ++ 左侧页面模块化, 方便添加 ++ 不同的模式的文件操作尽可能模块化 + +目前主要在重构editor_file, 思路是editor.file负责把editor.game内的游戏数据格式化成字符串以及写入到文件, 由editor.game来修改数据 ++ editor.file维护一些标记, 描述哪些数据需要格式化并写入, 在save时写入文件(自动保存的话就是每次修改数据都触发save) ++ editor.game修改数据, 并修改editor.file中的标记 ++ 此思路下editor.file的大部分内容会挪到editor.game, editor.game和editor.table可能会再进一步合并拆分 + +editor_file之后是更改editor.map的储存方式, 现有的存对象的模式要在对象和数字间来回转换, 非常繁琐和奇怪 + +## 文件结构 + +(全部小写,必要时用下划线分割) + ++ [ ] editor_blockly 图块化事件编辑器 ++ [ ] editor_multi 多行文本编辑器 ++ [x] editor_table 处理表格的生成, 及其响应的事件, 从原editor\_mode中分离 ++ [ ] editor_file 调用fs.js编辑文件, 把原editor\_file模块化, 并且只负责文件写入 ++ [ ] editor_game 处理游戏数据, 导入为editor的数据, 编辑数据, 从原editor和editor_file中抽离. **只有此文件允许`\s(main|core)`形式的调用**(以及其初始化`editor_game_wrapper(editor, main, core);`) ++ [x] editor_util 生成guid/处理颜色 等函数, 从editor分离 ++ [ ] editor_listen 处理界面上的按钮/下拉框点击等用户的操作与功能函数的绑定, 维护editor.dom, unsorted_1/2中的绑定挪到此处, 其中的函数内容, 分类放在其他文件 ++ [ ] editor_mappanel 与地图区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_datapanel 与数据区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_materialpanel 与素材区相关的功能, <-unsorted_1/2/3 ++ [ ] editor_ui 维护printe/printf/tip, 以及之后可能的窗口化, ui事件中没有具体到前三个区中的函数 <-unsorted_1/2/3 ++ [ ] editor 执行初始化流程加组合各组件 ++ [ ] 原editor_mode 移除 ++ [x] 原vm 移除 ++ [x] \*comment.js 表格注释与结构, 移至table/\*comment.js + +## 对象结构 + +``` +editor: { + __proto__: { + fs + util + file + table + multi + blockly + game + } + config: 编辑器配置 + mode: 当前的模式(左侧的选择) + map: 当前编辑层的地图 + isMobile: 编辑器是否是手机端 + currentFloorData: 当前编辑的楼层数据 + ... +} +``` + +--- + +## 某些注意到的点&准备修改的内容 + ++ 插入公共事件的参数的转义处理, .g4中添加ObjectString, 要求其中的值可以JSON.parse, 生成的code中也是作为对象而不是字符串出现 + ++ 修改editor.multi中的转义处理, 目前双击某些方块使用文本编辑的处理, 一部分在editor.blockly, 一部分在editor.multi, 比较混乱 + ++ 地图的编辑与其他(如全塔属性和楼层属性), 现在的文件操作的模式是完全不同的 + 楼层文件的储存与其他不同 + ++ [x] editor.file在修改时不再返回obj和commentobj,只在查询时返回 + ++ editor.file中的各个条目, 非常相似, 但是细节的不同处理非常麻烦. 是类似的代码复制后修改一部分, 尝试模块化(或者重写) + ++ functions和plugins的借助JSON.stringify的replacer特殊处理, 与其他项的处理完全不同, 改成用统一的方法处理(为了统一,全部使用这种不直观的replacer的处理) + ++ 怪物/物品/地图选点事件的处理, field中怪物id等明显与其他节地位不等, 处理起来很繁琐 + ++ 目前editor.map中储存的是info\, 准备改为和core一致只储存数字 + ++ editor.widthX特别不直观 + ++ ? 编辑器使用可拖拽和调大小的窗口做容器 + +## 功能改进 + ++ [x] 大地图 + 在切换时, 每次都回到最左上->每个楼层记录一个位置 + 四个箭头支持长按 + ? 滚动条 + ++ [ ] ? 表格折叠 + 变为四栏, 可以折叠展开 + ++ [x] blockly对于无法识别的图块原样返回 + ++ [ ] ? 简洁的事件方块注册 + `editor.registerEvent('log',[['test','Int','测试',0],['floorId','Idstring','楼层','MT0']])` + ++ [x] 一个显示所有快捷键的文本 + ++ [x] 更多快捷键 + 【全塔属性】、【楼层属性】等常用的编辑栏切换 + ++ [x] ? 地图编辑优化 + 常用的地图编辑快捷键/命令:复制ctrl+c、粘贴ctrl+v、(复制可绑定为现在的“选中xx位置事件” 粘贴为复制xx事件到此处),撤回ctrl+z、取消撤回ctrl+y + 可以按住拖动图块与事件。 + ++ [ ] ? 自由建立快捷键到命令的注册表。 + ++ [ ] 画地图也自动保存 + ++ [x] 修改系统的触发器(下拉菜单增加新项) + 在编辑器修改`comment.js`:现场发readFile请求读文件,然后开脚本编辑器进行编辑 + ++ [x] ? 删除注册项/修改图块ID + ++ [ ] ? 怪物和道具也能像其他类型那样查看“图块信息”(而不只是具体的怪物属性) + ++ [x] 素材区自动换列 + 怪物或道具太多时, 按照每100个进行拆分新开列来显示 + ++ [x] 多帧素材只显示第一帧 + ++ [x] `显示文章`以及`选项`等方块, 把`标题`和`图像`从字符串提取出填回相应的空 + ++ [x] blockly中某些需要选点的填空, 增加按钮, 点击后从缩略图中点击位置 + ++ [ ] 插件编写增加判定,如果保存时框内不是以 "function\s*()" 开头(也就是用户直接写的脚本),则自动添加一个 function() { } 将代码包装起来。 + +## 左侧页面模式 + +标题? 保存按钮? 添加按钮? 删除按钮? + +自定义内容? + +表格?