diff --git a/_server/img_md/view1.png b/_server/img_md/view1.png new file mode 100644 index 0000000..9cf0135 Binary files /dev/null and b/_server/img_md/view1.png differ diff --git a/_server/img_md/view2.png b/_server/img_md/view2.png new file mode 100644 index 0000000..8243840 Binary files /dev/null and b/_server/img_md/view2.png differ diff --git a/_server/img_md/view3.png b/_server/img_md/view3.png new file mode 100644 index 0000000..34dacea Binary files /dev/null and b/_server/img_md/view3.png differ diff --git a/_server/table/comment.js b/_server/table/comment.js new file mode 100644 index 0000000..a82b943 --- /dev/null +++ b/_server/table/comment.js @@ -0,0 +1,797 @@ +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:道具、怪物、图块属性、楼层属性等内容。 + * 相关文档 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改表格 + */ + +var comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + // --------------------------- 【道具】相关的表格配置 --------------------------- // + "items": { + "_type": "object", + "_data": { + "id": { + "_leaf": true, + "_type": "disable", + "_docs": "道具ID", + "_data": "道具ID,可于页面底部修改" + }, + "cls": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [ + "items", + "constants", + "tools", + "equips" + ] + }, + "_docs": "道具类别", + "_data": "items(宝石、血瓶) constants(永久物品) tools(消耗道具) equips(装备)" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "道具名称" + }, + "text": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "道具描述", + "_data": "道具在道具栏中显示的描述" + }, + "hideInToolbox": { + "_leaf": true, + "_type": "checkbox", + "_docs": "不显示在道具栏", + }, + "equip": { + "_leaf": true, + "_type": "event", + "_event": "equip", + "_docs": "道具的装备属性" + }, + "hideInReplay": { + "_leaf": true, + "_type": "checkbox", + "_docs": "回放不绘制道具栏", + "_data": "此项建议在会频繁连续多次使用的道具开启(如开启技能,或者《镜子》那样的镜像切换等等)" + }, + "itemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_docs": "即捡即用效果", + "_data": "即捡即用类物品的效果,仅对cls为items有效。" + }, + "itemEffectTip": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "即捡即用提示", + "_data": "即捡即用类物品在获得时提示的文字,仅对cls为items有效。" + }, + "useItemEvent": { + "_leaf": true, + "_type": "event", + "_event": "item", + "_docs": "碰触或使用事件", + "_data": "碰触或使用本道具所执行的事件,对所有cls有效" + }, + "useItemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_docs": "使用效果", + "_data": "道具效果,仅对cls为tools或constants有效。" + }, + "canUseItemEffect": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_docs": "能否使用或装备", + "_data": "当前能否使用或装备该道具,仅对cls不为items有效。null表示始终不可使用但可装备" + } + } + }, + "items_template": { 'cls': 'items', 'name': '新物品', 'canUseItemEffect': 'true' }, + + + // --------------------------- 【怪物】相关的表格配置 --------------------------- // + "enemys": { + "_type": "object", + "_data": { + "id": { + "_leaf": true, + "_type": "disable", + "_docs": "怪物ID", + "_data": "怪物ID,可于页面底部修改" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "名称" + }, + "description": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "怪物描述", + "_data": "可在怪物详细信息页面写的怪物描述,支持颜色、字体大小和样式、粗体斜体等转义方式。" + }, + "displayIdInBook": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "手册ID", + "_data": "在怪物手册中映射到的怪物ID。如果此项不为null,则在怪物手册中,将用目标ID来替换该怪物原本的ID。常被运用在同一个怪物的多朝向上。" + }, + "faceIds": { + "_leaf": true, + "_type": "event", + "_event": "faceIds", + "_docs": "行走朝向", + "_data": "行走图朝向。在勇士撞上图块时,或图块在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。" + }, + "bigImage": { + "_leaf": true, + "_type": "material", + "_directory": "./project/images/:images", + "_transform": (function (one) { + if (one.endsWith('.png')) return one; + return null; + }).toString(), + "_onconfirm": (function (previous, current) { + if (current.length == 0) return null; + return current[0]; + }).toString(), + "_docs": "绑定贴图", + "_data": "该怪物绑定的怪物贴图,用法详见文档" + }, + "hp": { + "_leaf": true, + "_type": "textarea", + "_data": "生命值" + }, + "atk": { + "_leaf": true, + "_type": "textarea", + "_data": "攻击力" + }, + "def": { + "_leaf": true, + "_type": "textarea", + "_data": "防御力" + }, + "money": { + "_leaf": true, + "_type": "textarea", + "_data": "金币" + }, + "exp": { + "_leaf": true, + "_type": "textarea", + "_data": "经验" + }, + "point": { + "_leaf": true, + "_type": "textarea", + "_data": "加点" + }, + "special": { + "_leaf": true, + "_type": "popCheckboxSet", + "_checkboxSet": function () { + var array = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys.getSpecials() + var b = [], + c = []; + for (var index = 0; index < array.length; index++) { + b.push(array[index][0]) + var name = array[index][1]; + if (name instanceof Function) name = name({}); + c.push(name + "(" + array[index][0] + ")") + } + return { + "prefix": c, + "key": b + } + }, + "_data": "特殊属性" + }, + "value": { + "_leaf": true, + "_type": "textarea", + "_docs": "特殊属性数值", + "_data": "特殊属性的数值\n如:领域/阻激/激光怪的伤害值;光环怪增加生命的比例" + }, + "zone": { + "_leaf": true, + "_type": "textarea", + "_docs": "领域伤害", + "_data": "领域怪的伤害值" + }, + "repulse": { + "_leaf": true, + "_type": "textarea", + "_docs": "阻击伤害", + "_data": "阻击怪的伤害值" + }, + "laser": { + "_leaf": true, + "_type": "textarea", + "_docs": "激光伤害", + "_data": "激光怪的伤害值" + }, + "breakArmor": { + "_leaf": true, + "_type": "textarea", + "_docs": "破甲比例", + "_data": "破甲百分比" + }, + "counterAttack": { + "_leaf": true, + "_type": "textarea", + "_docs": "反击比例", + "_data": "反击百分比" + }, + "vampire": { + "_leaf": true, + "_type": "textarea", + "_docs": "吸血比例", + "_data": "吸血怪的吸血百分比" + }, + "hpBuff": { + "_leaf": true, + "_type": "textarea", + "_docs": "光环加血", + "_data": "光环怪增加生命的比例" + }, + "atkBuff": { + "_leaf": true, + "_type": "textarea", + "_docs": "光环加攻", + "_data": "光环怪增加攻击的比例" + }, + "defBuff": { + "_leaf": true, + "_type": "textarea", + "_docs": "光环加防", + "_data": "光环怪增加防御的比例" + }, + "zoneSquare": { + "_leaf": true, + "_type": "checkbox", + "_docs": "九宫格", + "_data": "领域、阻击或捕捉怪是否九宫格" + }, + "haloSquare": { + "_leaf": true, + "_type": "checkbox", + "_docs": "九宫格", + "_data": "光环怪是否九宫格" + }, + "range": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_docs": "领域范围", + "_data": "领域的范围;不加默认为1" + }, + "haloRange": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_docs": "光环范围", + "_data": "光环的范围;不加为全图效果" + }, + "notBomb": { + "_leaf": true, + "_type": "checkbox", + "_docs": "不可炸", + "_data": "该怪物不可被炸" + }, + "n": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_docs": "连击数", + "_data": "多连击的连击数,净化怪的净化倍率" + }, + "purify": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval==~~thiseval && thiseval>0)||thiseval==null", + "_docs": "净化倍率", + "_data": "净化百分比" + }, + "add": { + "_leaf": true, + "_type": "checkbox", + "_docs": "吸血加到自身", + "_data": "吸血后是否加到自身" + }, + "haloAdd": { + "_leaf": true, + "_type": "checkbox", + "_docs": "光环是否叠加", + "_data": "光环是否叠加" + }, + "atkValue": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_docs": "退化扣攻", + "_data": "退化时勇士下降的攻击力点数" + }, + "defValue": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_docs": "退化扣防", + "_data": "退化时勇士下降的防御力点数" + }, + "damage": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval||thiseval==null", + "_docs": "固伤", + "_data": "战前扣血的点数" + }, + "beforeBattle": { + "_leaf": true, + "_type": "event", + "_event": "beforeBattle", + "_docs": "战前事件", + "_data": "和该怪物战斗前触发的事件列表" + }, + "afterBattle": { + "_leaf": true, + "_type": "event", + "_event": "afterBattle", + "_docs": "战后事件", + "_data": "和该怪物战斗后触发的事件列表" + } + } + }, + "enemys_template": { 'name': '新敌人', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'exp': 0, 'point': 0, 'special': [] }, + + + // --------------------------- 【图块属性】相关的表格配置 --------------------------- // + "maps": { + "_type": "object", + "_data": { + "id": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_docs": "图块ID", + "_data": "图块唯一ID,可在页面底部修改" + }, + "idnum": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_data": "图块数字" + }, + "cls": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_data": "图块类别" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "图块名称" + }, + "trigger": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [ + null, + "openDoor", + "pushBox", + "ski", + "custom" + ] + }, + "_docs": "触发器", + "_data": "该图块的默认触发器" + }, + "canPass": { + "_leaf": true, + "_type": "checkbox", + "_docs": "可通行性", + }, + "script": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_docs": "碰触脚本", + "_data": "触碰到该图块时自动执行的脚本内容;此脚本会在该点的触发器执行前执行" + }, + "event": { + "_leaf": true, + "_type": "event", + "_event": "item", + "_docs": "碰触事件", + "_data": "触碰到该图块时自动执行的事件内容;如果存在本事件则不会执行默认触发器" + }, + "cannotOut": { + "_leaf": true, + "_type": "checkboxSet", + "_checkboxSet": { + "prefix": ["上: ", "下: ", "
左: ", "右: "], + "key": ["up", "down", "left", "right"] + }, + "_docs": "不可出方向", + "_data": "该图块的不可出方向\n对背景层、事件层、前景层上的图块均有效" + }, + "cannotIn": { + "_leaf": true, + "_type": "checkboxSet", + "_checkboxSet": { + "prefix": ["上: ", "下: ", "
左: ", "右: "], + "key": ["up", "down", "left", "right"] + }, + "_docs": "不可入方向", + "_data": "该图块的不可入方向\n对背景层、事件层、前景层上的图块均有效" + }, + "canBreak": { + "_leaf": true, + "_type": "checkbox", + "_docs": "可破震", + "_data": "该图块是否可被破墙或地震" + }, + "animate": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [null, 1, 2, 3, 4], + }, + "_docs": "动画帧数", + "_data": "null代表素材默认帧数" + }, + "doorInfo": { + "_leaf": true, + "_type": "event", + "_event": "doorInfo", + "_docs": "门信息", + "_data": "该图块的门信息,仅对animates和npc48生效。" + }, + "faceIds": { + "_leaf": true, + "_type": "event", + "_event": "faceIds", + "_docs": "行走图朝向", + "_data": "行走图朝向。在勇士撞上图块时,或图块在移动时,会自动选择最合适的朝向图块(如果存在定义)来进行绘制。" + }, + "bigImage": { + "_leaf": true, + "_type": "material", + "_directory": "./project/images/:images", + "_transform": (function (one) { + if (one.endsWith('.png')) return one; + return null; + }).toString(), + "_onconfirm": (function (previous, current) { + if (current.length == 0) return null; + return current[0]; + }).toString(), + "_docs": "绑定贴图", + "_data": "该图块绑定的贴图,用法详见文档" + } + } + }, + + + // --------------------------- 【楼层属性】相关的表格配置 --------------------------- // + "floors": { + "_type": "object", + "_data": { + "floor": { + "_type": "object", + "_data": { + "floorId": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_docs": "楼层ID", + "_data": "文件名和floorId需要保持完全一致,可在页面底部修改" + }, + "title": { + "_leaf": true, + "_type": "textarea", + "_docs": "楼层名", + "_data": "楼层中文名,将在切换楼层和浏览地图时显示" + }, + "areas": { + "_leaf": true, + "_type": "textarea", + "_docs": "区域名", + "_data": "小地图及楼传区域显示" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_docs": "状态栏显示", + "_data": "显示在状态栏中的层数" + }, + "width": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_docs": "宽度", + "_data": "地图x方向大小,请在表格最下方修改" + }, + "height": { + "_leaf": true, + "_type": "disable", + "_range": "false", + "_docs": "高度", + "_data": "地图y方向大小,请在表格最下方修改" + }, + "canFlyTo": { + "_leaf": true, + "_type": "checkbox", + "_docs": "可楼传飞到", + "_data": "该楼能否被楼传器飞到" + }, + "canFlyFrom": { + "_leaf": true, + "_type": "checkbox", + "_docs": "可楼传飞出", + "_data": "该楼能否用楼传器飞出" + }, + "canUseQuickShop": { + "_leaf": true, + "_type": "checkbox", + "_docs": "快捷商店", + "_data": "该层是否允许使用快捷商店" + }, + "cannotViewMap": { + "_leaf": true, + "_type": "checkbox", + "_docs": "不可浏览", + "_data": "该层是否不允许被浏览地图看到;如果勾上则浏览地图会跳过该层" + }, + "cannotMoveDirectly": { + "_leaf": true, + "_type": "checkbox", + "_docs": "不可瞬移", + "_data": "该层是否不允许瞬间移动;如果勾上则不可在此层进行瞬移" + }, + "underGround": { + "_leaf": true, + "_type": "checkbox", + "_docs": "地下层", + "_data": "是否是地下层;如果该项为true则同层传送将传送至上楼梯" + }, + "firstArrive": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_docs": "首次到达事件", + "_data": "第一次到该楼层触发的事件,可以双击进入事件编辑器。" + }, + "eachArrive": { + "_leaf": true, + "_type": "event", + "_event": "eachArrive", + "_docs": "每次到达事件", + "_data": "每次到该楼层触发的事件,可以双击进入事件编辑器;该事件会在firstArrive执行后再执行。" + }, + "parallelDo": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_lint": true, + "_template": "(function (timestamp) {\\n\\t// 在这里写楼层并行脚本,大约每16.6ms执行一次\\n\\t// timestamp: 从游戏开始到当前所经过的毫秒数。\\n\\t\\n})(timestamp);", + "_docs": "并行处理脚本", + "_data": "在该层楼时执行的并行脚本处理。\n可以在这里写上任意需要自动执行的脚本,比如打怪自动开门等。\n详见文档-事件-并行事件处理。" + }, + "upFloor": { + "_leaf": true, + "_type": "point", + "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", + "_docs": "上楼点", + "_data": "该层上楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:upFloor,以及楼传器的落点会被替换成该点而不是该层的上楼梯。" + }, + "downFloor": { + "_leaf": true, + "_type": "point", + "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", + "_docs": "下楼点", + "_data": "该层下楼点,如[2,3]。\n如果此项不为null,则楼层转换时的stair:downFloor,以及楼传器的落点会被替换成该点而不是该层的下楼梯。" + }, + "flyPoint": { + "_leaf": true, + "_type": "point", + "_range": "thiseval==null||((thiseval instanceof Array) && thiseval.length==2)", + "_docs": "楼传落点", + "_data": "该层楼传落点,如[2,3]。\n如果此项不为null,则楼层飞行器强行落到此点,无视上下楼或平面塔属性。" + }, + "defaultGround": { + "_leaf": true, + "_type": "textarea", + "_docs": "地面图块", + "_data": "默认地面的图块ID,此项修改后需要刷新才能看到效果。不填则默认是ground" + }, + "images": { + "_leaf": true, + "_type": "event", + "_event": "floorImage", + "_docs": "楼层贴图" + }, + "color": { + "_leaf": true, + "_type": "color", + "_docs": "色调", + "_data": "该层的默认画面色调。本项可不写(代表无色调),如果写需要是一个RGBA数组如[255,0,0,0.3]" + }, + "weather": { + "_leaf": true, + "_type": "textarea", + "_docs": "天气", + "_data": "该层的默认天气。本项可忽略表示晴天,如果写则第一项为\"rain\",\"snow\", \"sun\", \"fog\", \"cloud\“代表对应的天气,第二项为1-10之间的数代表强度。\n如[\"rain\", 8]代表8级雨天。" + }, + "bgm": { + "_leaf": true, + "_type": "material", + "_directory": "./project/bgms/", + "_transform": (function (one) { + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + return one; + return null; + }).toString(), + "_onconfirm": (function (previous, current) { + if (current.length == 0) return null; + if (current.length == 1) return current[0]; + return current; + }).toString(), + "_docs": "背景音乐", + "_data": "到达该层后默认播放的BGM" + }, + "ratio": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval <= Number.MAX_SAFE_INTEGER && thiseval>=0", + "_docs": "宝石血瓶效果", + "_data": "每一层的宝石/血瓶效果,即获得宝石和血瓶时框内\"ratio\"的值。" + } + } + }, + "loc": { + "_type": "object", + "_data": { + "events": { + "_leaf": true, + "_type": "event", + "_event": "event", + "_docs": "普通事件", + "_data": "该点的可能事件列表,可以双击进入事件编辑器。" + }, + "autoEvent": { + "_type": "object", + "_leaf": false, + "_action": function (args) { + args.vobj = args.vobj || {}; + for (var ii = 0; ii < 2; ii++) { + args.vobj[ii] = args.vobj[ii] || null; + } + }, + "_data": function (key) { + return { + "_leaf": true, + "_type": "event", + "_event": "autoEvent", + "_data": "自动事件" + } + } + }, + "changeFloor": { + "_leaf": true, + "_type": "event", + "_event": "changeFloor", + "_docs": "楼层转换", + "_data": "该点楼层转换事件;该事件不能和上面的events同时出现,否则会被覆盖" + }, + "beforeBattle": { + "_leaf": true, + "_type": "event", + "_event": "beforeBattle", + "_docs": "战前事件", + "_data": "该点战斗前可能触发的事件列表,可以双击进入事件编辑器。" + }, + "afterBattle": { + "_leaf": true, + "_type": "event", + "_event": "afterBattle", + "_docs": "战后事件", + "_data": "该点战斗后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "afterGetItem": { + "_leaf": true, + "_type": "event", + "_event": "afterGetItem", + "_docs": "道具后事件", + "_data": "该点获得道具后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "afterOpenDoor": { + "_leaf": true, + "_type": "event", + "_event": "afterOpenDoor", + "_docs": "开门后事件", + "_data": "该点开完门后可能触发的事件列表,可以双击进入事件编辑器。" + }, + "cannotMove": { + "_leaf": true, + "_type": "checkboxSet", + "_checkboxSet": { + "prefix": ["上: ", "下: ", "
左: ", "右: "], + "key": ["up", "down", "left", "right"] + }, + "_docs": "不可出方向", + "_data": "该点不可通行出的方向 \n 可以在这里定义该点不能前往哪个方向,可以达到悬崖之类的效果" + }, + "cannotMoveIn": { + "_leaf": true, + "_type": "checkboxSet", + "_checkboxSet": { + "prefix": ["上: ", "下: ", "
左: ", "右: "], + "key": ["up", "down", "left", "right"] + }, + "_docs": "不可入方向", + "_data": "该点不可通行入的方向 \n 可以在这里定义从哪个方向前往该点,可以达到悬崖之类的效果" + }, + } + } + } + }, + + "floors_template": { + "floorId": "to be covered", + "title": "new floor", + "name": "new floor", + "width": 13, + "height": 13, + "canFlyTo": true, + "canFlyFrom": true, + "canUseQuickShop": true, + "cannotViewMap": false, + "cannotMoveDirectly": false, + "images": [], + "ratio": 1, + "defaultGround": "ground", + "bgm": null, + "upFloor": null, + "downFloor": null, + "color": null, + "weather": null, + "firstArrive": [], + "eachArrive": [], + "parallelDo": "", + "events": {}, + "changeFloor": {}, + "beforeBattle": {}, + "afterBattle": {}, + "afterGetItem": {}, + "afterOpenDoor": {}, + "autoEvent": {}, + "cannotMove": {}, + "cannotMoveIn": {} + } + } +} \ No newline at end of file diff --git a/_server/table/data.comment.js b/_server/table/data.comment.js new file mode 100644 index 0000000..0ebe2a8 --- /dev/null +++ b/_server/table/data.comment.js @@ -0,0 +1,684 @@ +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:全塔属性的配置项。 + * 相关文档 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改表格 + */ + +var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "main": { + "_type": "object", + "_data": { + "floorIds": { + "_leaf": true, + "_type": "textarea", + "_range": "editor.mode.checkFloorIds(thiseval)", + "_docs": "楼层列表", + "_data": "在这里按顺序放所有的楼层;其顺序直接影响到楼层传送器、浏览地图和上/下楼器的顺序" + }, + "floorPartitions": { + "_leaf": true, + "_type": "event", + "_event": "floorPartition", + "_docs": "分区指定", + "_data": "可以在这里指定楼层分区;主要适用于不可返回的高层塔;进入新分区后将自动调用砍层插件以减少空间消耗;未在任何分区的楼层将被视为公共区域" + }, + "images": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkImages(thiseval, './project/images/')", + "_directory": "./project/images/", + "_transform": (function (one) { + if (one.endsWith('.png') || one.endsWith('.jpg') || one.endsWith('.jpeg') || one.endsWith('.gif')) + return one; + return null; + }).toString(), + "_docs": "使用图片", + "_data": "在此存放所有可能使用的图片(tilesets除外) \n图片可以被作为背景图(的一部分),也可以直接用自定义事件进行显示。 \n 图片名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好 \n 建议对于较大的图片,在网上使用在线的“图片压缩工具(http://compresspng.com/zh/)”来进行压缩,以节省流量 \n 依次向后添加", + }, + "splitImages": { + "_leaf": true, + "_type": "event", + "_event": "splitImages", + "_docs": "图片切分", + "_data": "可以在这里对使用到的图片进行按照一定宽高切分,生成若干新的小图供使用" + }, + "tilesets": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkImages(thiseval, './project/tilesets/')", + "_directory": "./project/tilesets/", + "_transform": (function (one) { + if (one.endsWith('.png')) + return one; + return null; + }).toString(), + "_onconfirm": (function (previous, current) { + // 额外素材是有序的,因此根据之前的内容进行排序,然后在之后追加新的 + previous = previous || []; + current = current.sort(function (a, b) { + var i = previous.indexOf(a); + var j = previous.indexOf(b); + if (i >= 0 && j >= 0) return i - j; + return j >= 0 ? 1 : -1; + }); + return core.subarray(current, previous) == null && + !confirm("您修改了现有额外素材的内容或顺序,这可能导致已经使用的额外素材无法正确显示。确定要修改么?") ? + previous : current; + }).toString(), + "_docs": "额外素材", + "_data": "在此存放额外素材的图片名, \n可以自定导入任意张素材图片,无需PS,无需注册,即可直接在游戏中使用 \n 形式如[\"1.png\", \"2.png\"] ,将需要的素材图片放在images目录下 \n 素材的宽高必须都是32的倍数,且图片上的总图块数不超过1000(即最多有1000个32*32的图块在该图片上)" + }, + "animates": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/animates/", + "_transform": (function (one) { + if (one.endsWith(".animate")) return one.substring(0, one.lastIndexOf('.')); + return null; + }).toString(), + "_docs": "使用动画", + "_data": "在此存放所有可能使用的动画,必须是animate格式,在这里不写后缀名 \n动画必须放在animates目录下;文件名不能使用中文,不能带空格或特殊字符 \n \"jianji\", \"thunder\" 根据需求自行添加" + }, + "bgms": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/bgms/", + "_transform": (function (one) { + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + return one; + return null; + }).toString(), + "_docs": "使用音乐", + "_data": "在此存放所有的bgm,和文件名一致。 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" + }, + "sounds": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/sounds/", + "_transform": (function (one) { + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + return one; + return null; + }).toString(), + "_docs": "使用音效", + "_data": "在此存放所有的SE,和文件名一致 \n音频名不能使用中文,不能带空格或特殊字符;可以直接改名拼音就好" + }, + "fonts": { + "_leaf": true, + "_type": "material", + "_range": "editor.mode.checkUnique(thiseval)", + "_directory": "./project/fonts/", + "_transform": (function (one) { + if (one.endsWith(".ttf")) return one.substring(0, one.lastIndexOf('.')); + return null; + }).toString(), + "_docs": "使用字体", + "_data": "在此存放所有可能使用的字体 \n 字体名不能使用中文,不能带空格或特殊字符" + }, + "nameMap": { + "_leaf": true, + "_type": "event", + "_event": "nameMap", + "_docs": "文件别名", + "_data": "文件别名设置,目前仅对images, animates, bgms, sounds有效。" + }, + "levelChoose": { + "_leaf": true, + "_type": "event", + "_event": "levelChoose", + "_docs": "难度分歧", + "_data": "难度分歧定义和对应的事件;此处留空数组表示将直接开始游戏" + }, + "equipName": { + "_leaf": true, + "_type": "textarea", + "_range": "(thiseval instanceof Array)||thiseval==null", + "_docs": "装备孔", + "_data": "装备位名称,为一个数组,此项的顺序与equiptype数值关联;例如可写[\"武器\",\"防具\",\"首饰\"]等等。" + }, + "startBgm": { + "_leaf": true, + "_type": "material", + "_directory": "./project/bgms/", + "_transform": (function (one) { + if (one.endsWith('.mp3') || one.endsWith('.ogg') || one.endsWith('.wav') || one.endsWith('.m4a') || one.endsWith('.flac')) + return one; + return null; + }).toString(), + "_onconfirm": (function (previous, current) { + if (current.length == 0) return null; + return current[0]; + }).toString(), + "_docs": "标题音乐", + "_data": "在标题界面应该播放的bgm内容" + }, + "styles": { + "_leaf": true, + "_type": "event", + "_event": "mainStyle", + "_docs": "主样式", + "_data": "主要样式设置,包括标题、按钮、状态栏等的背景色等" + } + } + }, + "firstData": { + "_type": "object", + "_data": { + "title": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "游戏名", + "_data": "游戏名,将显示在标题页面以及切换楼层的界面中" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_range": "/^[a-zA-Z0-9_]{1,30}$/.test(thiseval)", + "_docs": "唯一英文标识符", + "_data": "游戏的唯一英文标识符。由英文、数字、下划线组成,不能超过30个字符。\n此项必须修改,其将直接影响到存档的定位!" + }, + "version": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_docs": "游戏版本", + "_data": "当前游戏版本;版本不一致的存档不能通用。" + }, + "floorId": { + "_leaf": true, + "_type": "select", + "_select": { + "values": data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds + }, + "_range": "data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.main.floorIds.indexOf(thiseval)!==-1", + "_docs": "初始楼层", + "_data": "初始楼层的ID" + }, + "hero": { + "_type": "object", + "_data": { + "image": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [null].concat(Object.keys(editor.core.material.images.images).filter(function (name) { + return name.endsWith('.png'); + })) + }, + "_data": "行走图" + }, + "animate": { + "_leaf": true, + "_type": "checkbox", + "_docs": "帧动画", + "_data": "是否开启停止状态和对话框的帧动画" + }, + "name": { + "_leaf": true, + "_type": "textarea", + "_string": true, + "_data": "勇士名" + }, + "lv": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval==~~thiseval &&thiseval>0", + "_data": "初始等级" + }, + "hpmax": { + "_leaf": true, + "_type": "textarea", + "_data": "生命上限" + }, + "hp": { + "_leaf": true, + "_type": "textarea", + "_data": "初始生命" + }, + "manamax": { + "_leaf": true, + "_type": "textarea", + "_docs": "魔力上限", + "_data": "魔力上限;此项非负才会生效(null或小于0都不会生效)" + }, + "mana": { + "_leaf": true, + "_type": "textarea", + "_data": "初始魔力" + }, + "atk": { + "_leaf": true, + "_type": "textarea", + "_data": "初始攻击" + }, + "def": { + "_leaf": true, + "_type": "textarea", + "_data": "初始防御" + }, + "mdef": { + "_leaf": true, + "_type": "textarea", + "_data": "初始护盾" + }, + "money": { + "_leaf": true, + "_type": "textarea", + "_data": "初始金币" + }, + "exp": { + "_leaf": true, + "_type": "textarea", + "_data": "初始经验" + }, + "equipment": { + "_leaf": true, + "_type": "textarea", + "_hide": true, + "_range": "thiseval instanceof Array", + "_data": "初始装备" + }, + "items": { + "_type": "object", + "_hide": true, + "_data": { + "constants": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_docs": "永久道具", + "_data": "初始永久道具个数,例如初始送手册可以写 {\"book\": 1}" + }, + "tools": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_docs": "消耗道具", + "_data": "初始消耗道具个数,例如初始有两破可以写 {\"pickaxe\": 2}" + }, + "equips": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_docs": "初始拥有装备个数", + "_data": "初始装备个数,例如初始送铁剑可以写 {\"sword1\": 1}" + } + } + }, + "loc": { + "_type": "object", + "_data": { + "direction": { + "_leaf": true, + "_type": "select", + "_data": "初始朝向", + "_select": { + "values": [ + "up", + "down", + "left", + "right" + ] + }, + }, + "x": { + "_leaf": true, + "_type": "textarea", + "_data": "横坐标" + }, + "y": { + "_leaf": true, + "_type": "textarea", + "_data": "纵坐标" + } + } + }, + "flags": { + "_leaf": true, + "_type": "textarea", + "_range": "thiseval instanceof Object && !(thiseval instanceof Array)", + "_data": "游戏变量" + }, + "followers": { + "_leaf": true, + "_type": "disable", + "_hide": true, + "_data": "跟随者" + }, + "steps": { + "_leaf": true, + "_type": "disable", + "_hide": true, + "_data": "行走步数" + } + } + }, + "startCanvas": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_range": "thiseval==null || thiseval instanceof Array", + "_docs": "标题事件", + "_data": "标题界面事件化,可以使用事件流的形式来绘制开始界面等。\n需要开启startUsingCanvas这个开关。\n详见文档-个性化-标题界面事件化。" + }, + "startText": { + "_leaf": true, + "_type": "event", + "_event": "firstArrive", + "_range": "thiseval==null || thiseval instanceof Array", + "_docs": "开场剧情", + "_data": "游戏开始前剧情,可以执行任意自定义事件。\n双击进入事件编辑器。\n如果无剧情直接留一个空数组即可。" + }, + "shops": { + "_leaf": true, + "_type": "event", + "_event": "shop", + "_range": "thiseval instanceof Array", + "_docs": "全局商店", + "_data": "全局商店,是一个数组,可以双击进入事件编辑器。" + }, + "levelUp": { + "_leaf": true, + "_type": "event", + "_event": "level", + "_range": "thiseval==null || thiseval instanceof Array", + "_docs": "等级提升", + "_data": "经验升级所需要的数值,是一个数组,可以双击进行编辑。 \n 第一项为初始等级,仅title生效 \n 每一个里面可以含有三个参数 need, title, action \n need为所需要的经验数值,可以是个表达式。请确保need依次递增 \n title为该等级的名称,也可以省略代表使用系统默认值;本项将显示在状态栏中 \n action为本次升级所执行的事件,可由若干项组成" + } + } + }, + "values": { + "_type": "object", + "_data": { + "lavaDamage": { + "_leaf": true, + "_type": "textarea", + "_data": "血网伤害" + }, + "poisonDamage": { + "_leaf": true, + "_type": "textarea", + "_data": "中毒伤害" + }, + "weakValue": { + "_leaf": true, + "_type": "textarea", + "_docs": "衰弱效果", + "_data": "衰弱状态下攻防减少的数值\n如果此项不小于1,则作为实际下降的数值(比如10就是攻防各下降10)\n如果在0到1之间则为下降的比例(比如0.3就是下降30%的攻防)" + }, + "redGem": { + "_leaf": true, + "_type": "textarea", + "_docs": "红宝石值", + }, + "blueGem": { + "_leaf": true, + "_type": "textarea", + "_docs": "蓝宝石值", + }, + "greenGem": { + "_leaf": true, + "_type": "textarea", + "_docs": "绿宝石值", + }, + "redPotion": { + "_leaf": true, + "_type": "textarea", + "_docs": "红血瓶值", + }, + "bluePotion": { + "_leaf": true, + "_type": "textarea", + "_docs": "蓝血瓶值", + }, + "yellowPotion": { + "_leaf": true, + "_type": "textarea", + "_docs": "黄血瓶值", + }, + "greenPotion": { + "_leaf": true, + "_type": "textarea", + "_docs": "绿血瓶值", + }, + "breakArmor": { + "_leaf": true, + "_type": "textarea", + "_docs": "破甲比例", + "_data": "破甲的比例(战斗前,怪物附加角色防御的x倍作为伤害);可以用怪物的 defValue 覆盖此值。" + }, + "counterAttack": { + "_leaf": true, + "_type": "textarea", + "_docs": "反击比例", + "_data": "反击的比例(战斗时,怪物每回合附加角色攻击的x倍作为伤害,无视角色防御);可以用怪物的 atkValue 覆盖此值。" + }, + "purify": { + "_leaf": true, + "_type": "textarea", + "_docs": "净化比例", + "_data": "净化的比例(战斗前,怪物附加勇士护盾的x倍作为伤害);可以用怪物的 n 覆盖此值。" + }, + "hatred": { + "_leaf": true, + "_type": "textarea", + "_docs": "仇恨增值", + "_data": "仇恨属性中,每杀死一个怪物获得的仇恨值" + }, + "animateSpeed": { + "_leaf": true, + "_type": "textarea", + "_docs": "全局帧动画时间", + "_data": "全局帧动画时间,即怪物振动频率,一般300比较合适" + }, + "moveSpeed": { + "_leaf": true, + "_type": "textarea", + "_docs": "勇士移速", + "_data": "勇士每一格的移速,默认是100;此项可被用户修改覆盖" + }, + "statusCanvasRowsOnMobile": { + "_leaf": true, + "_type": "select", + "_select": { + "values": [1, 2, 3, 4, 5] + }, + "_docs": "竖状态栏自绘行数", + "_data": "竖屏模式下,顶端状态栏canvas化后的行数。\n此项将决定竖屏的状态栏高度,如果设置则不小于1且不大于5。\n仅在statusCanvas开启时才有效" + }, + "floorChangeTime": { + "_leaf": true, + "_type": "textarea", + "_docs": "楼层切换时间", + "_data": "默认楼层切换时间;当楼层切换没有显式设置时间时将用此值;可以被用户在系统设置内手动修改" + } + } + }, + "flags": { + "_type": "object", + "_data": { + "statusBarItems": { + "_leaf": true, + "_type": "checkboxSet", + "_checkboxSet": { + "prefix": [ + "楼层", + "名字", + "
等级", + "血限", + "
生命", + "魔力", + "
攻击", + "防御", + "
护盾", + "金币", + "
经验", + "升级", + "
升级扣除模式", + "
钥匙", + "绿钥", + "
破炸", + "负面", + "
技能" + ], + "key": [ + "enableFloor", + "enableName", + "enableLv", + "enableHPMax", + "enableHP", + "enableMana", + "enableAtk", + "enableDef", + "enableMDef", + "enableMoney", + "enableExp", + "enableLevelUp", + "levelUpLeftMode", + "enableKeys", + "enableGreenKey", + "enablePZF", + "enableDebuff", + "enableSkill" + ] + }, + "_data": "状态栏显示项" + }, + "autoScale": { + "_leaf": true, + "_type": "checkbox", + "_docs": "自动缩放", + "_data": "是否自动缩放至合适值" + }, + "extendToolbar": { + "_leaf": true, + "_type": "checkbox", + "_docs": "画面下方道具栏", + "_data": "开启后道具栏会被挪动至游戏画面的下方" + }, + "hideLeftStatusBar": { + "_leaf": true, + "_type": "checkbox", + "_docs": "隐藏左侧状态栏", + "_data": "是否隐藏左侧状态栏,开启后强制开启画面下方道具栏" + }, + "flyNearStair": { + "_leaf": true, + "_type": "checkbox", + "_docs": "楼传需在楼梯边", + "_data": "传送器是否需要在楼梯边使用;如果flyRecordPosition开启,则此项对箭头也有效。" + }, + "flyRecordPosition": { + "_leaf": true, + "_type": "checkbox", + "_docs": "楼传开平面模式", + "_data": "传送器平面塔模式;此模式下楼层传送器将飞到上次离开该楼层的位置。" + }, + "itemFirstText": { + "_leaf": true, + "_type": "checkbox", + "_docs": "首次道具进行提示", + "_data": "首次获得道具是否提示" + }, + "equipboxButton": { + "_leaf": true, + "_type": "checkbox", + "_docs": "状态栏装备按钮", + "_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮" + }, + "enableAddPoint": { + "_leaf": true, + "_type": "checkbox", + "_docs": "开启加点", + "_data": "是否支持加点" + }, + "enableNegativeDamage": { + "_leaf": true, + "_type": "checkbox", + "_docs": "开启负伤", + "_data": "是否支持负伤害(回血)" + }, + "betweenAttackMax": { + "_leaf": true, + "_type": "checkbox", + "_docs": "夹击不超伤害值", + "_data": "夹击伤害是否不超过怪物伤害值。" + }, + "useLoop": { + "_leaf": true, + "_type": "checkbox", + "_docs": "二分临界", + "_data": "是否循环计算临界;如果此项为true则使用循环法(而不是回合数计算法)来算临界\n从V2.5.3开始,对于大数据的循环法将改为使用二分法进行计算" + }, + "startUsingCanvas": { + "_leaf": true, + "_type": "checkbox", + "_docs": "标题开启事件化", + "_data": "是否开始菜单canvas化;如果此项为true,则将使用canvas来绘制开始菜单" + }, + "statusCanvas": { + "_leaf": true, + "_type": "checkbox", + "_docs": "开启自绘状态栏", + "_data": "是否状态栏canvas化,即手动自定义绘制状态栏。\n如果此项开启,则可在脚本编辑的drawStatusBar中自定义绘制菜单栏。" + }, + "enableEnemyPoint": { + "_leaf": true, + "_type": "checkbox", + "_docs": "定点怪显", + "_data": "是否开启怪物的定点显示功能,即属性不同的怪物会在怪物手册单列;用户可以手动在菜单栏中开关" + }, + "enableGentleClick": { + "_leaf": true, + "_type": "checkbox", + "_docs": "允许轻按", + "_data": "是否允许轻触(获得面前物品)" + }, + "ignoreChangeFloor": { + "_leaf": true, + "_type": "checkbox", + "_docs": "允许穿透楼梯", + "_data": "经过楼梯、传送门时是否能“穿透”。\n穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件" + }, + "canGoDeadZone": { + "_leaf": true, + "_type": "checkbox", + "_docs": "允许将死领域", + "_data": "是否允许走到将死的领域上。如果此项为true,则可以走到将死的领域上" + }, + "enableMoveDirectly": { + "_leaf": true, + "_type": "checkbox", + "_docs": "允许瞬移", + "_data": "是否允许瞬间移动" + }, + "enableRouteFolding": { + "_leaf": true, + "_type": "checkbox", + "_docs": "录像折叠", + "_data": "是否开启录像折叠功能。\n此功能将尽可能的通过优化无损耗反复走的方式来缩短录像,但在某些情况下(尤其是创新向塔中)可能会存在副作用影响录像的正确性。" + }, + "disableShopOnDamage": { + "_leaf": true, + "_type": "checkbox", + "_docs": "伤害禁用商店", + "_data": "是否在经过领域/夹击/路障等伤害后禁用快捷商店。" + }, + "blurFg": { + "_leaf": true, + "_type": "checkbox", + "_docs": "虚化前景层", + "_data": "是否虚化前景层;如果此项开启,则在游戏中事件层有东西(如宝石等)时虚化前景层。" + } + } + } + } +} \ No newline at end of file diff --git a/_server/table/events.comment.js b/_server/table/events.comment.js new file mode 100644 index 0000000..63b58f0 --- /dev/null +++ b/_server/table/events.comment.js @@ -0,0 +1,39 @@ +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:公共事件。 + * 相关文档 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改表格 + */ + +var events_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "commonEvent": { + "_type": "object", + "_data": function (key) { + var obj = { + "加点事件": { + "_leaf": true, + "_type": "event", + "_range": "thiseval instanceof Array", + "_event": "commonEvent", + "_data": "加点事件" + }, + "回收钥匙商店": { + "_leaf": true, + "_type": "event", + "_event": "commonEvent", + "_data": "回收钥匙商店" + }, + } + if (obj[key]) return obj[key]; + return { + "_leaf": true, + "_type": "event", + "_event": "commonEvent", + "_data": "公共事件" + } + } + } + } +} \ No newline at end of file diff --git a/_server/table/functions.comment.js b/_server/table/functions.comment.js new file mode 100644 index 0000000..9326bf1 --- /dev/null +++ b/_server/table/functions.comment.js @@ -0,0 +1,213 @@ +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:脚本编辑。 + * 相关文档 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改表格 + */ + +var functions_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": { + "events": { + "_type": "object", + "_data": { + "resetGame": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "重置游戏" + }, + "win": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "游戏获胜" + }, + "lose": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "游戏失败" + }, + "changingFloor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "切换楼层中" + }, + "afterChangeFloor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "切换楼层后" + }, + "flyTo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "楼层飞行" + }, + "beforeBattle": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战前判定" + }, + "afterBattle": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战后脚本" + }, + "afterOpenDoor": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "开门后脚本" + }, + "afterGetItem": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "道具后脚本" + }, + "afterPushBox": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "推箱子后" + } + } + }, + "enemys": { + "_type": "object", + "_data": { + "getSpecials": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "怪物特殊属性" + }, + "getEnemyInfo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "怪物真实属性" + }, + "getDamageInfo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "战斗伤害信息" + } + } + }, + "actions": { + "_type": "object", + "_data": { + "onKeyUp": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "按键处理" + }, + "onStatusBarClick": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_docs": "点击状态栏", + "_data": "状态栏点击事件,仅在开启自绘状态栏时生效" + } + } + }, + "control": { + "_type": "object", + "_data": { + "saveData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "存档操作" + }, + "loadData": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "读档操作" + }, + "getStatusLabel": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "状态名定义" + }, + "triggerDebuff": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "毒衰咒处理" + }, + "updateStatusBar": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "更新状态栏" + }, + "updateCheckBlock": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "阻激夹域伤害" + }, + "moveOneStep": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "每步后操作" + }, + "moveDirectly": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "瞬间移动" + }, + "parallelDo": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "并行脚本" + } + } + }, + "ui": { + "_type": "object", + "_data": { + "getToolboxItems": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "道具栏显示项" + }, + "drawStatusBar": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_preview": "statusBar", + "_data": "自绘状态栏" + }, + "drawStatistics": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "数据统计" + }, + "drawAbout": { + "_leaf": true, + "_type": "textarea", + "_lint": true, + "_data": "关于界面" + } + } + } + } +} \ No newline at end of file diff --git a/_server/table/plugins.comment.js b/_server/table/plugins.comment.js new file mode 100644 index 0000000..d4b7861 --- /dev/null +++ b/_server/table/plugins.comment.js @@ -0,0 +1,124 @@ +/* + * 表格配置项。 + * 在这里可以对表格中的各项显示进行配置,包括表格项、提示内容等内容。具体写法照葫芦画瓢即可。 + * 本配置项包括:插件编写。 + * 相关文档 _docs/editor.md ~ http://127.0.0.1:1055/_docs/#/editor?id=修改表格 + */ + +var plugins_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = { + "_type": "object", + "_data": function (key) { + var obj = { + "init": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string'", + "_data": "初始化" + }, + "shop": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string'", + "_data": "全局商店" + }, + "drawLight": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "灯光效果" + }, + "removeMap": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "砍层插件" + }, + "fiveLayers": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "五图层(背景前景2)" + }, + "itemShop": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "道具商店" + }, + "enemyLevel": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "手册显示怪物境界" + }, + "multiHeros": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "多角色" + }, + "heroFourFrames": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "勇士四帧行走动画" + }, + "routeFixing": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "录像精修" + }, + "numpad": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "整数输入面板美化" + }, + "sprite": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "基于canvas的sprite化" + }, + "hotReload": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "热重载" + }, + "statusBar": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "自定义状态栏" + }, + "override": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "函数复写" + }, + "animate": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "高级动画" + }, + "func": { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_data": "实用函数集" + } + } + if (obj[key]) return obj[key]; + return { + "_leaf": true, + "_type": "textarea", + "_range": "typeof(thiseval)=='string' || thiseval==null", + "_template": "function () {\\n\\t// 在此增加新插件\\n\\t\\n}", + "_data": "自定义插件" + } + } +} \ No newline at end of file diff --git a/_server/thirdparty/LICENSE.md b/_server/thirdparty/LICENSE.md new file mode 100644 index 0000000..8867f3d --- /dev/null +++ b/_server/thirdparty/LICENSE.md @@ -0,0 +1,60 @@ +/* jsColor */ + +The MIT License (MIT) + +Copyright (c) 2014 Peter Dematté + +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. + +/* awesomplete */ + +The MIT License (MIT) + +Copyright (c) 2015 Lea Verou + +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. + + +/* caret-position */ + +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Ong me@jongleberry.com + +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. diff --git a/_server/thirdparty/awesomplete.css b/_server/thirdparty/awesomplete.css new file mode 100644 index 0000000..5f5323e --- /dev/null +++ b/_server/thirdparty/awesomplete.css @@ -0,0 +1,75 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + white-space: nowrap; +} + +.awesomplete > ul:empty { + display: none; +} + +.awesomplete > ul { + border-radius: .3em; + margin: .8em 0 0; + text-shadow: none; +} + +@supports (transform: scale(0)) { + .awesomplete > ul { + transition: .3s cubic-bezier(.4,.2,.5,1.4); + transform-origin: 1.43em -.43em; + } + + .awesomplete > ul[hidden], + .awesomplete > ul:empty { + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; + } +} + +/* Pointer */ +.awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + padding: .4em; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); +} + +.awesomplete > ul > li { + position: relative; + padding: .2em .5em; + cursor: pointer; + word-break: keep-all; +} diff --git a/_server/thirdparty/awesomplete.min.js b/_server/thirdparty/awesomplete.min.js new file mode 100644 index 0000000..ccaf6b0 --- /dev/null +++ b/_server/thirdparty/awesomplete.min.js @@ -0,0 +1,2 @@ +// Awesomplete - Lea Verou - MIT license +!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); diff --git a/_server/thirdparty/caret-position.js b/_server/thirdparty/caret-position.js new file mode 100644 index 0000000..b6e14aa --- /dev/null +++ b/_server/thirdparty/caret-position.js @@ -0,0 +1,158 @@ +/* jshint browser: true */ + +(function () { + +// We'll copy the properties below into the mirror div. +// Note that some browsers, such as Firefox, do not concatenate properties +// into their shorthand (e.g. padding-top, padding-bottom etc. -> padding), +// so we have to list every single property explicitly. + var properties = [ + 'direction', // RTL support + 'boxSizing', + 'width', // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does + 'height', + 'overflowX', + 'overflowY', // copy the scrollbar for IE + + 'borderTopWidth', + 'borderRightWidth', + 'borderBottomWidth', + 'borderLeftWidth', + 'borderStyle', + + 'paddingTop', + 'paddingRight', + 'paddingBottom', + 'paddingLeft', + + // https://developer.mozilla.org/en-US/docs/Web/CSS/font + 'fontStyle', + 'fontVariant', + 'fontWeight', + 'fontStretch', + 'fontSize', + 'fontSizeAdjust', + 'lineHeight', + 'fontFamily', + + 'textAlign', + 'textTransform', + 'textIndent', + 'textDecoration', // might not make a difference, but better be safe + + 'letterSpacing', + 'wordSpacing', + + 'tabSize', + 'MozTabSize' + + ]; + + var isBrowser = (typeof window !== 'undefined'); + var isFirefox = (isBrowser && window.mozInnerScreenX != null); + + function getCaretCoordinates(element, position, options) { + if (!isBrowser) { + throw new Error('textarea-caret-position#getCaretCoordinates should only be called in a browser'); + } + + var debug = options && options.debug || false; + if (debug) { + var el = document.querySelector('#input-textarea-caret-position-mirror-div'); + if (el) el.parentNode.removeChild(el); + } + + // The mirror div will replicate the textarea's style + var div = document.createElement('div'); + div.id = 'input-textarea-caret-position-mirror-div'; + document.body.appendChild(div); + + var style = div.style; + var computed = window.getComputedStyle ? window.getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9 + var isInput = element.nodeName === 'INPUT'; + + // Default textarea styles + style.whiteSpace = 'nowrap'; + style.zIndex = 2000; + if (!isInput) + style.wordWrap = 'break-word'; // only for textarea-s + + // Position off-screen + style.position = 'absolute'; // required to return coordinates properly + if (!debug) + style.visibility = 'hidden'; // not 'display: none' because we want rendering + + // Transfer the element's properties to the div + properties.forEach(function (prop) { + if (isInput && prop === 'lineHeight') { + // Special case for s because text is rendered centered and line height may be != height + if (computed.boxSizing === "border-box") { + var height = parseInt(computed.height); + var outerHeight = + parseInt(computed.paddingTop) + + parseInt(computed.paddingBottom) + + parseInt(computed.borderTopWidth) + + parseInt(computed.borderBottomWidth); + var targetHeight = outerHeight + parseInt(computed.lineHeight); + if (height > targetHeight) { + style.lineHeight = height - outerHeight + "px"; + } else if (height === targetHeight) { + style.lineHeight = computed.lineHeight; + } else { + style.lineHeight = 0; + } + } else { + style.lineHeight = computed.height; + } + } else { + style[prop] = computed[prop]; + } + }); + if (!isInput) + style.whiteSpace = 'pre-wrap'; + + if (isFirefox) { + // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 + if (element.scrollHeight > parseInt(computed.height)) + style.overflowY = 'scroll'; + } else { + style.overflow = 'hidden'; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' + } + + div.textContent = element.value.substring(0, position); + // The second special handling for input type="text" vs textarea: + // spaces need to be replaced with non-breaking spaces - http://stackoverflow.com/a/13402035/1269037 + if (isInput) + div.textContent = div.textContent.replace(/\s/g, '\u00a0'); + + var span = document.createElement('span'); + // Wrapping must be replicated *exactly*, including when a long word gets + // onto the next line, with whitespace at the end of the line before (#7). + // The *only* reliable way to do that is to copy the *entire* rest of the + // textarea's content into the created at the caret position. + // For inputs, just '.' would be enough, but no need to bother. + span.textContent = element.value.substring(position) || '.'; // || because a completely empty faux span doesn't render at all + div.appendChild(span); + + var coordinates = { + top: span.offsetTop + parseInt(computed['borderTopWidth']), + left: span.offsetLeft + parseInt(computed['borderLeftWidth']), + height: parseInt(computed['lineHeight']) + }; + + if (debug) { + span.style.backgroundColor = '#aaa'; + } else { + document.body.removeChild(div); + } + + return coordinates; + } + + if (typeof module != 'undefined' && typeof module.exports != 'undefined') { + module.exports = getCaretCoordinates; + } else if(isBrowser) { + window.getCaretCoordinates = getCaretCoordinates; + } + +}()); \ No newline at end of file diff --git a/_server/thirdparty/color.all.min.js b/_server/thirdparty/color.all.min.js new file mode 100644 index 0000000..a8032a0 --- /dev/null +++ b/_server/thirdparty/color.all.min.js @@ -0,0 +1,4 @@ +/*! colorPicker - v1.0.0 2016-05-18 */ + +!function(a,b){"use strict";function c(a,c,d,f,g){if("string"==typeof c){var c=v.txt2color(c);d=c.type,n[d]=c[d],g=g!==b?g:c.alpha}else if(c)for(var h in c)a[d][h]="Lab"===d?k(c[h],l[d][h][0],l[d][h][1]):k(c[h]/l[d][h][1],0,1);return g!==b&&(a.alpha=k(+g,0,1)),e(d,f?a:b)}function d(a,b,c){var d=m.options.grey,e={};return e.RGB={r:a.r,g:a.g,b:a.b},e.rgb={r:b.r,g:b.g,b:b.b},e.alpha=c,e.equivalentGrey=r.round(d.r*a.r+d.g*a.g+d.b*a.b),e.rgbaMixBlack=i(b,{r:0,g:0,b:0},c,1),e.rgbaMixWhite=i(b,{r:1,g:1,b:1},c,1),e.rgbaMixBlack.luminance=h(e.rgbaMixBlack,!0),e.rgbaMixWhite.luminance=h(e.rgbaMixWhite,!0),m.options.customBG&&(e.rgbaMixCustom=i(b,m.options.customBG,c,1),e.rgbaMixCustom.luminance=h(e.rgbaMixCustom,!0),m.options.customBG.luminance=h(m.options.customBG,!0)),e}function e(a,b){var c,e,k,o=r,p=b||n,q=v,s=m.options,t=l,u=p.RND,w="",x="",y={hsl:"hsv",cmyk:"cmy",rgb:a},z=u.rgb;if("alpha"!==a){for(var A in t)if(!t[A][A]){a!==A&&"XYZ"!==A&&(x=y[A]||"rgb",p[A]=q[x+"2"+A](p[x])),u[A]||(u[A]={}),c=p[A];for(w in c)u[A][w]=o.round(c[w]*("Lab"===A?1:t[A][w][1]))}"Lab"!==a&&delete p._rgb,z=u.rgb,p.HEX=q.RGB2HEX(z),p.equivalentGrey=s.grey.r*p.rgb.r+s.grey.g*p.rgb.g+s.grey.b*p.rgb.b,p.webSave=e=f(z,51),p.webSmart=k=f(z,17),p.saveColor=z.r===e.r&&z.g===e.g&&z.b===e.b?"web save":z.r===k.r&&z.g===k.g&&z.b===k.b?"web smart":"",p.hueRGB=q.hue2RGB(p.hsv.h),b&&(p.background=d(z,p.rgb,p.alpha))}var B,C,D,E,F,G,H,I=p.rgb,J=p.alpha,K="luminance",L=p.background,M=i,N=h,O=j,P=g;return B=M(I,{r:0,g:0,b:0},J,1),B[K]=N(B,!0),p.rgbaMixBlack=B,C=M(I,{r:1,g:1,b:1},J,1),C[K]=N(C,!0),p.rgbaMixWhite=C,s.allMixDetails&&(B.WCAG2Ratio=O(B[K],0),C.WCAG2Ratio=O(C[K],1),s.customBG&&(D=M(I,s.customBG,J,1),D[K]=N(D,!0),D.WCAG2Ratio=O(D[K],s.customBG[K]),p.rgbaMixCustom=D),E=M(I,L.rgb,J,L.alpha),E[K]=N(E,!0),p.rgbaMixBG=E,F=M(I,L.rgbaMixBlack,J,1),F[K]=N(F,!0),F.WCAG2Ratio=O(F[K],L.rgbaMixBlack[K]),F.luminanceDelta=o.abs(F[K]-L.rgbaMixBlack[K]),F.hueDelta=P(L.rgbaMixBlack,F,!0),p.rgbaMixBGMixBlack=F,G=M(I,L.rgbaMixWhite,J,1),G[K]=N(G,!0),G.WCAG2Ratio=O(G[K],L.rgbaMixWhite[K]),G.luminanceDelta=o.abs(G[K]-L.rgbaMixWhite[K]),G.hueDelta=P(L.rgbaMixWhite,G,!0),p.rgbaMixBGMixWhite=G),s.customBG&&(H=M(I,L.rgbaMixCustom,J,1),H[K]=N(H,!0),H.WCAG2Ratio=O(H[K],L.rgbaMixCustom[K]),p.rgbaMixBGMixCustom=H,H.luminanceDelta=o.abs(H[K]-L.rgbaMixCustom[K]),H.hueDelta=P(L.rgbaMixCustom,H,!0)),p.RGBLuminance=N(z),p.HUELuminance=N(p.hueRGB),s.convertCallback&&s.convertCallback(p,a),p}function f(a,b){var c={},d=0,e=b/2;for(var f in a)d=a[f]%b,c[f]=a[f]+(d>e?b-d:-d);return c}function g(a,b,c){var d=r;return(d.max(a.r-b.r,b.r-a.r)+d.max(a.g-b.g,b.g-a.g)+d.max(a.b-b.b,b.b-a.b))*(c?255:1)/765}function h(a,b){for(var c=b?1:255,d=[a.r/c,a.g/c,a.b/c],e=m.options.luminance,f=d.length;f--;)d[f]=d[f]<=.03928?d[f]/12.92:r.pow((d[f]+.055)/1.055,2.4);return e.r*d[0]+e.g*d[1]+e.b*d[2]}function i(a,c,d,e){var f={},g=d!==b?d:1,h=e!==b?e:1,i=g+h*(1-g);for(var j in a)f[j]=(a[j]*g+c[j]*h*(1-g))/i;return f.a=i,f}function j(a,b){var c=1;return c=a>=b?(a+.05)/(b+.05):(b+.05)/(a+.05),r.round(100*c)/100}function k(a,b,c){return a>c?c:b>a?b:a}var l={rgb:{r:[0,255],g:[0,255],b:[0,255]},hsv:{h:[0,360],s:[0,100],v:[0,100]},hsl:{h:[0,360],s:[0,100],l:[0,100]},cmy:{c:[0,100],m:[0,100],y:[0,100]},cmyk:{c:[0,100],m:[0,100],y:[0,100],k:[0,100]},Lab:{L:[0,100],a:[-128,127],b:[-128,127]},XYZ:{X:[0,100],Y:[0,100],Z:[0,100]},alpha:{alpha:[0,1]},HEX:{HEX:[0,16777215]}},m={},n={},o={X:[.4124564,.3575761,.1804375],Y:[.2126729,.7151522,.072175],Z:[.0193339,.119192,.9503041],R:[3.2404542,-1.5371385,-.4985314],G:[-.969266,1.8760108,.041556],B:[.0556434,-.2040259,1.0572252]},p={r:.298954,g:.586434,b:.114612},q={r:.2126,g:.7152,b:.0722},r=a.Math,s=(a.parseInt,a.Colors=function(a){this.colors={RND:{}},this.options={color:"rgba(204, 82, 37, 0.8)",XYZMatrix:o,grey:p,luminance:q,valueRanges:l},t(this,a||{})}),t=function(a,d){var e,f,g=a.options;u(a);for(var h in d)d[h]!==b&&(g[h]=d[h]);e=g.XYZMatrix,d.XYZReference||(g.XYZReference={X:e.X[0]+e.X[1]+e.X[2],Y:e.Y[0]+e.Y[1]+e.Y[2],Z:e.Z[0]+e.Z[1]+e.Z[2]}),f=g.customBG,g.customBG="string"==typeof f?v.txt2color(f).rgb:f,n=c(a.colors,g.color,b,!0)},u=function(a){m!==a&&(m=a,n=a.colors)};s.prototype.setColor=function(a,d,f){return u(this),a?c(this.colors,a,d,b,f):(f!==b&&(this.colors.alpha=k(f,0,1)),e(d))},s.prototype.getColor=function(a){var c=this.colors,d=0;if(a){for(a=a.split(".");c[a[d]];)c=c[a[d++]];a.length!==d&&(c=b)}return c},s.prototype.setCustomBackground=function(a){return u(this),this.options.customBG="string"==typeof a?v.txt2color(a).rgb:a,c(this.colors,b,"rgb")},s.prototype.saveAsBackground=function(){return u(this),c(this.colors,b,"rgb",!0)},s.prototype.convertColor=function(a,b){var c=v,d=l,e=b.split("2"),f=e[0],g=e[1],h=/(?:RG|HS|CM|LA)/,i=h.test(f),j=h.test(g),k={LAB:"Lab"},m=function(a,b,c){var e={},f="Lab"===b?1:0;for(var g in a)e[g]=c?r.round(a[g]*(f||d[b][g][1])):a[g]/(f||d[b][g][1]);return e};return f=d[f]?f:k[f]||f.toLowerCase(),g=d[g]?g:k[g]||g.toLowerCase(),i&&"RGB2HEX"!==b&&(a=m(a,f)),a=f===g?a:c[f+"2"+g]?c[f+"2"+g](a,!0):"HEX"===g?c.RGB2HEX("RGB2HEX"===b?a:m("rgb"===f?a:c[f+"2rgb"](a,!0),"rgb",!0)):c["rgb2"+g](c[f+"2rgb"](a,!0),!0),j&&(a=m(a,g,!0)),a},s.prototype.toString=function(a,b){return v.color2text((a||"rgb").toLowerCase(),this.colors,b)};var v={txt2color:function(a){var b={},c=a.replace(/(?:#|\)|%)/g,"").split("("),d=(c[1]||"").split(/,\s*/),e=c[1]?c[0].substr(0,3):"rgb",f="";if(b.type=e,b[e]={},c[1])for(var g=3;g--;)f=e[g]||e.charAt(g),b[e][f]=+d[g]/l[e][f][1];else b.rgb=v.HEX2rgb(c[0]);return b.alpha=d[3]?+d[3]:1,b},color2text:function(a,b,c){var d=c!==!1&&r.round(100*b.alpha)/100,e="number"==typeof d&&c!==!1&&(c||1!==d),f=b.RND.rgb,g=b.RND.hsl,h="hex"===a&&e,i="hex"===a&&!h,j="rgb"===a||h,k=j?f.r+", "+f.g+", "+f.b:i?"#"+b.HEX:g.h+", "+g.s+"%, "+g.l+"%";return i?k:(h?"rgb":a)+(e?"a":"")+"("+k+(e?", "+d:"")+")"},RGB2HEX:function(a){return((a.r<16?"0":"")+a.r.toString(16)+(a.g<16?"0":"")+a.g.toString(16)+(a.b<16?"0":"")+a.b.toString(16)).toUpperCase()},HEX2rgb:function(a){return a=a.split(""),{r:+("0x"+a[0]+a[a[3]?1:0])/255,g:+("0x"+a[a[3]?2:1]+(a[3]||a[1]))/255,b:+("0x"+(a[4]||a[2])+(a[5]||a[2]))/255}},hue2RGB:function(a){var b=r,c=6*a,d=~~c%6,e=6===c?0:c-d;return{r:b.round(255*[1,1-e,0,0,e,1][d]),g:b.round(255*[e,1,1,1-e,0,0][d]),b:b.round(255*[0,0,e,1,1,1-e][d])}},rgb2hsv:function(a){var b,c,d,e=r,f=a.r,g=a.g,h=a.b,i=0;return h>g&&(g=h+(h=g,0),i=-1),c=h,g>f&&(f=g+(g=f,0),i=-2/6-i,c=e.min(g,h)),b=f-c,d=f?b/f:0,{h:1e-15>d?n&&n.hsl&&n.hsl.h||0:b?e.abs(i+(g-h)/(6*b)):0,s:f?b/f:n&&n.hsv&&n.hsv.s||0,v:f}},hsv2rgb:function(a){var b=6*a.h,c=a.s,d=a.v,e=~~b,f=b-e,g=d*(1-c),h=d*(1-f*c),i=d*(1-(1-f)*c),j=e%6;return{r:[d,h,g,g,i,d][j],g:[i,d,d,h,g,g][j],b:[g,g,i,d,d,h][j]}},hsv2hsl:function(a){var b=(2-a.s)*a.v,c=a.s*a.v;return c=a.s?1>b?b?c/b:0:c/(2-b):0,{h:a.h,s:a.v||c?c:n&&n.hsl&&n.hsl.s||0,l:b/2}},rgb2hsl:function(a,b){var c=v.rgb2hsv(a);return v.hsv2hsl(b?c:n.hsv=c)},hsl2rgb:function(a){var b=6*a.h,c=a.s,d=a.l,e=.5>d?d*(1+c):d+c-c*d,f=d+d-e,g=e?(e-f)/e:0,h=~~b,i=b-h,j=e*g*i,k=f+j,l=e-j,m=h%6;return{r:[e,l,f,f,k,e][m],g:[k,e,e,l,f,f][m],b:[f,f,k,e,e,l][m]}},rgb2cmy:function(a){return{c:1-a.r,m:1-a.g,y:1-a.b}},cmy2cmyk:function(a){var b=r,c=b.min(b.min(a.c,a.m),a.y),d=1-c||1e-20;return{c:(a.c-c)/d,m:(a.m-c)/d,y:(a.y-c)/d,k:c}},cmyk2cmy:function(a){var b=a.k;return{c:a.c*(1-b)+b,m:a.m*(1-b)+b,y:a.y*(1-b)+b}},cmy2rgb:function(a){return{r:1-a.c,g:1-a.m,b:1-a.y}},rgb2cmyk:function(a,b){var c=v.rgb2cmy(a);return v.cmy2cmyk(b?c:n.cmy=c)},cmyk2rgb:function(a,b){var c=v.cmyk2cmy(a);return v.cmy2rgb(b?c:n.cmy=c)},XYZ2rgb:function(a,b){var c=r,d=m.options.XYZMatrix,e=a.X,f=a.Y,g=a.Z,h=e*d.R[0]+f*d.R[1]+g*d.R[2],i=e*d.G[0]+f*d.G[1]+g*d.G[2],j=e*d.B[0]+f*d.B[1]+g*d.B[2],l=1/2.4;return d=.0031308,h=h>d?1.055*c.pow(h,l)-.055:12.92*h,i=i>d?1.055*c.pow(i,l)-.055:12.92*i,j=j>d?1.055*c.pow(j,l)-.055:12.92*j,b||(n._rgb={r:h,g:i,b:j}),{r:k(h,0,1),g:k(i,0,1),b:k(j,0,1)}},rgb2XYZ:function(a){var b=r,c=m.options.XYZMatrix,d=a.r,e=a.g,f=a.b,g=.04045;return d=d>g?b.pow((d+.055)/1.055,2.4):d/12.92,e=e>g?b.pow((e+.055)/1.055,2.4):e/12.92,f=f>g?b.pow((f+.055)/1.055,2.4):f/12.92,{X:d*c.X[0]+e*c.X[1]+f*c.X[2],Y:d*c.Y[0]+e*c.Y[1]+f*c.Y[2],Z:d*c.Z[0]+e*c.Z[1]+f*c.Z[2]}},XYZ2Lab:function(a){var b=r,c=m.options.XYZReference,d=a.X/c.X,e=a.Y/c.Y,f=a.Z/c.Z,g=16/116,h=1/3,i=.008856,j=7.787037;return d=d>i?b.pow(d,h):j*d+g,e=e>i?b.pow(e,h):j*e+g,f=f>i?b.pow(f,h):j*f+g,{L:116*e-16,a:500*(d-e),b:200*(e-f)}},Lab2XYZ:function(a){var b=r,c=m.options.XYZReference,d=(a.L+16)/116,e=a.a/500+d,f=d-a.b/200,g=b.pow(e,3),h=b.pow(d,3),i=b.pow(f,3),j=16/116,k=.008856,l=7.787037;return{X:(g>k?g:(e-j)/l)*c.X,Y:(h>k?h:(d-j)/l)*c.Y,Z:(i>k?i:(f-j)/l)*c.Z}},rgb2Lab:function(a,b){var c=v.rgb2XYZ(a);return v.XYZ2Lab(b?c:n.XYZ=c)},Lab2rgb:function(a,b){var c=v.Lab2XYZ(a);return v.XYZ2rgb(b?c:n.XYZ=c,b)}}}(window),function(a){"use strict";var b='^§app alpha-bg-w">^§slds">^§sldl-1">$^§sldl-2">$^§sldl-3">$^§curm">$^§sldr-1">$^§sldr-2">$^§sldr-4">$^§curl">$^§curr">$$^§opacity">|^§opacity-slider">$$$^§memo">^§raster">$^§raster-bg">$|$|$|$|$|$|$|$|$^§memo-store">$^§memo-cursor">$$^§panel">^§hsv">^hsl-mode §ß">$^hsv-h-ß §ß">H$^hsv-h-~ §~">-^§nsarrow">$$^hsl-h-@ §@">H$^hsv-s-ß §ß">S$^hsv-s-~ §~">-$^hsl-s-@ §@">S$^hsv-v-ß §ß">B$^hsv-v-~ §~">-$^hsl-l-@ §@">L$$^§hsl §hide">^hsv-mode §ß">$^hsl-h-ß §ß">H$^hsl-h-~ §~">-$^hsv-h-@ §@">H$^hsl-s-ß §ß">S$^hsl-s-~ §~">-$^hsv-s-@ §@">S$^hsl-l-ß §ß">L$^hsl-l-~ §~">-$^hsv-v-@ §@">B$$^§rgb">^rgb-r-ß §ß">R$^rgb-r-~ §~">-$^rgb-r-@ §ß"> $^rgb-g-ß §ß">G$^rgb-g-~ §~">-$^rgb-g-@ §ß"> $^rgb-b-ß §ß">B$^rgb-b-~ §~">-$^rgb-b-@ §ß"> $$^§cmyk">^Lab-mode §ß">$^cmyk-c-ß §@">C$^cmyk-c-~ §~">-$^Lab-L-@ §@">L$^cmyk-m-ß §@">M$^cmyk-m-~ §~">-$^Lab-a-@ §@">a$^cmyk-y-ß §@">Y$^cmyk-y-~ §~">-$^Lab-b-@ §@">b$^cmyk-k-ß §@">K$^cmyk-k-~ §~">-$^Lab-x-@ §ß"> $$^§Lab §hide">^cmyk-mode §ß">$^Lab-L-ß §@">L$^Lab-L-~ §~">-$^cmyk-c-@ §@">C$^Lab-a-ß §@">a$^Lab-a-~ §~">-$^cmyk-m-@ §@">M$^Lab-b-ß §@">b$^Lab-b-~ §~">-$^cmyk-y-@ §@">Y$^Lab-x-ß §@"> $^Lab-x-~ §~">-$^cmyk-k-@ §@">K$$^§alpha">^alpha-ß §ß">A$^alpha-~ §~">-$^alpha-@ §ß">W$$^§HEX">^HEX-ß §ß">#$^HEX-~ §~">-$^HEX-@ §ß">M$$^§ctrl">^§raster">$^§cont">$^§cold">$^§col1">| $$^§col2">| $$^§bres">RESET$^§bsav">SAVE$$$^§exit">$^§resize">$^§resizer">|$$$'.replace(/\^/g,'
").replace(/~/g,"disp").replace(/ß/g,"butt").replace(/@/g,"labl").replace(/\|/g,"
"),c="är^1,äg^1,äb^1,öh^1,öh?1,öh?2,ös?1,öv?1,üh^1,üh?1,üh?2,üs?1,ül?1,.no-rgb-r är?2,.no-rgb-r är?3,.no-rgb-r är?4,.no-rgb-g äg?2,.no-rgb-g äg?3,.no-rgb-g äg?4,.no-rgb-b äb?2,.no-rgb-b äb?3,.no-rgb-b äb?4{visibility:hidden}är^2,är^3,äg^2,äg^3,äb^2,äb^3{@-image:url(_patches.png)}.§slds div{@-image:url(_vertical.png)}öh^2,ös^1,öv^1,üh^2,üs^1,ül^1{@-image:url(_horizontal.png)}ös?4,öv^3,üs?4,ül^3{@:#000}üs?3,ül^4{@:#fff}är?1{@-color:#f00}äg?1{@-color:#0f0}äb?1{@-color:#00f}är^2{@|-1664px 0}är^3{@|-896px 0}är?1,äg?1,äb?1,öh^3,ös^2,öv?2Ü-2432Öär?2Ü-2944Öär?3Ü-4480Öär?4Ü-3202Öäg^2Äöh^2{@|-640px 0}äg^3{@|-384px 0}äg?2Ü-4736Öäg?3Ü-3968Öäg?4Ü-3712Öäb^2{@|-1152px 0}äb^3{@|-1408px 0}äb?2Ü-3456Öäb?3Ü-4224Öäb?4Ü-2688Ööh^2Äär^3Ääb?4Ü0}öh?4,üh?4Ü-1664Öös^1,öv^1,üs^1,ül^1Ääg^3{@|-256px 0}ös^3,öv?4,üs^3,ül?4Ü-2176Öös?2,öv^2Ü-1920Öüh^2{@|-768px 0}üh^3,üs^2,ül?2Ü-5184Öüs?2,ül^2Ü-5824Ö.S är^2{@|-128px -128Ö.S är?1Ääg?1Ääb?1Äöh^3Äös^2Äöv?2Ü-1408Ö.S är?2Ääb^3Ü-128Ö.S är?3Ü-896Ö.S är?4Ü-256Ö.S äg^2{@|-256px -128Ö.S äg?2Ü-1024Ö.S äg?3Ü-640Ö.S äg?4Ü-512Ö.S äb^2{@|-128px 0}.S äb?2Ü-384Ö.S äb?3Ü-768Ö.S öh?4Äüh?4Ü-1536Ö.S ös^1Äöv^1Äüs^1Äül^1{@|-512px 0}.S ös^3Äöv?4Äüs^3Äül?4Ü-1280Ö.S ös?2Äöv^2Ü-1152Ö.S üh^2{@|-1024px 0}.S üh^3Äüs^2Äül?2Ü-5440Ö.S üs?2Äül^2Ü-5696Ö.XXS ös^2,.XXS öv?2Ü-5120Ö.XXS ös^3,.XXS öv?4,.XXS üs^3,.XXS ül^3,.XXS ül?4Ü-5056Ö.XXS ös?2,.XXS öv^2Ü-4992Ö.XXS üs^2,.XXS ül?2Ü-5568Ö.XXS üs?2,.XXS ül^2Ü-5632Ö".replace(/Ü/g,"{@|0 ").replace(/Ö/g,"px}").replace(/Ä/g,",.S ").replace(/\|/g,"-position:").replace(/@/g,"background").replace(/ü/g,".hsl-").replace(/ö/g,".hsv-").replace(/ä/g,".rgb-").replace(/~/g," .no-rgb-}").replace(/\?/g," .§sldr-").replace(/\^/g," .§sldl-"),d='∑{@#bbb;font-family:monospace, "Courier New", Courier, mono;font-size:12¥line-ä15¥font-weight:bold;cursor:default;~412¥ä323¥?top-left-radius:7¥?top-Ü-radius:7¥?bottom-Ü-radius:7¥?bottom-left-radius:7¥ö@#444}.S{~266¥ä177px}.XS{~158¥ä173px}.XXS{ä105¥~154px}.no-alpha{ä308px}.no-alpha .§opacity,.no-alpha .§alpha{display:none}.S.no-alpha{ä162px}.XS.no-alpha{ä158px}.XXS.no-alpha{ä90px}∑,∑ div{border:none;padding:0¥float:none;margin:0¥outline:none;box-sizing:content-box}∑ div{|absolute}^s .§curm,«§disp,«§nsarrow,∑ .§exit,∑ ø-cursor,∑ .§resize{öimage:url(_icons.png)}∑ .do-drag div{cursor:none}∑ .§opacity,ø .§raster-bg,∑ .§raster{öimage:url(_bgs.png)}∑ ^s{~287¥ä256¥top:10¥left:10¥overflow:hidden;cursor:crosshair}.S ^s{~143¥ä128¥left:9¥top:9px}.XS ^s{left:7¥top:7px}.XXS ^s{left:5¥top:5px}^s div{~256¥ä256¥left:0px}.S ^l-1,.S ^l-2,.S ^l-3,.S ^l-4{~128¥ä128px}.XXS ^s,.XXS ^s ^l-1,.XXS ^s ^l-2,.XXS ^s ^l-3,.XXS ^s ^l-4{ä64px}^s ^r-1,^s ^r-2,^s ^r-3,^s ^r-4{~31¥left:256¥cursor:default}.S ^r-1,.S ^r-2,.S ^r-3,.S ^r-4{~15¥ä128¥left:128px}^s .§curm{margin:-5¥~11¥ä11¥ö|-36px -30px}.light .§curm{ö|-7px -30px}^s .§curl,^s .§curr{~0¥ä0¥margin:-3px -4¥border:4px solid;cursor:default;left:auto;öimage:none}^s .§curl,∑ ^s .§curl-dark,.hue-dark div.§curl{Ü:27¥?@† † † #fff}.light .§curl,∑ ^s .§curl-light,.hue-light .§curl{?@† † † #000}.S ^s .§curl,.S ^s .§curr{?~3px}.S ^s .§curl-light,.S ^s .§curl{Ü:13px}^s .§curr,∑ ^s .§curr-dark{Ü:4¥?@† #fff † †}.light .§curr,∑ ^s .§curr-light{?@† #000 † †}∑ .§opacity{bottom:44¥left:10¥ä10¥~287¥ö|0 -87px}.S .§opacity{bottom:27¥left:9¥~143¥ö|0 -100px}.XS .§opacity{left:7¥bottom:25px}.XXS .§opacity{left:5¥bottom:23px}.§opacity div{~100%;ä16¥margin-top:-3¥overflow:hidden}.§opacity .§opacity-slider{margin:0 -4¥~0¥ä8¥?~4¥?style:solid;?@#eee †}∑ ø{bottom:10¥left:10¥~288¥ä31¥ö@#fff}.S ø{ä15¥~144¥left:9¥bottom:9px}.XS ø{left:7¥bottom:7px}.XXS ø{left:5¥bottom:5px}ø div{|relative;float:left;~31¥ä31¥margin-Ü:1px}.S ø div{~15¥ä15px}∑ .§raster,ø .§raster-bg,.S ø .§raster,.S ø .§raster-bg{|absolute;top:0¥Ü:0¥bottom:0¥left:0¥~100%}.S ø .§raster-bg{ö|0 -31px}∑ .§raster{opacity:0.2;ö|0 -49px}.alpha-bg-b ø{ö@#333}.alpha-bg-b .§raster{opacity:1}ø ø-cursor{|absolute;Ü:0¥ö|-26px -87px}∑ .light ø-cursor{ö|3px -87px}.S ø-cursor{ö|-34px -95px}.S .light ø-cursor{ö|-5px -95px}∑ .§panel{|absolute;top:10¥Ü:10¥bottom:10¥~94¥?~1¥?style:solid;?@#222 #555 #555 #222;overflow:hidden;ö@#333}.S .§panel{top:9¥Ü:9¥bottom:9px}.XS .§panel{display:none}.§panel div{|relative}«§hsv,«§hsl,«§rgb,«§cmyk,«§Lab,«§alpha,.no-alpha «§HEX,«§HEX{~86¥margin:-1px 0px 1px 4¥padding:1px 0px 3¥?top-~1¥?top-style:solid;?top-@#444;?bottom-~1¥?bottom-style:solid;?bottom-@#222;float:Ö«§hsv,«§hsl{padding-top:2px}.S .§hsv,.S .§hsl{padding-top:1px}«§HEX{?bottom-style:none;?top-~0¥margin-top:-4¥padding-top:0px}.no-alpha «§HEX{?bottom-style:none}«§alpha{?bottom-style:none}.S .rgb-r .§hsv,.S .rgb-g .§hsv,.S .rgb-b .§hsv,.S .rgb-r .§hsl,.S .rgb-g .§hsl,.S .rgb-b .§hsl,.S .hsv-h .§rgb,.S .hsv-s .§rgb,.S .hsv-v .§rgb,.S .hsl-h .§rgb,.S .hsl-s .§rgb,.S .hsl-l .§rgb,.S .§cmyk,.S .§Lab{display:none}«§butt,«§labl{float:left;~14¥ä14¥margin-top:2¥text-align:center;border:1px solid}«§butt{?@#555 #222 #222 #555}«§butt:active{ö@#444}«§labl{?@†}«Lab-mode,«cmyk-mode,«hsv-mode,«hsl-mode{|absolute;Ü:0¥top:1¥ä50px}«hsv-mode,«hsl-mode{top:2px}«cmyk-mode{ä68px}.hsl-h .hsl-h-labl,.hsl-s .hsl-s-labl,.hsl-l .hsl-l-labl,.hsv-h .hsv-h-labl,.hsv-s .hsv-s-labl,.hsv-v .hsv-v-labl{@#f90}«cmyk-mode,«hsv-mode,.rgb-r .rgb-r-butt,.rgb-g .rgb-g-butt,.rgb-b .rgb-b-butt,.hsv-h .hsv-h-butt,.hsv-s .hsv-s-butt,.hsv-v .hsv-v-butt,.hsl-h .hsl-h-butt,.hsl-s .hsl-s-butt,.hsl-l .hsl-l-butt,«rgb-r-labl,«rgb-g-labl,«rgb-b-labl,«alpha-butt,«HEX-butt,«Lab-x-labl{?@#222 #555 #555 #222;ö@#444}.no-rgb-r .rgb-r-labl,.no-rgb-g .rgb-g-labl,.no-rgb-b .rgb-b-labl,.mute-alpha .alpha-butt,.no-HEX .HEX-butt,.cmy-only .Lab-x-labl{?@#555 #222 #222 #555;ö@#333}.Lab-x-disp,.cmy-only .cmyk-k-disp,.cmy-only .cmyk-k-butt{visibility:hidden}«HEX-disp{öimage:none}«§disp{float:left;~48¥ä14¥margin:2px 2px 0¥cursor:text;text-align:left;text-indent:3¥?~1¥?style:solid;?@#222 #555 #555 #222}∑ .§nsarrow{|absolute;top:0¥left:-13¥~8¥ä16¥display:none;ö|-87px -23px}∑ .start-change .§nsarrow{display:block}∑ .do-change .§nsarrow{display:block;ö|-87px -36px}.do-change .§disp{cursor:default}«§hide{display:none}«§cont,«§cold{|absolute;top:-5¥left:0¥ä3¥border:1px solid #333}«§cold{z-index:1;ö@#c00}«§cont{margin-Ü:-1¥z-index:2}«contrast .§cont{z-index:1;ö@#ccc}«orange .§cold{ö@#f90}«green .§cold{ö@#4d0}«§ctrl{|absolute;bottom:0¥left:0¥~100%;ö@#fff}.alpha-bg-b .§ctrl,«§bres,«§bsav{ö@#333}«§col1,«§col2,«§bres,«§bsav{?~1¥?style:solid;?@#555 #222 #222 #555;float:left;~45¥line-ä28¥text-align:center;top:0px}.§panel div div{ä100%}.S .§ctrl div{line-ä25px}.S «§bres,.S «§bsav{line-ä26px}∑ .§exit,∑ .§resize{Ü:3¥top:3¥~15¥ä15¥ö|0 -52px}∑ .§resize{top:auto;bottom:3¥cursor:nwse-resize;ö|-15px -52px}.S .§exit{ö|1px -52px}.XS .§resize,.XS .§exit{~10¥ä10¥Ü:0¥öimage:none}.XS .§exit{top:0px}.XS .§resize{bottom:0px}∑ .§resizer,∑ .§resizer div{|absolute;border:1px solid #888;top:-1¥Ü:-1¥bottom:-1¥left:-1¥z-index:2;display:none;cursor:nwse-resize}∑ .§resizer div{border:1px dashed #333;opacity:0.3;display:block;ö@#bbb}'.replace(/Ü/g,"right").replace(/Ö/g,"left}").replace(/∑/g,".§app").replace(/«/g,".§panel .").replace(/¥/g,"px;").replace(/\|/g,"position:").replace(/@/g,"color:").replace(/ö/g,"background-").replace(/ä/g,"height:").replace(/ø/g,".§memo").replace(/†/g,"transparent").replace(/\~/g,"width:").replace(/\?/g,"border-").replace(/\^/g,".§sld"),e="iVBORw0KGgoAAAANSUhEUgAABIAAAAABCAYAAACmC9U0AAABT0lEQVR4Xu2S3Y6CMBCFhyqIsjGBO1/B9/F5DC/pK3DHhVkUgc7Zqus2DVlGU/cnQZKTjznttNPJBABA149HyRf1iN//4mIBCg0jV4In+j9xJiuihly1V/Z9X88v//kNeDXVvyO/lK+IPR76B019+1Riab3H1zkmeqerKnL+Bzwxx6PAgZxaSQU8vB62T28pxcQeRQ2sHw6GxCOWHvP78zwHAARBABOfdYtd30rwxXOEPDF+dj2+91r6vV/id3k+/brrXmaGUkqKhX3i+ffSt16HQ/dorTGZTHrs7ev7Tl7XdZhOpzc651nfsm1bRFF0YRiGaJoGs9nsQuN/xafTCXEco65rzOdzHI9HJEmCqqqwXC6x3++RZRnKssRqtUJRFFiv19jtdthutyAi5Hl+Jo9VZg7+7f3yXuvZf5c3KaXYzByb+WIzO5ymKW82G/0BNcFhO/tOuuMAAAAASUVORK5CYII=",f="iVBORw0KGgoAAAANSUhEUgAAAAEAABfACAYAAABn2KvYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABHtJREFUeNrtnN9SqzAQxpOF1to6zuiVvoI+j6/gva/lA/kKeqUzjtX+QTi7SzSYBg49xdIzfL34+e1usoQQklCnmLwoCjImNwDQA2xRGMqNAYB+gPEH9IdCgIUA6Aem0P1fLoMQAPYNHYDoCKAv8OMHFgKgX2AjDPQDXn4t1l+gt/1fId//yWgE/hUJ+mAn8EyY5wCwXxhrbaHzn8E9iPlv79DdHxXTqciZ4KROnXRVZMF/6U2OPhcEavtAbZH1SM7wRDD7VoHZItCiyEQf4t6+MW9UOxaZybmdCGKqNrB9Eb5SfMg3wTyiagMtigTmWofiSDCOYNTSNz6sLDIoaCU9GWDd0tdhoMMsRm+r8U/EfB0GfjmLXiqzimDd0tdhoLMsI7la45+I+ToM/HIW0kfGVQTrlr7tA91kaUr//fxrKo8jUFB7VAn6AKpHJf+EKwAAAIYD/f7F7/8MVgMo7P+gBqDKr57Lf72V8x8AAMDgYIuvH4EAAAAMDQX6AACAQcI9GGMjDADA4MA/P2KlP8IEAAAYFCz6AACAgaLA8y8AAIN+CMYXoQAADA7u/UPYCAMAMDjI7z9S+SdwDFQX2C9Gh9GMEOWriz8/Pw1lWQZsi/L3R4czzP678Ve+P8f9nCv/C7hwLq99ah8NfKrU15zPB5pVcwtiJt9qGy0IfEE+jQa+Fn0VtI/fkxUPqBlEfRENeF+tqUpbGpi1iu8epwJzvV5XA4GpWC6XGz7F+/u766EgwJ+ckiTJKU3TnI6OjnI6OzvLZf6zMggt3dzckPhIoiTlSGpQ+eEsVegdz0fbCCi4fRs+Po+4yWdeDXiT+6pBSTeHple1pkz3FZ+avpyavoiPxgLN0B7yprY08PlyQTTm0+PWmkH7ynedNKraar4F/lRj1WpTtYh+ozL/cY2sAvZl0gcbZm0gSLBLvkxGoaogiy/HDXemQk2t5pUm8OAhH8/HH6e0mkJ9q9XKKQXfb07xfZnJbZrRxcVFVt6/t7e3Kc1ms5RGo1Eq5VIZuyl9fHw4k/M5xYeoKj64A7eqCt1ZeqWFVSl8NV9OTV3fmvP5qE9VmzSoEcsXpArK1UHen/hZbgL53BZSdyEXalGau/hU8TEW0u3VcoFPy3EDFrTgT+njydeZ0+l0UV7fu7u7iVzziQQmUm4iqRw4n/NxMxw4s/Mp1NSALxf4NEtQ10cjMDwSl+b+/j6hp6enVGb+jUvrn05iKobm6PboOt8vPISY5Pr6OqGXlxe3fOokoGtAbMUJZmqvYmaLQDP+sdrecOjtO/SXeH69P8Imutm5urqy9PDwYOny8tLS4+OjpfPzc0vPz8+WTk9PLb2+vlpZbCzN53NLx8fHVtYZS5PJxMoEZWWqsjKULY3HYytTi1Pex5OMldXKRVXxuLcy/20onmms3BBOxcr5qCrZtsrd45SPel8sGlOxGoGy0neynQ6VL9fsa1YtWlCrtj9G83G7PjdVush5n5q1iJWLZW6u21a1bUvbVnVzlru0pe3RdmlV1/23fZtbZv4Dx+7FBypx77kAAAAASUVORK5CYII=",g="iVBORw0KGgo^NSUhEUgAAB4^EACAI#DdoPxz#L0UlEQVR4Xu3cQWrDQBREwR7FF8/BPR3wXktnQL+KvxfypuEhvLJXcp06d/bXd71OPt+trIw95zr33Z1bk1/fudEv79wa++7OfayZ59wrO2PBzklcGQmAZggAAOBYgAYBmpWRAGg^BGgRofAENgAAN#I0CBA6w8AG^ECABgEa/QH§AI0CNDoDwAY^QIAGAVp/AM§AjQI0OgPAAY^QoEGARn8Aw§CNAjQ+gMABg#BCgQYCmGQmABgAAEKBBgEZ/AM§AjQI0PoDAAY^QoEGARn8AM^IAADQI0+gMABg#BCgQYDWHwAw^gAANAjT6A4AB^BGgQoNEfAD^C#0CtP4AgAE^EaBCgaUYCoAE#RoEKDRHwAw^gAANArT+AIAB^BGgQoNEfAAw^gQIMAjf4AgAE^EaBCg9QcAD^CBAgwCN/gBg§EaBGj0BwAM^IECDAK0/AG§ARoEaJqRAGg^BGgRo9AcAD^CBAgwCtPwBg§EaBGj0BwAD^CNAgQKM/AG§ARoEaP0BAAM^I0CBAoz8AG^ECABgEa/QEAAw^jQIEDrDwAY^QIAGAZpmJACaBw^RoEKD1BwAM^IECDAK0/AG§ARoEaPQHAAw^gQIMArT8AY§BGgRo/QEAAw^jQIECjPwBg§EaBGj9AQAD^CNAgQOsPABg#BAgAYBGv0BAANwCwAAGB6gYeckmpEAa^AEaBGj0BwAM^IECDAK0/AG§ARoEaPQHAAM^I0CBAoz8AY§BGgRo/QEAAw^jQIECjPwAY^QIAGARr9AQAD^CNAgQOsPABg#BAgAYBmmYkABoAAECABgEa/QEAAw^jQIEDrDwAY^QIAGARr9Ac§AjQI0OgPABg#BAgAYBWn8Aw§CNAjQ6A8ABg#BCgQYBGfwD§AI0CND6AwAG^EKBBgKYZCYAG#QoEGARn8Aw§CNAjQ+gMABg#BCgQYBGfwAw^gAANAjT6AwAG^EKBBgNYfAD^C#0CNPoDgAE^EaBCg0R8AM^IAADQK0/gCAAQ^RoEKBpRgKgAQAABGgQoNEfAD^C#0CtP4AgAE^EaBCg0R8AD^CBAgwCN/gCAAQ^RoEKD1BwAM^IECDAI3+AG§ARoEaPQHAAw^gQIMArT8AY§BGgRomsMAM^IAADQK0/gCAAQ^RoEKDRHwAw^gAANO7fQHwAw^gAANArT+AIAB^BGgQoNEfAGg^BGgRo9AcAD^CBAgwCtPwBg§EaBGj0BwAD^RIB+Ntg5iea5AD^DAIwI0CND6AwAG^EKBBgEZ/AKAB#EaBCg0R8AM^IAADQK0/gCAAQ^RoEKDRHwAM^IECDAI3+AIAB^BGgQoPUHAAw^gQIMAjf4AY§BGgRo9AcAD^CBAgwCtPwBg§EaBGiakQBo^ARoEaPQHAAw^gQIMArT8AY§BGgRo9AcAAw^jQIECjPwBg§EaBGj9AQAD^CNAgQKM/ABg#BAgAYBGv0BAAM^I0CBA6w8AG^ECABgGaZiQAGgAAQIAGARr9AQAD^CNAgQOsPABg#BAgAYBGv0Bw§CNAjQ6A8AG^ECABgFafwD§AI0CNDoDwAG^EKBBgEZ/AM§AjQI0PoDAAY^QoEGApjkMAAM^I0CBA6w8AG^ECABgEa/QEAAw^jQsIP+AIAB^BGgQoPUHAAw^gQIMAjf4AgAE#Bea/fK+3P5/3PJOvh8t1cO4nflmQAQoAEAAF9Aw/7JHfQHAAw^gQIMArT8AY§BGvwHNPoDAA0AACBAgwCN/gCAAQ^RoEKD1BwAM^IECDAI3+AG§ARoEaPQHAAw^gQIMArT8AY§BGgRo9AcAAw^jQIECjPwBg§EaBGj9AQAD^CNAgQNOMBEAD#I0CBAoz8AY§BGgRo/QEAAw^jQIECjPwAY^QIAGARr9AQAD^CNAgQOsPABg#BAgAYBGv0Bw§CNAjQ6A8AG^ECABgFafwD§AI0CNA0IwHQ^AjQI0OgPABg#BAgAYBWn8Aw§CNAjQ6A8ABg#BCgQYBGfwD§AI0CND6AwAG^EKBBgEZ/AD^C#0CNPoDAAY^QoEGA1h8AM^IAADQI0DQAG^EKBBgEZ/AM§AjQI0PoDAAY^QoEGA1h8AM^IAADQI0+gMABg#BCgQYDWHwAw^gAANArT+AIAB^BGgQoNEfAD^C#0CtP4AgAE^EaBCg9QcAD^CBAgwCN/gCAAQ^RoEKD1BwAM^IECDAK0/AG§ARoEaPQHAAw^gQIMArT8AY§BGgRo/QEAAw^jQIECjPwBgACDhFgC#07t9AfAD^C#0CtP4AgAE^EaBCg0R8Aa^AEaBGj0BwAM^IECDAK0/AG§ARoEaPQHAAM^I0CBAoz8AY§BGgRo/QEAAw^jQIECjPwAY^QIAGARr9AQAD^CNAgQOsPABg#BAgAYBmmYkABoAAECABgEa/QEAAw^jQIEDrDwAY^QIAGARr9Ac§AjQI0OgPABg#BAgAYBWn8Aw§CNAjQ6A8ABg#BCgQYBGfwD§AI0CND6AwAG^EKBBgKYZCYAG#QoEGARn8Aw§CNAjQ+gMABg#BCgQYBGfwAw^gAANAjT6AwAG^EKBBgNYfAD^C#0CNPoDgAE^EaBCg0R8AM^IAADQK0/gCAAQ^RoEKBpRgKgAQAABGgQoNEfAD^C#0CtP4AgAE^EaBCg0R8AD^CBAgwCN/gCAAQ^RoEKD1BwAM^IECDAI3+AG§ARoEaPQHAAw^gQIMArT8AY§BGgRommEAM^CBAgwCN/gCAAQ^RoEKD1BwAM^IECDAI3+AIAB^ARoEaPQHAAw^gQIMArT8AY§BGgRo9AcAGgAAQICGCNBfRfNcABg#BgeICGnVvoDwAY^QIAGAVp/AM§AjQI0OgPADQAAIAADQI0+gMABg#BCgQYDWHwAw^gAANAjT6A4AB^BGgQoNEfAD^C#0CtP4AgAE^EaBCg0R8AD^CBAgwCN/gCAAQ^RoEKD1BwAM^IECDAE0zEgAN#gQIMAjf4AgAE^EaBCg9QcAD^CBAgwCN/gBg§EaBGj0BwAM^IECDAK0/AG§ARoEaPQHAAM^I0CBAoz8AY§BGgRo/QEAAw^jQIEDTjARAAwAACNAgQKM/AG§ARoEaP0BAAM^I0CBAoz8AG^ECABgEa/QEAAw^jQIEDrDwAY^QIAGARr9Ac§AjQI0OgPABg#BAgAYBWn8Aw§CNAjQNIcBY§BGgRo/QEAAw^jQIECjPwBg§EadtAfAD^C#0CtP4AgAE^EaBCgAQABGgAA+AO2TAbHupOgH^ABJRU5ErkJggg==".replace(/§/g,"AAAAAA").replace(/\^/g,"AAAA").replace(/#/g,"AAA"),h="iVBORw0KGgoAAAANSUhEUgAAAGEAAABDCAMAAAC7vJusAAAAkFBMVEUAAAAvLy9ERERubm7///8AAAD///9EREREREREREREREQAAAD///8AAAD///8AAAD///8AAAD///8AAAD///8AAAD///8AAAD///8AAAD///8AAAD///8cHBwkJCQnJycoKCgpKSkqKiouLi4vLy8/Pz9AQEBCQkJDQ0NdXV1ubm58fHykpKRERERVVVUzMzPx7Ab+AAAAHXRSTlMAAAAAAAQEBQ4QGR4eIyMtLUVFVVVqapKSnJy7u9JKTggAAAFUSURBVHja7dXbUoMwEAbgSICqLYeW88F6KIogqe//dpoYZ0W4AXbv8g9TwkxmvtndZMrEwlw/F8YIRjCCEYxgBCOsFmzqGMEI28J5zzmt0Pc9rdDL0NYgMxIYC5KiKpKAzZphWtZlGm4SjlnkOV6UHeeEUx77rh/npw1dCrI9k9lnwUwF+UG9D3m4ftJJxH4SJdPtaawXcbr+tBaeFrxiur309cIv19+4ytGCU0031a5euPVigLYGqjlAqM4ShOQ+QAYQUO80AMMAAkUGGfMfR9Ul+kmvPq2QGxXKOQBAKdjUgk0t2NiCGEVP+rHT3/iCUMBT90YrPMsKsIWP3x/VolaonJEETchHCS8AYAmaUICQQwaAQnjoXgHAES7jLkEFaHO4bdq/k25HAIpgWY34FwAE5xjCffM+D2DV8B0gRsAZT7hr5gE8wdrJcU+CJqhcqQD7Cx5L7Ph4WnrKAAAAAElFTkSuQmCC",i="iVBORw0KGgoAAAANSUhEUgAAASAAAABvCAYAAABM+h2NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABORJREFUeNrs3VtTW1UYBuCEcxAI4YydWqTWdqr1V7T/2QsvvPDCCy9qjxZbamsrhZIQUHsCEtfafpmJe8qFjpUxfZ4Zuvt2feydJvAOARZUut1u5bRerl692nV913f99/f6QxWAU6KAAAUEKCAABQQoIAAFBCggAAUEKCAABQQoIAAFBCggAAUEKCAABQQoIEABASggQAEBKCBAAQEoIEABASggQAEBKCBAAQEoIGBQC+jatWvd07zxrv9+Xx8fAQEoIEABASggQAEBKCBAAQEoIEABAQoIQAEBCghAAQEKCEABAQOk2u36kS6AAgLetwJKL29toFRM1be+QrVq3rx58//KvM8BAadGAQEKCFBAAAoIGHwnfhneZ+/Nmzf/LufzrI+AAE/BAAUEoIAABQTwztgLZt68eXvBAE/BABQQoIAAFBAweOwFM2/evL1ggKdgAAoIUEAACggYPPaCmTdv3l4wwFMwAAUEKCAABQQMHnvBzJs3by8Y4CkYgAICFBCAAgIGz4lfBQNQQMDgFlCtVisaaHV1tThubW1VInciD0U+ysdnz54N5+PKysphOnRTHsvHlN9EHo/1l5FrkV9Enoz8W87b29tTOS8vLx9EnoncjlyPvBe5EbkZeT4fU96NvBDr2znv7Ows57y0tLQVeSXy08gf5mNfPhPrjyOfrVarlcXFxZ9yfv78+bl8TPlh5LU8n/KDyOuxfj/y+VjfyHl3d/dCKv28fi/yp/m4sLDwQ+SLke9GvhT5Tinfjnw5f4/F/Pz8rZybzeZn+ZjyzVK+EfnzUr4S+Xopf9/L+fxzc3M5d1qt1hf531Mu5k/IxzGf85VYL+fefHH+RqNRrO/t7RW3L+UbkS9Hvhk5/386Kd/qW8/5duRLMV/OdyJfzNebnZ0t7t92u53v/07K9yJfiLwROT9+ef7HyOux/iDyWuSHkT+K+eLtZX9//2xer9frjyOfyY9/Wn8S86v59qT1p7Ge315zLt4RU16K19+O9YXIu5HnYn435hux3opcj9yOPB3z+5E/iPXf43y1yMX778HBQS3f3pTz+28l5bHIr2N+LN3+zszMzGHkoh/S+mHMF98XlNaP8zHd/0W/pMe943NAwKlSQIACAhQQgAICFBCAAgIUEIACAhQQgAIC/n9GqtXqYbfbHa38+RtSu32llPdqdNL6aOSj+LfxyMVekLTem39Ryr/mPDQ0NBznzXtROikPRW6W8k7k3m9rzXthOsPDw73bUuylGRkZ6cR63nvTSfko8oPIr+Pnz96P/DLW816ezujoaN6DdtyX9+P8eS9QZ2xs7Hxf7qa8Xlr/JO6Ljcjrcf6cj1P+OO+N6V1/fHz8XLz+/Tjfubh+sZcorZ+N9Ycxfybyo8ircf6fc56YmFiJ1/8l8mLk7cjzkfP92U15Ns63G+u9nPcKdWq12lQ8Xu3Ixd6f9Pd8P3UmJycnUszzL2N9LM7/anNzs9V7Q2q32395w/q7ubdH6L/KrVbrpPxlKX9Vyl+X8jel/G0pf5f/aDabvXy9tH6ztH63lDdKebOUH5Xyk1LeKuWd/ry2tlap9P125Onp6Zf9eWpq6lW3b8f6zMzM6/71er3+ppSP+u/XNN/pz41Go+sjIMBTMEABASggQAEBKCBAAQEoIEABASggQAEB/CN/CDAAw78uW9AVDw4AAAAASUVORK5CYII=";a.ColorPicker={_html:b,_cssFunc:c,_cssMain:d,_horizontalPng:e,_verticalPng:f,_patchesPng:g,_iconsPng:h,_bgsPng:i}}(window),function(a,b){"use strict";function c(c,e){var j,k="",l="";for(var m in e)c.options[m]=e[m];Q=document.createStyleSheet!==b&&document.getElementById||!!a.MSInputMethodContext,R="undefined"!=typeof document.body.style.opacity,_=new Colors(c.options),delete c.options,bb=_.options,bb.scale=1,l=bb.CSSPrefix,c.color=_,S=bb.valueRanges,c.nodes=cb=g(f(c),c),q(bb.mode),d(c),u(),k=" "+bb.mode.type+"-"+bb.mode.z,cb.slds.className+=k,cb.panel.className+=k,bb.noHexButton&&C(cb.HEX_butt,l+"butt",l+"labl"),bb.size!==b&&p(b,bb.size),j={alphaBG:cb.alpha_labl,cmyOnly:cb.HEX_labl};for(var n in j)bb[n]!==b&&o({target:j[n],data:bb[n]});bb.noAlpha&&(cb.colorPicker.className+=" no-alpha"),c.renderMemory(bb.memoryColors),h(c),I=!0,i(b,"init"),N&&(d(N),w())}function d(a){Y=!0,M!==a&&(M=a,ab=a.color.colors,bb=a.color.options,cb=a.nodes,_=a.color,$={},v(ab))}function e(){var a=["L","S","XS","XXS"];bb.sizes={},cb.testNode.style.cssText="position:absolute;left:-1000px;top:-1000px;",document.body.appendChild(cb.testNode);for(var b=a.length;b--;)cb.testNode.className=bb.CSSPrefix+"app "+a[b],bb.sizes[a[b]]=[cb.testNode.offsetWidth,cb.testNode.offsetHeight];cb.testNode.removeNode?cb.testNode.removeNode(!0):document.body.removeChild(cb.testNode)}function f(a){var b=document.createElement("div"),c=bb.CSSPrefix,d="data:image/png;base64,",e=function(a,b){var c=document.createElement("style");c.setAttribute("type","text/css"),b&&c.setAttribute("id",b),c.styleSheet||c.appendChild(document.createTextNode(a)),document.getElementsByTagName("head")[0].appendChild(c),c.styleSheet&&(document.styleSheets[document.styleSheets.length-1].cssText=a)},f=function(a){O._cssFunc=O._cssFunc.replace(/§/g,c).replace("_patches.png",a?d+O._patchesPng:bb.imagePath+"_patches.png").replace("_vertical.png",a?d+O._verticalPng:bb.imagePath+"_vertical.png").replace("_horizontal.png",a?d+O._horizontalPng:bb.imagePath+"_horizontal.png"),e(O._cssFunc,"colorPickerCSS"),bb.customCSS||(O._cssMain=O._cssMain.replace(/§/g,c).replace("_bgs.png",a?d+O._bgsPng:bb.imagePath+"_bgs.png").replace("_icons.png",a?d+O._iconsPng:bb.imagePath+"_icons.png").replace(/opacity:(\d*\.*(\d+))/g,function(a,b){return R?"-moz-opacity: "+b+"; -khtml-opacity: "+b+"; opacity: "+b:'-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity='+db.round(100*+b)+')";filter: alpha(opacity='+db.round(100*+b)+")"}),e(O._cssMain))},g=document.createElement("img");return P?a.color.options.devPicker:(document.getElementById("colorPickerCSS")?a.cssIsReady=!0:(g.onload=g.onerror=function(){O._cssFunc&&f(1===this.width&&1===this.height),a.cssIsReady=!0},g.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="),(N=M)&&r(),b.insertAdjacentHTML("afterbegin",M?M.nodes.colorPicker.outerHTML||(new XMLSerializer).serializeToString(M.nodes.colorPicker):O._html.replace(/§/g,c)),b=b.children[0],b.style.cssText=bb.initStyle||"",(bb.appendTo||document.body).appendChild(b))}function g(a){var b,c,d=a.getElementsByTagName("*"),e={colorPicker:a},f=new RegExp(bb.CSSPrefix);e.styles={},e.textNodes={},e.memos=[],e.testNode=document.createElement("div");for(var g=0,h=d.length;h>g;g++)b=d[g],(c=b.className)&&f.test(c)?(c=c.split(" ")[0].replace(bb.CSSPrefix,"").replace(/-/g,"_"),/_disp/.test(c)?(c=c.replace("_disp",""),e.styles[c]=b.style,e.textNodes[c]=b.firstChild,b.contentEditable=!0):(/(?:hs|cmyk|Lab).*?(?:butt|labl)/.test(c)||(e[c]=b),/(?:cur|sld[^s]|opacity|cont|col)/.test(c)&&(e.styles[c]=/(?:col\d)/.test(c)?b.children[0].style:b.style))):/memo/.test(b.parentNode.className)&&e.memos.push(b);return e.panelCover=e.panel.appendChild(document.createElement("div")),e}function h(c,f){var g=f?G:F;g(cb.colorPicker,"mousedown",function(f){var g=f||a.event,h=E(g),n=(g.button||g.which)<2?g.target||g.srcElement:{},o=n.className;return d(c),J=n,i(b,"resetEventListener"),U="",n===cb.sldl_3||n===cb.curm?(J=cb.sldl_3,I=j,U="changeXYValue",C(cb.slds,"do-drag")):/sldr/.test(o)||n===cb.curl||n===cb.curr?(J=cb.sldr_4,I=k,U="changeZValue"):n===cb.opacity.children[0]||n===cb.opacity_slider?(J=cb.opacity,I=l,U="changeOpacityValue"):/-disp/.test(o)&&!/HEX-/.test(o)?(I=m,U="changeInputValue",(3===n.nextSibling.nodeType?n.nextSibling.nextSibling:n.nextSibling).appendChild(cb.nsarrow),K=o.split("-disp")[0].split("-"),K={type:K[0],z:K[1]||""},C(cb.panel,"start-change"),V=0):n!==cb.resize||bb.noResize?I=b:(bb.sizes||e(),J=cb.resizer,I=p,U="resizeApp"),I&&(W={pageX:h.X,pageY:h.Y},J.style.display="block",X=D(J),X.width=cb.opacity.offsetWidth,X.childWidth=cb.opacity_slider.offsetWidth,J.style.display="",I(g),F(Q?document.body:a,"mousemove",I),L=a[fb](w)),/-disp/.test(o)?void 0:B(g)}),g(cb.colorPicker,"click",function(a){d(c),o(a)}),g(cb.colorPicker,"dblclick",o),g(cb.colorPicker,"keydown",function(a){d(c),n(a)}),g(cb.colorPicker,"keypress",n),g(cb.colorPicker,"paste",function(a){return a.target.firstChild.data=a.clipboardData.getData("Text"),B(a)})}function i(c,d){var e=I;I&&(a[gb](L),G(Q?document.body:a,"mousemove",I),V&&(K={type:"alpha"},w()),("function"==typeof I||"number"==typeof I)&&delete bb.webUnsave,V=1,I=b,C(cb.slds,"do-drag",""),C(cb.panel,"(?:start-change|do-change)",""),cb.resizer.style.cssText="",cb.panelCover.style.cssText="",cb.memo_store.style.cssText="background-color: "+y(ab.RND.rgb)+"; "+A(ab.alpha),cb.memo.className=cb.memo.className.replace(/\s+(?:dark|light)/,"")+(ab["rgbaMix"+T[bb.alphaBG]].luminance<.22?" dark":" light"),K=b,s(),bb.actionCallback&&bb.actionCallback(c,U||e.name||d||"external"))}function j(b){var c=b||a.event,d=bb.scale,e=E(c),f=(e.X-X.left)*(4===d?2:d),g=(e.Y-X.top)*d,h=bb.mode;return ab[h.type][h.x]=z(f/255,0,1),ab[h.type][h.y]=1-z(g/255,0,1),t(),B(c)}function k(b){var c=b||a.event,d=E(c),e=(d.Y-X.top)*bb.scale,f=bb.mode;return ab[f.type][f.z]=1-z(e/255,0,1),t(),B(c)}function l(b){var c=b||a.event,d=E(c);return Y=!0,ab.alpha=z(db.round((d.X-X.left)/X.width*100),0,100)/100,t("alpha"),B(c)}function m(b){var c,d=b||a.event,e=E(d),f=W.pageY-e.Y,g=bb.delayOffset,h=K.type,i="alpha"===h;return V||db.abs(f)>=g?(V||(V=(f>0?-g:g)+ +J.firstChild.data*(i?100:1),W.pageY+=V,f+=V,V=1,C(cb.panel,"start-change","do-change"),cb.panelCover.style.cssText="position:absolute;left:0;top:0;right:0;bottom:0",document.activeElement.blur(),L=a[fb](w)),"cmyk"===h&&bb.cmyOnly&&(h="cmy"),i?(Y=!0,ab.alpha=z(f/100,0,1)):(c=S[h][K.z],ab[h][K.z]="Lab"===h?z(f,c[0],c[1]):z(f/c[1],0,1)),t(i?"alpha":h),B(d)):void 0 +}function n(c){var d,e=c||a.event,f=e.which||e.keyCode,g=String.fromCharCode(f),h=document.activeElement,j=h.className.replace(bb.CSSPrefix,"").split("-"),k=j[0],l=j[1],m="alpha"===k,n="HEX"===k,o={k40:-1,k38:1,k34:-10,k33:10}["k"+f]/(m?100:1),p={HEX:/[0-9a-fA-F]/,Lab:/[\-0-9]/,alpha:/[\.0-9]/}[k]||/[0-9]/,q=S[k][k]||S[k][l],r=h.firstChild,s=H(h),u=r.data,w="0"!==u||n?u.split(""):[];return/^(?:27|13)$/.test(f)?(B(e),h.blur()):"keydown"===e.type?(o?d=z(db.round(1e6*(+u+o))/1e6,q[0],q[1]):/^(?:8|46)$/.test(f)&&(s.range||(s.range++,s.start-=8===f?1:0),w.splice(s.start,s.range),d=w.join("")||"0"),d!==b&&B(e,!0)):"keypress"===e.type&&(/^(?:37|39|8|46|9)$/.test(f)||B(e,!0),p.test(g)&&(w.splice(s.start,s.range,g),d=w.join("")),s.start++),13===f&&n?r.data.length%3===0||"0"===r.data?M.setColor("0"===r.data?"000":r.data,"rgb",ab.alpha,!0):(B(e,!0),h.focus()):(n&&d!==b&&(d=/^0+/.test(d)?d:parseInt(""+d,16)||0),void(d!==b&&""!==d&&+d>=q[0]&&+d<=q[1]&&(n&&(d=d.toString(16).toUpperCase()||"0"),m?ab[k]=+d:n||(ab[k][l]=+d/("Lab"===k?1:q[1])),t(m?"alpha":k),v(ab),I=!0,i(c,e.type),r.data=d,H(h,db.min(h.firstChild.data.length,s.start<0?0:s.start)))))}function o(c){var d,e,f=c||a.event,g=f.target||f.srcElement,h=g.className,j=g.parentNode,k=bb,l=ab.RND.rgb,m=bb.mode,n="",o=k.CSSPrefix,p=/(?:hs|rgb)/.test(j.className)&&/^[HSBLRG]$/.test(g.firstChild?g.firstChild.data:""),q=/dblc/.test(f.type),r="";if(!q||p){if(-1!==h.indexOf("-labl "+o+"labl"))C(cb[h.split("-")[0]],o+"hide",""),C(cb[j.className.split("-")[1]],o+"hide");else if(-1!==h.indexOf(o+"butt"))if(p)q&&2===bb.scale&&(n=/hs/.test(m.type)?"rgb":/hide/.test(cb.hsl.className)?"hsv":"hsl",n=n+"-"+n[m.type.indexOf(m.z)]),M.setMode(n?n:h.replace("-butt","").split(" ")[0]),r="modeChange";else if(/^[rgb]/.test(h))n=h.split("-")[1],C(cb.colorPicker,"no-rgb-"+n,(k["noRGB"+n]=!k["noRGB"+n])?b:""),r="noRGB"+n;else if(g===cb.alpha_labl)d=k.customBG,e=k.alphaBG,C(cb.colorPicker,"alpha-bg-"+e,"alpha-bg-"+(e=k.alphaBG=c.data||("w"===e?d?"c":"b":"c"===e?"b":"w"))),g.firstChild.data=e.toUpperCase(),cb.ctrl.style.backgroundColor=cb.memo.style.backgroundColor="c"!==e?"":"rgb("+db.round(255*d.r)+", "+db.round(255*d.g)+", "+db.round(255*d.b)+")",cb.raster.style.cssText=cb.raster_bg.previousSibling.style.cssText="c"!==e?"":A(d.luminance<.22?.5:.4),r="alphaBackground";else if(g===cb.alpha_butt)C(cb.colorPicker,"mute-alpha",(k.muteAlpha=!k.muteAlpha)?b:""),r="alphaState";else if(g===cb.HEX_butt)C(cb.colorPicker,"no-HEX",(k.HEXState=!k.HEXState)?b:""),r="HEXState";else if(g===cb.HEX_labl){var s="web save"===ab.saveColor;"web smart"===ab.saveColor||s?s?M.setColor(k.webUnsave,"rgb"):(k.webUnsave||(k.webUnsave=x(l)),M.setColor(ab.webSave,"rgb")):(k.webUnsave=x(l),M.setColor(ab.webSmart,"rgb")),r="webColorState"}else/Lab-x-labl/.test(h)&&(C(cb.colorPicker,"cmy-only",(k.cmyOnly=!k.cmyOnly)?b:""),r="cmykState");else if(g===cb.bsav)u(),r="saveAsBackground";else if(g===cb.bres){var w=x(l),y=ab.alpha;M.setColor(k.color),u(),M.setColor(w,"rgb",y),r="resetColor"}else if(j===cb.col1)ab.hsv.h-=ab.hsv.h>.5?.5:-.5,t("hsv"),r="shiftColor";else if(j===cb.col2)M.setColor(g.style.backgroundColor,"rgb",ab.background.alpha),r="setSavedColor";else if(j===cb.memo){var z=function(){cb.memos.blinker&&(cb.memos.blinker.style.cssText=cb.memos.cssText)},B=function(b){cb.memos.blinker=b,b.style.cssText="background-color:"+(ab.RGBLuminance>.22?"#333":"#DDD"),a.setTimeout(z,200)};if(g===cb.memo_cursor){z(),cb.memos.blinker=b,cb.testNode.style.cssText=cb.memo_store.style.cssText,cb.memos.cssText=cb.testNode.style.cssText;for(var D=cb.memos.length-1;D--;)if(cb.memos.cssText===cb.memos[D].style.cssText){B(cb.memos[D]);break}if(!cb.memos.blinker){for(var D=cb.memos.length-1;D--;)cb.memos[D+1].style.cssText=cb.memos[D].style.cssText;cb.memos[0].style.cssText=cb.memo_store.style.cssText}r="toMemory"}else z(),M.setColor(g.style.backgroundColor,"rgb",g.style.opacity||1),cb.memos.cssText=g.style.cssText,B(g),I=1,r="fromMemory"}r&&(v(ab),I=I||!0,i(c,r))}}function p(c,d){var e,f=c||a.event,g=f?E(f):{},h=d!==b,i=h?d:g.X-X.left+8,j=h?d:g.Y-X.top+8,k=[" S XS XXS"," S XS"," S",""],l=bb.sizes,m=h?d:j10?i:10)+"px;height: "+(j>10?j:10)+"px;"}function q(a){var b={rgb_r:{x:"b",y:"g"},rgb_g:{x:"b",y:"r"},rgb_b:{x:"r",y:"g"},hsv_h:{x:"s",y:"v"},hsv_s:{x:"h",y:"v"},hsv_v:{x:"h",y:"s"},hsl_h:{x:"s",y:"l"},hsl_s:{x:"h",y:"l"},hsl_l:{x:"h",y:"s"}},c=a.replace("-","_"),d="\\b(?:rg|hs)\\w\\-\\w\\b";return C(cb.panel,d,a),C(cb.slds,d,a),a=a.split("-"),bb.mode={type:a[0],x:b[c].x,y:b[c].y,z:a[1]}}function r(){var a=/\s+(?:hue-)*(?:dark|light)/g,b="className";cb.curl[b]=cb.curl[b].replace(a,""),cb.curr[b]=cb.curr[b].replace(a,""),cb.slds[b]=cb.slds[b].replace(a,""),cb.sldr_2[b]=bb.CSSPrefix+"sldr-2",cb.sldr_4[b]=bb.CSSPrefix+"sldr-4",cb.sldl_3[b]=bb.CSSPrefix+"sldl-3";for(var c in cb.styles)c.indexOf("sld")||(cb.styles[c].cssText="");$={}}function s(){cb.styles.curr.cssText=cb.styles.curl.cssText,cb.curl.className=bb.CSSPrefix+"curl"+(Z.noRGBZ?" "+bb.CSSPrefix+"curl-"+Z.noRGBZ:""),cb.curr.className=bb.CSSPrefix+"curr "+bb.CSSPrefix+"curr-"+("h"===bb.mode.z?Z.HUEContrast:Z.noRGBZ?Z.noRGBZ:Z.RGBLuminance)}function t(a){v(_.setColor(b,a||bb.mode.type)),Y=!0}function u(a){return _.saveAsBackground(),cb.styles.col2.cssText="background-color: "+y(ab.background.RGB)+";"+A(ab.background.alpha),a&&v(ab),ab}function v(a){var c=db,d=Z,e=T[bb.alphaBG];d.hueDelta=c.round(100*a["rgbaMixBGMix"+e].hueDelta),d.luminanceDelta=c.round(100*a["rgbaMixBGMix"+e].luminanceDelta),d.RGBLuminance=a.RGBLuminance>.22?"light":"dark",d.HUEContrast=a.HUELuminance>.22?"light":"dark",d.contrast=d.luminanceDelta>d.hueDelta?"contrast":"",d.readabiltiy=a["rgbaMixBGMix"+e].WCAG2Ratio>=7?"green":a["rgbaMixBGMix"+e].WCAG2Ratio>=4.5?"orange":"",d.noRGBZ=bb["no"+bb.mode.type.toUpperCase()+bb.mode.z]?"g"===bb.mode.z&&a.rgb.g<.59||"b"===bb.mode.z||"r"===bb.mode.z?"dark":"light":b}function w(){if(I){if(!Y)return L=a[fb](w);Y=!1}var c,d,e,f,g=bb,h=g.mode,i=g.scale,l=g.CSSPrefix,m=ab,n=cb,o=n.styles,p=n.textNodes,q=S,r=K,s=Z,t=$,u=db,v=A,x=y,z=0,B=0,C=m[h.type][h.x],D=u.round(255*C/(4===i?2:i)),E=m[h.type][h.y],F=1-E,G=u.round(255*F/i),H=1-m[h.type][h.z],M=u.round(255*H/i),N=[C,E],O="rgb"===h.type,P="h"===h.z,Q="hsl"===h.type,R=Q&&"s"===h.z,T=I===j,U=I===k;O&&(N[0]>=N[1]?B=1:z=1,t.sliderSwap!==z&&(n.sldr_2.className=g.CSSPrefix+"sldr-"+(3-z),t.sliderSwap=z)),(O&&!U||P&&!T||!P&&!U)&&(o[P?"sldl_2":"sldr_2"][O?"cssText":"backgroundColor"]=O?v((N[z]-N[B])/(1-N[B]||0)):x(m.hueRGB)),P||(U||(o.sldr_4.cssText=v(O?N[B]:R?u.abs(1-2*F):F)),T||(o.sldl_3.cssText=v(Q&&"l"===h.z?u.abs(1-2*H):H)),Q&&(f=R?"sldr_4":"sldl_3",d=R?"r-":"l-",e=R?F>.5?4:3:H>.5?3:4,t[f]!==e&&(n[f].className=g.CSSPrefix+"sld"+d+e,t[f]=e))),U||(o.curm.cssText="left: "+D+"px; top: "+G+"px;"),T||(o.curl.top=M+"px"),r&&(o.curr.top=M+"px"),(r&&"alpha"===r.type||J===n.opacity)&&(o.opacity_slider.left=g.opacityPositionRelative?m.alpha*((X.width||n.opacity.offsetWidth)-(X.childWidth||n.opacity_slider.offsetWidth))+"px":100*m.alpha+"%"),o.col1.cssText="background-color: "+x(m.RND.rgb)+"; "+(g.muteAlpha?"":v(m.alpha)),o.opacity.backgroundColor=x(m.RND.rgb),o.cold.width=s.hueDelta+"%",o.cont.width=s.luminanceDelta+"%";for(c in p)d=c.split("_"),g.cmyOnly&&(d[0]=d[0].replace("k","")),e=d[1]?m.RND[d[0]][d[1]]:m.RND[d[0]]||m[d[0]],t[c]!==e&&(t[c]=e,p[c].data=e>359.5&&"HEX"!==c?0:e,"HEX"===c||g.noRangeBackground||(e=m[d[0]][d[1]]!==b?m[d[0]][d[1]]:m[d[0]],"Lab"===d[0]&&(e=(e-q[d[0]][d[1]][0])/(q[d[0]][d[1]][1]-q[d[0]][d[1]][0])),o[c].backgroundPosition=u.round(100*(1-e))+"% 0%"));d=m._rgb?[m._rgb.r!==m.rgb.r,m._rgb.g!==m.rgb.g,m._rgb.b!==m.rgb.b]:[],d.join("")!==t.outOfGammut&&(n.rgb_r_labl.firstChild.data=d[0]?"!":" ",n.rgb_g_labl.firstChild.data=d[1]?"!":" ",n.rgb_b_labl.firstChild.data=d[2]?"!":" ",t.outOfGammut=d.join("")),s.noRGBZ&&t.noRGBZ!==s.noRGBZ&&(n.curl.className=l+"curl "+l+"curl-"+s.noRGBZ,U||(n.curr.className=l+"curr "+l+"curr-"+s.noRGBZ),t.noRGBZ=s.noRGBZ),t.HUEContrast!==s.HUEContrast&&"h"===h.z?(n.slds.className=n.slds.className.replace(/\s+hue-(?:dark|light)/,"")+" hue-"+s.HUEContrast,U||(n.curr.className=l+"curr "+l+"curr-"+s.HUEContrast),t.HUEContrast=s.HUEContrast):t.RGBLuminance!==s.RGBLuminance&&(n.colorPicker.className=n.colorPicker.className.replace(/\s+(?:dark|light)/,"")+" "+s.RGBLuminance,U||"h"===h.z||s.noRGBZ||(n.curr.className=l+"curr "+l+"curr-"+s.RGBLuminance),t.RGBLuminance=s.RGBLuminance),(t.contrast!==s.contrast||t.readabiltiy!==s.readabiltiy)&&(n.ctrl.className=n.ctrl.className.replace(" contrast","").replace(/\s*(?:orange|green)/,"")+(s.contrast?" "+s.contrast:"")+(s.readabiltiy?" "+s.readabiltiy:""),t.contrast=s.contrast,t.readabiltiy=s.readabiltiy),t.saveColor!==m.saveColor&&(n.HEX_labl.firstChild.data=m.saveColor?"web save"===m.saveColor?"W":"M":"!",t.saveColor=m.saveColor),g.renderCallback&&g.renderCallback(m,h),I&&(L=a[fb](w))}function x(a){var b={};for(var c in a)b[c]=a[c];return b}function y(a,b){for(var c="",d=(b||"rgb").split(""),e=d.length;e--;)c=", "+a[d[e]]+c;return(b||"rgb")+"("+c.substr(2)+")"}function z(a,b,c){return a>c?c:b>a?b:a}function A(a){return a===b&&(a=1),R?"opacity: "+db.round(1e10*a)/1e10+";":"filter: alpha(opacity="+db.round(100*a)+");"}function B(b,c){return b.preventDefault?b.preventDefault():b.returnValue=!1,c||(a.getSelection?a.getSelection().removeAllRanges():document.selection.empty()),!1}function C(a,c,d){return a?a.className=d!==b?a.className.replace(new RegExp("\\s+?"+c,"g"),d?" "+d:""):a.className+" "+c:!1}function D(b){var c=b.getBoundingClientRect?b.getBoundingClientRect():{top:0,left:0},d=b&&b.ownerDocument,e=d.body,f=d.defaultView||d.parentWindow||a,g=d.documentElement||e.parentNode,h=g.clientTop||e.clientTop||0,i=g.clientLeft||e.clientLeft||0;return{left:c.left+(f.pageXOffset||g.scrollLeft)-i,top:c.top+(f.pageYOffset||g.scrollTop)-h}}function E(b){var c=a.document;return{X:b.pageX||b.clientX+c.body.scrollLeft+c.documentElement.scrollLeft,Y:b.pageY||b.clientY+c.body.scrollTop+c.documentElement.scrollTop}}function F(a,b,c){F.cache=F.cache||{_get:function(a,b,c,d){for(var e=F.cache[b]||[],f=e.length;f--;)if(a===e[f].obj&&""+c==""+e[f].func)return c=e[f].func,d||(e[f]=e[f].obj=e[f].func=null,e.splice(f,1)),c},_set:function(a,b,c){var d=F.cache[b]=F.cache[b]||[];return F.cache._get(a,b,c,!0)?!0:void d.push({func:c,obj:a})}},!c.name&&F.cache._set(a,b,c)||"function"!=typeof c||(a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c))}function G(a,b,c){"function"==typeof c&&(c.name||(c=F.cache._get(a,b,c)||c),a.removeEventListener?a.removeEventListener(b,c,!1):a.detachEvent("on"+b,c))}function H(c,d){var e={};if(d===b){if(a.getSelection){c.focus();var f=a.getSelection().getRangeAt(0),g=f.cloneRange();g.selectNodeContents(c),g.setEnd(f.endContainer,f.endOffset),e={end:g.toString().length,range:f.toString().length}}else{c.focus();var f=document.selection.createRange(),g=document.body.createTextRange();g.moveToElementText(c),g.setEndPoint("EndToEnd",f),e={end:g.text.length,range:f.text.length}}return e.start=e.end-e.range,e}if(-1==d&&(d=c.text().length),a.getSelection)c.focus(),a.getSelection().collapse(c.firstChild,d);else{var h=document.body.createTextRange();h.moveToElementText(c),h.moveStart("character",d),h.collapse(!0),h.select()}return d}var I,J,K,L,M,N,O=a.ColorPicker,P=!O,Q=!1,R=!1,S={},T={w:"White",b:"Black",c:"Custom"},U="",V=1,W={},X={},Y=!0,Z={},$={},_={},ab={},bb={},cb={},db=Math,eb="AnimationFrame",fb="request"+eb,gb="cancel"+eb,hb=["ms","moz","webkit","o"],ib=function(a){this.options={color:"rgba(204, 82, 37, 0.8)",mode:"rgb-b",fps:60,delayOffset:8,CSSPrefix:"cp-",allMixDetails:!0,alphaBG:"w",imagePath:""},c(this,a||{})};a.ColorPicker=ib,ib.addEvent=F,ib.removeEvent=G,ib.getOrigin=D,ib.limitValue=z,ib.changeClass=C,ib.prototype.setColor=function(a,b,c,e){d(this),K=!0,v(_.setColor.apply(_,arguments)),e&&this.startRender(!0)},ib.prototype.saveAsBackground=function(){return d(this),u(!0)},ib.prototype.setCustomBackground=function(a){return d(this),_.setCustomBackground(a)},ib.prototype.startRender=function(b){d(this),b?(I=!1,w(),this.stopRender()):(I=1,L=a[fb](w))},ib.prototype.stopRender=function(){d(this),a[gb](L),K&&(I=1,i(b,"external"))},ib.prototype.setMode=function(a){d(this),q(a),r(),w()},ib.prototype.destroyAll=function(){var a=this.nodes.colorPicker,b=function(a){for(var c in a)(a[c]&&"[object Object]"===a[c].toString()||a[c]instanceof Array)&&b(a[c]),a[c]=null,delete a[c]};this.stopRender(),h(this,!0),b(this),a.parentNode.removeChild(a),a=null},ib.prototype.renderMemory=function(a){var c=this.nodes.memos,d=[];"string"==typeof a&&(a=a.replace(/^'|'$/g,"").replace(/\s*/,"").split("','"));for(var e=c.length;e--;)a&&"string"==typeof a[e]&&(d=a[e].replace("rgba(","").replace(")","").split(","),a[e]={r:d[0],g:d[1],b:d[2],a:d[3]}),c[e].style.cssText="background-color: "+(a&&a[e]!==b?y(a[e])+";"+A(a[e].a||1):"rgb(0,0,0);")},F(Q?document.body:a,"mouseup",i);for(var jb=hb.length;jb--&&!a[fb];)a[fb]=a[hb[jb]+"Request"+eb],a[gb]=a[hb[jb]+"Cancel"+eb]||a[hb[jb]+"CancelRequest"+eb];a[fb]=a[fb]||function(b){return a.setTimeout(b,1e3/bb.fps)},a[gb]=a[gb]||function(b){return a.clearTimeout(b),L=null}}(window); \ No newline at end of file diff --git a/_server/thirdparty/jsColor.js b/_server/thirdparty/jsColor.js new file mode 100644 index 0000000..0d85e92 --- /dev/null +++ b/_server/thirdparty/jsColor.js @@ -0,0 +1,305 @@ +// ------ ColorPicker ------ // + +(function (window) { + window.jsColorPicker = function(selectors, config) { + var renderCallback = function(colors, mode) { + var options = this, + input = options.input, + patch = options.patch, + RGB = colors.RND.rgb, + HSL = colors.RND.hsl, + AHEX = options.isIE8 ? (colors.alpha < 0.16 ? '0' : '') + + (Math.round(colors.alpha * 100)).toString(16).toUpperCase() + colors.HEX : '', + RGBInnerText = RGB.r + ',' + RGB.g + ',' + RGB.b, + RGBAText = RGBInnerText + ',' + colors.alpha, + isAlpha = colors.alpha !== 1 && !options.isIE8, + colorMode = input.getAttribute('data-colorMode'); + + patch.style.cssText = + 'color:' + (colors.rgbaMixCustom.luminance > 0.22 ? '#222' : '#ddd') + ';' + // Black...??? + 'background-color: rgba(' + RGBAText + ');' + + 'filter:' + (options.isIE8 ? 'progid:DXImageTransform.Microsoft.gradient(' + // IE<9 + 'startColorstr=#' + AHEX + ',' + 'endColorstr=#' + AHEX + ')' : ''); + + input.value = RGBAText; + + if (options.displayCallback) { + options.displayCallback(colors, mode, options); + } + }, + extractValue = function(elm) { + var val = elm.value || elm.getAttribute('value') || elm.style.backgroundColor || "0,0,0,1"; + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+,[0-9. ]+$/.test(val)) return "rgba("+val+")"; + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+$/.test(val)) return "rgba("+val+",1)"; + return null; + }, + actionCallback = function(event, action) { + var options = this, + colorPicker = colorPickers.current; + + if (action === 'toMemory') { + var memos = colorPicker.nodes.memos, + backgroundColor = '', + opacity = 0, + cookieTXT = []; + + for (var n = 0, m = memos.length; n < m; n++) { + backgroundColor = memos[n].style.backgroundColor; + opacity = memos[n].style.opacity; + opacity = Math.round((opacity === '' ? 1 : opacity) * 100) / 100; + cookieTXT.push(backgroundColor. + replace(/, /g, ','). + replace('rgb(', 'rgba('). + replace(')', ',' + opacity + ')') + ); + } + cookieTXT = '\'' + cookieTXT.join('\',\'') + '\''; + ColorPicker.docCookies('colorPickerMemos' + (options.noAlpha ? 'NoAlpha' : ''), cookieTXT); + } else if (action === 'resizeApp') { + ColorPicker.docCookies('colorPickerSize', colorPicker.color.options.currentSize); + } else if (action === 'modeChange') { + var mode = colorPicker.color.options.mode; + + ColorPicker.docCookies('colorPickerMode', mode.type + '-' + mode.z); + } + }, + createInstance = function(elm, config) { + var initConfig = { + klass: window.ColorPicker, + input: elm, + patch: elm, + isIE8: !!document.all && !document.addEventListener, // Opera??? + // *** animationSpeed: 200, + // *** draggable: true, + margin: {left: -1, top: 2}, + customBG: '#FFFFFF', + // displayCallback: displayCallback, + /* --- regular colorPicker options from this point --- */ + color: extractValue(elm), + initStyle: 'display: none', + mode: ColorPicker.docCookies('colorPickerMode') || 'hsv-h', + // memoryColors: (function(colors, config) { + // return config.noAlpha ? + // colors.replace(/\,\d*\.*\d*\)/g, ',1)') : colors; + // })($.docCookies('colorPickerMemos'), config || {}), + memoryColors: ColorPicker.docCookies('colorPickerMemos' + + ((config || {}).noAlpha ? 'NoAlpha' : '')), + size: ColorPicker.docCookies('colorPickerSize') || 1, + renderCallback: renderCallback, + actionCallback: actionCallback + }; + + for (var n in config) { + initConfig[n] = config[n]; + } + return new initConfig.klass(initConfig); + }, + doEventListeners = function(elm, multiple, off) { + var onOff = off ? 'removeEventListener' : 'addEventListener', + inputListener = function(e) { + var index = multiple ? Array.prototype.indexOf.call(elms, this) : 0, + colorPicker = colorPickers[index] || + (colorPickers[index] = createInstance(this, config)), + options = colorPicker.color.options; + + options.color = extractValue(elm); // brings color to default on reset + //检查颜色合法性 + if (options.color != null && options.color == options.color.match(/rgba\([0-9 ]+,[0-9 ]+,[0-9 ]+,[0-9. ]+\)/)[0]) { + var chec = options.color.match(/[0-9.]+/g); + if (chec.length != 4) + return; + for (var i = 0; i < 3; i++) { + if (chec[i] != chec[i].match(/\d+/)[0] || +chec[i] < 0 || +chec[i] > 255) + return; + } + if (chec[3] != chec[3].match(/\d+(\.\d+)?/)[0] || parseFloat(chec[3]) > 1 || parseFloat(chec[3] < 0)) + return; + if (!multiple) { + colorPicker.setColor(extractValue(elm), undefined, undefined, true); + colorPicker.saveAsBackground(); + } + colorPickers.current = colorPickers[index]; + } + }, + createListener = function() { + elm = document.getElementById("colorPicker"); + var input = elm, + position = window.ColorPicker.getOrigin(input), + index = multiple ? Array.prototype.indexOf.call(elms, elm) : 0, + colorPicker = colorPickers[index] || + (colorPickers[index] = createInstance(elm, config)), + options = colorPicker.color.options, + colorPickerUI = colorPicker.nodes.colorPicker, + appendTo = (options.appendTo || document.body), + isStatic = /static/.test(window.getComputedStyle(appendTo).position), + atrect = isStatic ? {left: 0, top: 0} : appendTo.getBoundingClientRect(), + waitTimer = 0; + + options.color = extractValue(elm); // brings color to default on reset + colorPickerUI.style.cssText = + 'position: absolute;' + (!colorPickers[index].cssIsReady ? 'display: none;' : '') + + 'left:' + (position.left + options.margin.left - atrect.left) + 'px;' + + 'top:' + (position.top + +input.offsetHeight + options.margin.top - atrect.top) + 'px;'; + + if (!multiple) { + options.input = elm; + options.patch = elm; // check again??? + colorPicker.setColor(extractValue(elm), undefined, undefined, true); + colorPicker.saveAsBackground(); + } + colorPickers.current = colorPickers[index]; + appendTo.appendChild(colorPickerUI); + waitTimer = setInterval(function() { // compensating late style on onload in colorPicker + if (colorPickers.current.cssIsReady) { + waitTimer = clearInterval(waitTimer); + colorPickerUI.style.display = 'block'; + } + }, 10); + }, + hideListener = function(e) { + var colorPicker = colorPickers.current, + colorPickerUI = (colorPicker ? colorPicker.nodes.colorPicker : undefined), + animationSpeed = colorPicker ? colorPicker.color.options.animationSpeed : 0, + isColorPicker = colorPicker && (function(elm) { + while (elm) { + if ((elm.className || '').indexOf('cpPanel') !== -1) return elm; + elm = elm.parentNode; + } + return false; + })(e.target), + inputIndex = Array.prototype.indexOf.call(elms, e.target); + + if (isColorPicker && Array.prototype.indexOf.call(colorPickers, isColorPicker)) { + if (e.target === colorPicker.nodes.exit) { + colorPickerUI.parentNode.style.display = 'none'; + document.activeElement.blur(); + } else { + // ... + } + } else if (inputIndex !== -1) { + // ... + } else if (colorPickerUI) { + colorPickerUI.parentNode.style.display = 'none'; + } + }; + elm[onOff]('input', inputListener); + window.jsColorPicker.create = createListener; + }, + // this is a way to prevent data binding on HTMLElements + colorPickers = window.jsColorPicker.colorPickers || [], + elms = document.querySelectorAll(selectors), + testColors = new window.Colors({customBG: config.customBG, allMixDetails: true}); + + window.jsColorPicker.colorPickers = colorPickers; + + for (var n = 0, m = elms.length; n < m; n++) { + var elm = elms[n]; + + if (config === 'destroy') { + doEventListeners(elm, (config && config.multipleInstances), true); + if (colorPickers[n]) { + colorPickers[n].destroyAll(); + } + } else { + var color = extractValue(elm); + var value = color.split('('); + + testColors.setColor(color); + if (config && config.init) { + config.init(elm, testColors.colors); + } + elm.setAttribute('data-colorMode', value[1] ? value[0].substr(0, 3) : 'HEX'); + doEventListeners(elm, (config && config.multipleInstances), false); + if (config && config.readOnly) { + elm.readOnly = true; + } + } + }; + + return window.jsColorPicker.colorPickers; + }; + + window.ColorPicker.docCookies = function(key, val, options) { + var encode = encodeURIComponent, decode = decodeURIComponent, + cookies, n, tmp, cache = {}, + days; + + if (val === undefined) { // all about reading cookies + cookies = document.cookie.split(/;\s*/) || []; + for (n = cookies.length; n--; ) { + tmp = cookies[n].split('='); + if (tmp[0]) cache[decode(tmp.shift())] = decode(tmp.join('=')); // there might be '='s in the value... + } + + if (!key) return cache; // return Json for easy access to all cookies + else return cache[key]; // easy access to cookies from here + } else { // write/delete cookie + options = options || {}; + + if (val === '' || options.expires < 0) { // prepare deleteing the cookie + options.expires = -1; + // options.path = options.domain = options.secure = undefined; // to make shure the cookie gets deleted... + } + + if (options.expires !== undefined) { // prepare date if any + days = new Date(); + days.setDate(days.getDate() + options.expires); + } + + document.cookie = encode(key) + '=' + encode(val) + + (days ? '; expires=' + days.toUTCString() : '') + + (options.path ? '; path=' + options.path : '') + + (options.domain ? '; domain=' + options.domain : '') + + (options.secure ? '; secure' : ''); + } + }; +})(this); + +// Added +jsColorPicker('input.color', { + customBG: '#222', + readOnly: false, + // patch: false, + init: function(elm, colors) { // colors is a different instance (not connected to colorPicker) + elm.style.backgroundColor = elm.value; + elm.style.color = colors.rgbaMixCustom.luminance > 0.22 ? '#222' : '#ddd'; + }, + appendTo: document.getElementById("colorPanel"), + size: 1, +}); + +function openColorPicker(px, py, callback) { + window.jsColorPicker.confirm = callback; + var colorPanel = document.getElementById('colorPanel'); + if (colorPanel.style.display=='none' && px != null && py != null) { + colorPanel.style.display = "inline-block"; + colorPanel.style.left = px + 'px'; + colorPanel.style.top = py + 'px'; + window.jsColorPicker.create(); + } + else { + colorPanel.style.display = 'none'; + delete window.jsColorPicker.confirm; + } +} + +function confirmColor() { + var colorPicker = document.getElementById("colorPicker"); + if (window.jsColorPicker.confirm) { /* 存在块 */ + // 检测需要是合法数值 + var val = colorPicker.value; + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+,[0-9. ]+$/.test(val)) val = "rgba("+val+")"; + else if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+$/.test(val)) val = "rgba("+val+",1)"; + else val = null; + if (val) window.jsColorPicker.confirm(val); + } + else { + colorPicker.select(); + document.execCommand("Copy"); + } + colorPanel.style.display = 'none'; + delete window.jsColorPicker.confirm; +} + +// ------ AutoCompletion ------ + diff --git a/editor-mobile.html b/editor-mobile.html new file mode 100644 index 0000000..ed4c344 --- /dev/null +++ b/editor-mobile.html @@ -0,0 +1,501 @@ + + + + + + + + + + + + + +
+
+
+
+
+
+ +
+
+ + + + + + + 保留楼层属性 +
+ +
+
+ + + + +
+ + +
+
+
+

追加素材

+
+

+ + + + + + 自动注册 +

+

+ 色相: + +

+
+ + + + +
+
1
+
2
+
3
+
4
+
+
+
+
+
+

地图选点       +

+
+

0,0

+
+ + + + + + + + +
条目注释
+
+
+
+
+

图块属性         +

+
+
+
+ + + + + + + + +
条目注释
+
+
+ + + + +
+
+
+ + + +
+ + + +
+
+ + + + +
+
+
+
+

楼层属性         +

+
+
+ + + + + + + + +
条目注释
+
+
+ + +
+
+ 修改地图大小:宽,高, + 偏移x y + +
+
+
+
+

全塔属性       +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+
+

事件编辑器    + + + + + + +
+ + +
+ + + + 开启中文名替换 + + 展开值块逻辑运算 + +

+
+
+
+
+ +
+
+
+ +
+
+ + + + + + + 语法检查 + + 字体大小 + + 字体加粗 + +
+ +
+
+

脚本编辑     +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+

公共事件         +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+

插件编写         +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+
+
+
+ + + +
+ +
+ + +
+
+
+
+ + + +
+ + +
+ + + 通行度 + + + +
+ + + + + +
+ + + + + + +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/editor.html b/editor.html new file mode 100644 index 0000000..31332e0 --- /dev/null +++ b/editor.html @@ -0,0 +1,485 @@ + + + + + + + + + + + + +
+
+
+
+
+
+ +
+
+ + + + + + + + 保留楼层属性 +
+
+ + + + +
+ + +
+
+
+

追加素材

+
+

+ + + + + +   自动注册 +

+

从V2.7.1开始,你可以直接将素材图片拖到对应的素材区,将自动追加并注册。同时,4x4的道具素材已支持快速追加一次16个。

+

+ 色相: + +

+
+ + + + +
+
1
+
2
+
3
+
4
+
+
+
+
+
+

地图选点       +

+
+

0,0

+
+ + + + + + + + +
条目注释
+
+
+
+
+

图块属性         +

+
+
+
+ + + + + + + + +
条目注释
+
+
+ + + + +
+
+
+ + + +
+ + + +
+
+ + + + +
+
+
+
+

楼层属性         +

+
+
+ + + + + + + + +
条目注释
+
+
+ + +
+
+ 修改地图大小:宽,高, + 偏移x y + +
+
+
+
+

全塔属性       +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+
+

事件编辑器    + + + + + +
+ + +
+ + + + 开启中文名替换 + + 展开值块逻辑运算 + +

+
+
+
+
+ +
+
+
+ +
+
+ + + + + + + 语法检查 + + 字体大小 + + 字体加粗 + +
+ +
+
+

脚本编辑     +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+

公共事件         +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+

插件编写         +

+
+
+ + + + + + + + +
条目注释
+
+
+
+
+
+
+
+ + + +
+
+
+ + 通行度 + +
+ + 线 + 矩形 + tile平铺 + 填充 + + +
+ + 背景层 + 事件层 + 前景层 + +
+
+ + + + + +
+ + + + + + +
+
+
+

(Ctrl+滚轮放缩,右键置顶)

+
+ +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/localSave.js b/extensions/localSave.js new file mode 100644 index 0000000..061d5c1 --- /dev/null +++ b/extensions/localSave.js @@ -0,0 +1,110 @@ +/** + * 离线游戏使用本地存储扩展。 + * 开启本拓展后,将会把所有存档存至 _saves 目录下。 + * 需配合样板V2.8.2+使用 + */ + +"use strict"; + +(function () { + // 将这一行改成 false 可以禁用本拓展 + var __enabled = true; + + if (window.jsinterface || !window.fs || !__enabled) return; + + function rewrite() { + core.utils._setLocalForage_set = function (name, str, callback) { + var data = LZString.compressToBase64(str); + core.saves.cache[name] = data; + fs.writeFile('_saves/' + name, data, 'utf-8', callback); + } + + core.utils._getLocalForage_get = function (name, callback) { + fs.readFile('_saves/' + name, 'utf-8', function (err, data) { + if (err) return callback(err); + callback(null, data); + }); + } + + core.utils.decompress = function (data) { + try { + return JSON.parse(LZString.decompressFromBase64(data)) + } catch (e) { + return null; + } + } + + core.utils._removeLocalForage_remove = function (name, callback) { + fs.deleteFile('_saves/' + name, callback); + } + + core.utils.clearLocalForage = function (callback) { + fs.deleteFile('_saves', function () { + fs.mkdir('_saves', callback); + }) + } + + core.utils.iterateLocalForage = function (iter, callback) { + fs.readdir('_saves', function (err, data) { + if (err) callback(err); + else { + data.forEach(function (one) { + iter(null, one, null); + }); + callback(); + } + }); + } + + core.utils.keysLocalForage = function (callback) { + fs.readdir('_saves', callback); + } + + core.utils.lengthLocalForage = function (callback) { + fs.readdir('_saves', function (err, data) { + if (err) callback(err); + else callback(null, data.length); + }); + } + } + + + var _export = function () { + var toExport = []; + + localforage.iterate(function (value, key, n) { + if (value == null || !key.startsWith(core.firstData.name)) return; + value = core.decompress(value); + if (value == null) return; + var str = JSON.stringify(value).replace(/[\u007F-\uFFFF]/g, function (chr) { + return "\\u" + ("0000" + chr.charCodeAt(0).toString(16)).substr(-4) + }); + str = LZString.compressToBase64(str); + toExport.push(key); + core.saves.cache[key] = str; + fs.writeFile('_saves/' + key, str, 'utf-8', function () {}); + }, function () { + if (toExport.length > 0) { + alert('提示!本塔已开启存档本地化!原始存档已全部导出至 _saves/ 目录下。'); + } + fs.writeFile('_saves/.exported', '1', 'utf-8', function () {}); + rewrite(); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); + }); + } + + fs.mkdir('_saves', function (err) { + if (err) return; + + fs.readFile('_saves/.exported', 'utf-8', function(err, data) { + if (!err && data) { + rewrite(); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); + return; + } + _export(); + }); + }); +})(); + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..8db0234 --- /dev/null +++ b/index.html @@ -0,0 +1,204 @@ + + + + + + + + + HTML5魔塔 + + + + + + + + + + + +
+
+ +
+ + +
+

请稍候...

+ +
+
+
+
+
+

资源即将开始加载

+

HTML5魔塔游戏平台,享受更多魔塔游戏:
https://h5mota.com/

+
+ + +
+
+ 开始游戏 + 载入游戏 + 录像回放 +
+
+
+
+
+

+

+

+
+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ +

+
+
+ + + + +
+
+ + + +
+
+ + + +
+ + + +
+
+ + + + + + + + + + + + + + + + +

+
+
+
+
+ + + + + + + + + + 此浏览器不支持HTML5 + +
+
+
+
+

请输入文字...

+ + + +
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/libs/actions.js b/libs/actions.js new file mode 100644 index 0000000..e25afb8 --- /dev/null +++ b/libs/actions.js @@ -0,0 +1,3165 @@ + +/* +actions.js:用户交互的事件的处理 +键盘、鼠标、触摸屏事件相关 + */ + +"use strict"; + +function actions () { + this._init(); + this._HX_ = core._HALF_WIDTH_; + this._HY_ = core._HALF_HEIGHT_; + this._out = function (x) { return x < this._HX_ - 2 || this._HX_ + 2 < x; }; + this.LAST = core._WIDTH_ - 1; +} + +actions.prototype._init = function () { + this.actionsdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.actions; + this.actions = {}; + // --- onkeyDown注册 + this.registerAction('onkeyDown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onkeyDown', '_sys_onkeyDown', this._sys_onkeyDown, 0); + // --- onkeyUp注册 + this.registerAction('onkeyUp', '_sys_onkeyUp_replay', this._sys_onkeyUp_replay, 100); + this.registerAction('onkeyUp', '_sys_onkeyUp', this._sys_onkeyUp, 0); + // --- pressKey注册 + this.registerAction('pressKey', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('pressKey', '_sys_pressKey', this._sys_pressKey, 0); + // --- keyDown注册 + this.registerAction('keyDown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('keyDown', '_sys_keyDown_lockControl', this._sys_keyDown_lockControl, 50); + this.registerAction('keyDown', '_sys_keyDown', this._sys_keyDown, 0); + // --- keyUp注册 + this.registerAction('keyUp', '_sys_keyUp_replay', this._sys_keyUp_replay, 100); + this.registerAction('keyUp', '_sys_keyUp_lockControl', this._sys_keyUp_lockControl, 50); + this.registerAction('keyUp', '_sys_keyUp', this._sys_keyUp, 0); + // --- ondown注册 + this.registerAction('ondown', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('ondown', '_sys_ondown_lockControl', this._sys_ondown_lockControl, 30); + this.registerAction('ondown', '_sys_ondown', this._sys_ondown, 0); + // --- onmove注册 + this.registerAction('onmove', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onmove', '_sys_onmove_choices', this._sys_onmove_choices, 30); + this.registerAction('onmove', '_sys_onmove', this._sys_onmove, 0); + // --- onup注册 + this.registerAction('onup', '_sys_checkReplay', this._sys_checkReplay, 100); + this.registerAction('onup', '_sys_onup', this._sys_onup, 0); + // --- onclick已废弃,将视为ondown + // --- onmousewheel注册 + this.registerAction('onmousewheel', '_sys_onmousewheel', this._sys_onmousewheel, 0); + // --- keyDownCtrl注册 + this.registerAction('keyDownCtrl', '_sys_keyDownCtrl', this._sys_keyDownCtrl, 0); + // --- longClick注册 + this.registerAction('longClick', '_sys_longClick_lockControl', this._sys_longClick_lockControl, 50); + // --- onStatusBarClick注册 + this.registerAction('onStatusBarClick', '_sys_onStatusBarClick', this._sys_onStatusBarClick, 0); + +} + +////// 注册一个用户交互行为 ////// +/* + * 此函数将注册一个用户交互行为。 + * action:要注册的交互类型,如 ondown, onup, keyDown 等等。 + * name:你的自定义名称,可被注销使用;同名重复注册将后者覆盖前者。 + * func:执行函数。 + * priority:优先级;优先级高的将会被执行。此项可不填,默认为0。 + * 返回:如果func返回true,则不会再继续执行其他的交互函数;否则会继续执行其他的交互函数。 + */ +actions.prototype.registerAction = function (action, name, func, priority) { + if (!name || !func) + return; + // 将onclick视为ondown处理 + if (action == 'onclick') action = 'ondown'; + priority = priority || 0; + if (!this.actions[action]) { + this.actions[action] = []; + } + this.unregisterAction(action, name); + this.actions[action].push( + { "action": action, "name": name, "func": func, "priority": priority } + ); + this.actions[action] = this.actions[action].sort(function (a, b) { + return b.priority - a.priority; + }); +} + +////// 注销一个用户交互行为 ////// +actions.prototype.unregisterAction = function (action, name) { + // 将onclick视为ondown处理 + if (action == 'onclick') action = 'ondown'; + if (!this.actions[action]) return; + this.actions[action] = this.actions[action].filter(function (x) { + return x.name != name; + }); +} + +////// 执行一个用户交互行为 ////// +actions.prototype.doRegisteredAction = function (action) { + var actions = this.actions[action]; + if (!actions) return false; + for (var i = 0; i < actions.length; ++i) { + try { + if (core.doFunc.apply(core, [actions[i].func, this].concat(Array.prototype.slice.call(arguments, 1)))) + return true; + } + catch (e) { + console.error(e); + console.error("ERROR in actions[" + actions[i].name + "]."); + } + } + return false; +} + +actions.prototype._checkReplaying = function () { + if (core.isReplaying() && + ['save', 'book', 'book-detail', 'viewMaps', 'toolbox', 'equipbox', 'text'].indexOf(core.status.event.id) < 0) + return true; + return false; +} + +////// 检查是否在录像播放中,如果是,则停止交互 +actions.prototype._sys_checkReplay = function () { + if (this._checkReplaying()) return true; +} + +////// 检查左手模式 +actions.prototype.__checkLeftHandPrefer = function (e) { + if (!core.flags.leftHandPrefer) return e; + var map = { + 87: 38, // W -> up + 83: 40, // S -> down + 65: 37, // A -> left + 68: 39, // D -> right + 73: 87, // I -> W + 74: 65, // J -> A + 75: 83, // K -> S + 76: 68, // L -> D + } + var newEvent = {}; + for (var one in e) { + if (!(e[one] instanceof Function)) { + newEvent[one] = e[one]; + } + }; + ["stopPropagation", "stopImmediatePropagation", "preventDefault"].forEach(function (one) { + newEvent[one] = function () { + return e[one](); + } + }); + newEvent.keyCode = map[e.keyCode] || e.keyCode; + return newEvent; +} + +////// 按下某个键时 ////// +actions.prototype.onkeyDown = function (e) { + this.doRegisteredAction('onkeyDown', this.__checkLeftHandPrefer(e)); +} + +actions.prototype._sys_onkeyDown = function (e) { + core.status.holdingKeys = core.status.holdingKeys || [] + var isArrow = { 37: true, 38: true, 39: true, 40: true }[e.keyCode] + if (isArrow && !core.status.lockControl) { + for (var ii = 0; ii < core.status.holdingKeys.length; ii++) { + if (core.status.holdingKeys[ii] === e.keyCode) { + return; + } + } + if (e.preventDefault) e.preventDefault(); + core.status.holdingKeys.push(e.keyCode); + this.pressKey(e.keyCode); + } else { + if (e.keyCode == 17) core.status.ctrlDown = true; + this.keyDown(e.keyCode); + } +} + +////// 放开某个键时 ////// +actions.prototype.onkeyUp = function (e) { + this.doRegisteredAction('onkeyUp', this.__checkLeftHandPrefer(e)); +} + +actions.prototype._sys_onkeyUp_replay = function (e) { + if (this._checkReplaying()) { + if (e.keyCode == 27) // ESCAPE + core.stopReplay(); + else if (e.keyCode == 90) // Z + core.speedDownReplay(); + else if (e.keyCode == 67) // C + core.speedUpReplay(); + else if (e.keyCode == 32) // SPACE + core.triggerReplay(); + else if (e.keyCode == 65) // A + core.rewindReplay(); + else if (e.keyCode == 83) // S + core.control._replay_SL(); + else if (e.keyCode == 88) // X + core.control._replay_book(); + else if (e.keyCode == 33 || e.keyCode == 34) // PgUp/PgDn + core.control._replay_viewMap(); + else if (e.keyCode == 78) // N + core.stepReplay(); + else if (e.keyCode == 84) // T + core.control._replay_toolbox(); + else if (e.keyCode == 81) // Q + core.control._replay_equipbox(); + else if (e.keyCode == 66) // B + core.ui._drawStatistics(); + else if (e.keyCode >= 49 && e.keyCode <= 51) // 1-3 + core.setReplaySpeed(e.keyCode - 48); + else if (e.keyCode == 52) // 4 + core.setReplaySpeed(6); + else if (e.keyCode == 53) // 5 + core.setReplaySpeed(12); + else if (e.keyCode == 54) // 6 + core.setReplaySpeed(24); + return true; + } +} + +actions.prototype._sys_onkeyUp = function (e) { + var isArrow = { 37: true, 38: true, 39: true, 40: true }[e.keyCode] + if (isArrow && !core.status.lockControl) { + for (var ii = 0; ii < core.status.holdingKeys.length; ii++) { + if (core.status.holdingKeys[ii] === e.keyCode) { + core.status.holdingKeys = core.status.holdingKeys.slice(0, ii).concat(core.status.holdingKeys.slice(ii + 1)); + if (ii === core.status.holdingKeys.length && core.status.holdingKeys.length !== 0) core.pressKey(core.status.holdingKeys.slice(-1)[0]); + break; + } + } + if (e.preventDefault) e.preventDefault(); + this.keyUp(e.keyCode, e.altKey); + } else { + if (e.keyCode == 17) core.status.ctrlDown = false; + this.keyUp(e.keyCode, e.altKey); + } +} + +////// 按住某个键时 ////// +actions.prototype.pressKey = function (keyCode) { + this.doRegisteredAction('pressKey', keyCode); +} + +actions.prototype._sys_pressKey = function (keyCode) { + if (keyCode === core.status.holdingKeys.slice(-1)[0]) { + this.keyDown(keyCode); + window.setTimeout(function () { + core.pressKey(keyCode); + }, 30); + } +} + +////// 根据按下键的code来执行一系列操作 ////// +actions.prototype.keyDown = function (keyCode) { + this.doRegisteredAction('keyDown', keyCode); +} + +actions.prototype._sys_keyDown_lockControl = function (keyCode) { + if (!core.status.lockControl) return false; + // Ctrl跳过对话 + if (keyCode == 17) { + this.keyDownCtrl(); + return true; + } + switch (core.status.event.id) { + case 'action': + this._keyDownAction(keyCode); + break; + case 'book': + this._keyDownBook(keyCode); + break; + case 'fly': + this._keyDownFly(keyCode); + break; + case 'viewMaps': + this._keyDownViewMaps(keyCode); + break; + case 'equipbox': + this._keyDownEquipbox(keyCode); + break; + case 'toolbox': + this._keyDownToolbox(keyCode); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._keyDownSL(keyCode); + break; + case 'selectShop': + case 'switchs': + case 'switchs-sounds': + case 'switchs-display': + case 'switchs-action': + case 'notes': + case 'settings': + case 'syncSave': + case 'syncSelect': + case 'localSaveSelect': + case 'storageRemove': + case 'replay': + case 'gameInfo': + this._keyDownChoices(keyCode); + break; + case 'cursor': + this._keyDownCursor(keyCode); + break; + } + return true; +} + +actions.prototype._sys_keyDown = function (keyCode) { + if (!core.status.played) + return true; + switch (keyCode) { + case 37: + core.moveHero('left'); + break; + case 38: + core.moveHero('up'); + break; + case 39: + core.moveHero('right'); + break; + case 40: + core.moveHero('down'); + break; + } + return true; +} + +////// 根据放开键的code来执行一系列操作 ////// +actions.prototype.keyUp = function (keyCode, altKey, fromReplay) { + this.doRegisteredAction('keyUp', keyCode, altKey, fromReplay); +} + +actions.prototype._sys_keyUp_replay = function (keyCode, altKey, fromReplay) { + if (!fromReplay && this._checkReplaying()) return true; +} + +actions.prototype._sys_keyUp_lockControl = function (keyCode, altKey) { + if (!core.status.lockControl) return false; + + var ok = function () { + return keyCode == 27 || keyCode == 88 || keyCode == 13 || keyCode == 32 || keyCode == 67; + } + + core.status.holdingKeys = []; + switch (core.status.event.id) { + case 'text': + ok() && core.drawText(); + break; + case 'confirmBox': + this._keyUpConfirmBox(keyCode); + break; + case 'action': + this._keyUpAction(keyCode); + break; + case 'about': + ok() && core.closePanel(); + break; + case 'help': + ok() && core.closePanel(); + break; + case 'book': + this._keyUpBook(keyCode); + break; + case 'book-detail': + ok() && this._clickBookDetail(); + break; + case 'fly': + this._keyUpFly(keyCode); + break; + case 'viewMaps': + this._keyUpViewMaps(keyCode); + break; + case 'selectShop': + this._keyUpQuickShop(keyCode); + break; + case 'toolbox': + this._keyUpToolbox(keyCode); + break; + case 'equipbox': + this._keyUpEquipbox(keyCode, altKey); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._keyUpSL(keyCode); + break; + case 'keyBoard': + ok() && core.closePanel(); + break; + case 'switchs': + this._keyUpSwitchs(keyCode); + break; + case 'switchs-sounds': + this._keyUpSwitchs_sounds(keyCode); + break; + case 'switchs-display': + this._keyUpSwitchs_display(keyCode); + break; + case 'switchs-action': + this._keyUpSwitchs_action(keyCode); + break; + case 'settings': + this._keyUpSettings(keyCode); + break; + case 'notes': + this._keyUpNotes(keyCode); + break; + case 'syncSave': + this._keyUpSyncSave(keyCode); + break; + case 'syncSelect': + this._keyUpSyncSelect(keyCode); + break; + case 'localSaveSelect': + this._keyUpLocalSaveSelect(keyCode); + break; + case 'storageRemove': + this._keyUpStorageRemove(keyCode); + break; + case 'cursor': + this._keyUpCursor(keyCode); + break; + case 'replay': + this._keyUpReplay(keyCode); + break; + case 'gameInfo': + this._keyUpGameInfo(keyCode); + break; + case 'centerFly': + this._keyUpCenterFly(keyCode); + break; + } + return true; +} + +actions.prototype._sys_keyUp = function (keyCode, altKey) { + if (!core.status.played) + return true; + this.actionsdata.onKeyUp(keyCode, altKey); + if (core.status.automaticRoute && core.status.automaticRoute.autoHeroMove) { + core.stopAutomaticRoute(); + } + core.status.heroStop = true; + return true; +} + +////// 点击(触摸)事件按下时 ////// +actions.prototype.ondown = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('ondown', x, y, px, py); +} + +actions.prototype._sys_ondown_lockControl = function (x, y, px, py) { + if (core.status.played && !core.status.lockControl) return false; + + switch (core.status.event.id) { + case 'centerFly': + this._clickCenterFly(x, y, px, py); + break; + case 'book': + this._clickBook(x, y, px, py); + break; + case 'book-detail': + this._clickBookDetail(x, y, px, py); + break; + case 'fly': + this._clickFly(x, y, px, py); + break; + case 'viewMaps': + this._clickViewMaps(x, y, px, py); + break; + case 'switchs': + this._clickSwitchs(x, y, px, py); + break; + case 'switchs-sounds': + this._clickSwitchs_sounds(x, y, px, py); + break; + case 'switchs-display': + this._clickSwitchs_display(x, y, px, py); + break; + case 'switchs-action': + this._clickSwitchs_action(x, y, px, py); + break; + case 'settings': + this._clickSettings(x, y, px, py); + break; + case 'selectShop': + this._clickQuickShop(x, y, px, py); + break; + case 'equipbox': + this._clickEquipbox(x, y, px, py); + break; + case 'toolbox': + this._clickToolbox(x, y, px, py); + break; + case 'save': + case 'load': + case 'replayLoad': + case 'replayRemain': + case 'replaySince': + this._clickSL(x, y, px, py); + break; + case 'confirmBox': + this._clickConfirmBox(x, y, px, py); + break; + case 'keyBoard': + this._clickKeyBoard(x, y, px, py); + break; + case 'action': + this._clickAction(x, y, px, py); + break; + case 'text': + core.drawText(); + break; + case 'notes': + this._clickNotes(x, y, px, py); + break; + case 'syncSave': + this._clickSyncSave(x, y, px, py); + break; + case 'syncSelect': + this._clickSyncSelect(x, y, px, py); + break; + case 'localSaveSelect': + this._clickLocalSaveSelect(x, y, px, py); + break; + case 'storageRemove': + this._clickStorageRemove(x, y, px, py); + break; + case 'cursor': + this._clickCursor(x, y, px, py); + break; + case 'replay': + this._clickReplay(x, y, px, py); + break; + case 'gameInfo': + this._clickGameInfo(x, y, px, py); + break; + case 'about': + case 'help': + core.ui.closePanel(); + break; + } + + // --- 长按判定 + if (core.timeout.onDownTimeout == null) { + core.timeout.onDownTimeout = setTimeout(function () { + if (core.interval.onDownInterval == null) { + core.interval.onDownInterval = setInterval(function () { + if (!core.actions.longClick(x, y, px, py)) { + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + } + }, 40) + } + }, 500); + } + return true; +} + +actions.prototype._sys_ondown = function (x, y, px, py) { + if (core.status.lockControl) return false; + core.status.downTime = new Date(); + core.deleteCanvas('route'); + var pos = { 'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32) }; + core.status.stepPostfix = []; + core.status.stepPostfix.push(pos); + core.fillRect('ui', pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8, '#bfbfbf'); + + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + core.status.preview.prepareDragging = false; + if (!core.hasFlag('__lockViewport__') && (core.status.thisMap.width > core._WIDTH_ || core.status.thisMap.height > core._HEIGHT_)) { + core.status.preview.prepareDragging = true; + core.status.preview.px = px; + core.status.preview.py = py; + core.timeout.onDownTimeout = setTimeout(function () { + core.clearMap('ui'); + core.status.preview.prepareDragging = false; + core.status.preview.enabled = true; + core.status.preview.dragging = true; + core.drawTip('已进入预览模式,可直接拖动大地图'); + core.status.stepPostfix = []; + }, 500); + } +} + +////// 当在触摸屏上滑动时 ////// +actions.prototype.onmove = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('onmove', x, y, px, py); +} + +actions.prototype._sys_onmove_choices = function (x, y, px, py) { + if (!core.status.lockControl) return false; + + switch (core.status.event.id) { + case 'action': + if (core.status.event.data.type == 'choices') { + this._onMoveChoices(x, y); + return true; + } + if (core.status.event.data.type == 'confirm') { + this._onMoveConfirmBox(x, y, px, py); + return true; + } + break; + case 'selectShop': + case 'switchs': + case 'switchs-sounds': + case 'switchs-display': + case 'switchs-action': + case 'notes': + case 'settings': + case 'syncSave': + case 'syncSelect': + case 'localSaveSelect': + case 'storageRemove': + case 'replay': + case 'gameInfo': + this._onMoveChoices(x, y); + return true; + case 'confirmBox': + this._onMoveConfirmBox(x, y, px, py); + return true; + default: + break; + } + return false; +} + +actions.prototype._sys_onmove = function (x, y, px, py) { + if (core.status.lockControl) return false; + + if (core.status.preview.dragging) { + core.setViewport(core.bigmap.offsetX - px + core.status.preview.px, core.bigmap.offsetY - py + core.status.preview.py); + core.status.preview.px = px; + core.status.preview.py = py; + return true; + } + if (core.status.preview.prepareDragging) { + if (Math.abs(px - core.status.preview.px) <= 20 && Math.abs(py - core.status.preview.py) <= 20) + return true; + else core.status.preview.prepareDragging = false; + } + + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + + if ((core.status.stepPostfix || []).length > 0) { + var pos = { 'x': parseInt((px + core.bigmap.offsetX) / 32), 'y': parseInt((py + core.bigmap.offsetY) / 32) }; + var pos0 = core.status.stepPostfix[core.status.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 ii = 0; ii < 4; ii++) { + if (directionDistance[ii] > max) { + index = ii; + max = directionDistance[ii]; + } + } + 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; + core.status.stepPostfix.push(pos); + core.fillRect('ui', pos.x * 32 + 12 - core.bigmap.offsetX, pos.y * 32 + 12 - core.bigmap.offsetY, 8, 8, '#bfbfbf'); + } + } + return true; +} + +////// 当点击(触摸)事件放开时 ////// +actions.prototype.onup = function (loc) { + var x = parseInt(loc.x / loc.size), y = parseInt(loc.y / loc.size); + var px = parseInt(loc.x / core.domStyle.scale), py = parseInt(loc.y / core.domStyle.scale); + this.doRegisteredAction('onup', x, y, px, py); +} + +actions.prototype._sys_onup = function (x, y, px, py) { + clearTimeout(core.timeout.onDownTimeout); + core.timeout.onDownTimeout = null; + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = null; + + if (core.isPlaying()) { + core.status.preview.prepareDragging = false; + if (core.status.preview.dragging) { + core.status.preview.dragging = false; + return true; + } + } + + if ((core.status.stepPostfix || []).length == 0) return false; + + var stepPostfix = []; + var direction = { '0': { '1': 'down', '-1': 'up' }, '-1': { '0': 'left' }, '1': { '0': 'right' } }; + for (var ii = 1; ii < core.status.stepPostfix.length; ii++) { + var pos0 = core.status.stepPostfix[ii - 1]; + var pos = core.status.stepPostfix[ii]; + stepPostfix.push({ + 'direction': direction[pos.x - pos0.x][pos.y - pos0.y], + 'x': pos.x, + 'y': pos.y + }); + } + var posx = core.status.stepPostfix[0].x; + var posy = core.status.stepPostfix[0].y; + core.status.stepPostfix = []; + if (!core.status.lockControl) { + core.clearMap('ui'); + } + + // 长按 + if (!core.status.lockControl && stepPostfix.length == 0 && core.status.downTime != null && new Date() - core.status.downTime >= 1000) { + core.actions.longClick(x, y, px, py); + } + else { + //posx,posy是寻路的目标点,stepPostfix是后续的移动 + core.setAutomaticRoute(posx, posy, stepPostfix); + } + core.status.downTime = null; + return true; +} + +////// 获得点击事件相对左上角的坐标 ////// +actions.prototype._getClickLoc = function (x, y) { + + var statusBar = { 'x': 0, 'y': 0 }; + var size = 32; + size = size * core.domStyle.scale; + + if (core.domStyle.isVertical) { + statusBar.x = 3; + statusBar.y = core.dom.statusBar.offsetHeight + 3; + } + else { + statusBar.x = core.dom.statusBar.offsetWidth + 3; + statusBar.y = 3; + } + + var left = core.dom.gameGroup.offsetLeft + statusBar.x; + var top = core.dom.gameGroup.offsetTop + statusBar.y; + var loc = { 'x': Math.max(x - left), 'y': Math.max(y - top, 0), 'size': size }; + return loc; +} + + +////// 滑动鼠标滚轮时的操作 ////// +actions.prototype.onmousewheel = function (direct) { + this.doRegisteredAction('onmousewheel', direct); +} + +actions.prototype._sys_onmousewheel = function (direct) { + // 向下滚动是 -1 ,向上是 1 + + if (this._checkReplaying()) { + // 滚轮控制速度 + if (direct == 1) core.speedUpReplay(); + if (direct == -1) core.speedDownReplay(); + return; + } + + // 楼层飞行器 + if (core.status.lockControl && core.status.event.id == 'fly') { + if (direct == 1) core.ui.drawFly(this._getNextFlyFloor(1)); + if (direct == -1) core.ui.drawFly(this._getNextFlyFloor(-1)); + return; + } + + // 怪物手册 + if (core.status.lockControl && core.status.event.id == 'book') { + var pageinfo = core.ui._drawBook_pageinfo(); + if (direct == 1) core.ui.drawBook(core.status.event.data - pageinfo.per_page); + if (direct == -1) core.ui.drawBook(core.status.event.data + pageinfo.per_page); + return; + } + + // 存读档 + if (core.status.lockControl && (core.status.event.id == 'save' || core.status.event.id == 'load')) { + var index = core.status.event.data.page * 10 + core.status.event.data.offset; + if (direct == 1) core.ui._drawSLPanel(index - 10); + if (direct == -1) core.ui._drawSLPanel(index + 10); + return; + } + + // 浏览地图 + if (core.status.lockControl && core.status.event.id == 'viewMaps') { + if (direct == 1) this._clickViewMaps(this._HX_, this._HY_ - 3, core._PX_ / 2, core._PY_ / 5 * 1.5); + if (direct == -1) this._clickViewMaps(this._HX_, this._HY_ + 3, core._PX_ / 2, core._PY_ / 5 * 3.5); + return; + } + + // wait事件 + if (core.status.lockControl && core.status.event.id == 'action' && core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 0); + var keycode = direct == 1 ? 33 : 34; + core.setFlag('keycode', keycode); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + keycode)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + +} + +////// 长按Ctrl键时 ////// +actions.prototype.keyDownCtrl = function () { + this.doRegisteredAction('keyDownCtrl'); +} + +actions.prototype._sys_keyDownCtrl = function () { + if (core.status.event.id == 'text') { + core.drawText(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'text') { + core.doAction(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' + && !core.status.event.data.current.noSkip) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { + clearTimeout(core.timeout.sleepTimeout); + core.timeout.sleepTimeout = null; + core.doAction(); + } + return true; + } +} + +////// 长按 ////// +actions.prototype.longClick = function (x, y, px, py) { + if (!core.isPlaying()) return false; + return this.doRegisteredAction('longClick', x, y, px, py); +} + +actions.prototype._sys_longClick_lockControl = function (x, y, px, py) { + if (!core.status.lockControl) return false; + if (core.status.event.id == 'text') { + core.drawText(); + return true; + } + if (core.status.event.id == 'action' && core.status.event.data.type == 'text') { + core.doAction(); + return true; + } + // 长按楼传器的箭头可以快速翻页 + if (core.status.event.id == 'fly') { + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && (y == this._HY_ - 1 || y == this._HY_ + 3)) { + this._clickFly(x, y); + return true; + } + } + // 长按SL上下页快速翻页 + if (["save", "load", "replayLoad", "replayRemain", "replaySince"].indexOf(core.status.event.id) >= 0) { + if ([this._HX_ - 2, this._HX_ - 3, this._HX_ + 2, this._HX_ + 3].indexOf(x) >= 0 && y===core._HEIGHT_-1) { + this._clickSL(x, y); + return true; + } + } + // 长按可以跳过等待事件 + if (core.status.event.id == 'action' && core.status.event.data.type == 'sleep' + && !core.status.event.data.current.noSkip) { + if (core.timeout.sleepTimeout && !core.hasAsync()) { + clearTimeout(core.timeout.sleepTimeout); + core.timeout.sleepTimeout = null; + core.doAction(); + return true; + } + } + return false; +} + +actions.prototype.onStatusBarClick = function (e) { + if (!core.isPlaying()) return false; + var left = core.dom.gameGroup.offsetLeft + 3; + var top = core.dom.gameGroup.offsetTop + 3; + var px = parseInt((e.clientX - left) / core.domStyle.scale), py = parseInt((e.clientY - top) / core.domStyle.scale); + return this.doRegisteredAction('onStatusBarClick', Math.max(px, 0), Math.max(py, 0)); +} + +actions.prototype._sys_onStatusBarClick = function (px, py, vertical) { + if (this.actionsdata.onStatusBarClick) + return this.actionsdata.onStatusBarClick(px, py, vertical); +} + +/////////////////// 在某个界面时的按键点击效果 /////////////////// + +actions.prototype._getChoicesTopIndex = function (length) { + return this._HY_ - parseInt((length - 1) / 2) + (core.status.event.ui.offset || 0); +} + +// 数字键快速选择选项 +actions.prototype._selectChoices = function (length, keycode, callback) { + var topIndex = this._getChoicesTopIndex(length); + if (keycode == 13 || keycode == 32 || keycode == 67) { + callback.apply(this, [this._HX_, topIndex + core.status.event.selection]); + } + + if (keycode >= 49 && keycode <= 57) { + var index = keycode - 49; + if (index < length) { + callback.apply(this, [this._HX_, topIndex + index]); + } + } +} + +// 上下键调整选项 +actions.prototype._keyDownChoices = function (keycode) { + if (keycode == 38) { + core.status.event.selection--; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } + if (keycode == 40) { + core.status.event.selection++; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } +} + +// 移动光标 +actions.prototype._onMoveChoices = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + if (selection == core.status.event.selection) return; + core.status.event.selection = selection; + core.playSound('光标移动'); + core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices, core.status.event.ui.width); + } +} + +////// 点击中心对称飞行器时 +actions.prototype._clickCenterFly = function (x, y) { + var posX = core.status.event.data.posX, posY = core.status.event.data.posY; + core.ui.closePanel(); + if (x == posX && y == posY) { + if (core.canUseItem('centerFly')) { + core.useItem('centerFly'); + } + else { + core.playSound('操作失败'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name, 'centerFly'); + } + } +} + +actions.prototype._keyUpCenterFly = function (keycode) { + core.ui.closePanel(); + if (keycode == 51 || keycode == 13 || keycode == 32 || keycode == 67) { + if (core.canUseItem('centerFly')) { + core.useItem('centerFly'); + } + else { + core.playSound('操作失败'); + core.drawTip('当前不能使用' + core.material.items['centerFly'].name, 'centerFly'); + } + } +} + +////// 点击确认框时 ////// +actions.prototype._clickConfirmBox = function (x, y, px, py) { + if (px >= core._PX_ / 2 - 70 && px <= core._PX_ / 2 - 10 + && py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64 && core.status.event.data.yes) + core.status.event.data.yes(); + if (px >= core._PX_ / 2 + 10 && px <= core._PX_ / 2 + 70 + && py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64 && core.status.event.data.no) + core.status.event.data.no(); +} + +////// 键盘操作确认框时 ////// +actions.prototype._keyUpConfirmBox = function (keycode) { + if (keycode == 37 || keycode == 39) { + core.status.event.selection = 1 - core.status.event.selection; + core.playSound('光标移动'); + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (core.status.event.selection == 0 && core.status.event.data.yes) { + // core.playSound('确定'); + core.status.event.selection = null; + core.status.event.data.yes(); + return; + } + if (core.status.event.selection == 1 && core.status.event.data.no) { + // core.playSound('确定'); + core.status.event.selection = null; + core.status.event.data.no(); + return; + } + } +} + +////// 鼠标在确认框上移动时 ////// +actions.prototype._onMoveConfirmBox = function (x, y, px, py) { + if (py >= core._PY_ / 2 && py <= core._PY_ / 2 + 64) { + if (px >= core._PX_ / 2 - 70 && px <= core._PX_ / 2 - 10) { + if (core.status.event.selection != 0) { + core.status.event.selection = 0; + core.playSound('光标移动'); + if (core.status.event.id == 'action') { + core.ui.drawConfirmBox(core.status.event.ui.text); + } else { + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + } + } + return; + } + if (px >= core._PX_ / 2 + 10 && px <= core._PX_ / 2 + 70) { + if (core.status.event.selection != 1) { + core.status.event.selection = 1; + core.playSound('光标移动'); + if (core.status.event.id == 'action') { + core.ui.drawConfirmBox(core.status.event.ui.text); + } else { + core.ui.drawConfirmBox(core.status.event.ui, core.status.event.data.yes, core.status.event.data.no); + } + } + return; + } + } +} + +actions.prototype._clickAction_text = function () { + // 正在淡入淡出的话不执行 + if (core.status.event.animateUI) return; + + var data = core.clone(core.status.event.data.current); + if (typeof data == 'string') data = { "type": "text", "text": data }; + + // 打字机效果显示全部文字 + if (core.status.event.interval != null) { + data.showAll = true; + core.insertAction(data); + core.doAction(); + return; + } + + if (!data.code) { + core.ui._animateUI('hide', null, core.doAction); + } else { + // 不清除对话框 + core.doAction(); + } +} + +////// 自定义事件时的点击操作 ////// +actions.prototype._clickAction = function (x, y, px, py) { + if (core.status.event.data.type == 'text') { + return this._clickAction_text(); + } + + if (core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 1); + core.setFlag('x', x); + core.setFlag('y', y); + core.setFlag('px', px); + core.setFlag('py', py); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + 1000000 + 1000 * px + py)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + + if (core.status.event.data.type == 'choices') { + // 选项 + var data = core.status.event.data.current; + var choices = data.choices; + if (choices.length == 0) return; + if (this._out(x)) return; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var choice = choices[y - topIndex]; + if (choice.need != null && choice.need != '' && !core.calValue(choice.need)) { + core.playSound('操作失败'); + core.drawTip("无法选择此项"); + return; + } + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + // 对全局商店特殊处理 + var index = y - topIndex; + if (index == choices.length - 1 && core.hasFlag('@temp@shop')) { + index = -1; + } + core.status.route.push("choices:" + (100 * timeout + index)); + core.insertAction(choice.action); + core.doAction(); + } + return; + } + + if (core.status.event.data.type == 'confirm') { + if ((x == this._HX_ - 2 || x == this._HX_ - 1) && y == this._HY_ + 1) { + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + 100 * timeout); + core.insertAction(core.status.event.ui.yes); + core.doAction(); + } + else if ((x == this._HX_ + 2 || x == this._HX_ + 1) && y == this._HY_ + 1) { + clearTimeout(core.status.event.interval); + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + (100 * timeout + 1)); + core.insertAction(core.status.event.ui.no); + core.doAction(); + } + return; + } +} + +////// 自定义事件时,按下某个键的操作 ////// +actions.prototype._keyDownAction = function (keycode) { + if (core.status.event.data.type == 'choices') { + this._keyDownChoices(keycode); + return; + } + if (core.status.event.data.type == 'confirm' && (keycode == 37 || keycode == 39)) { + core.status.event.selection = 1 - core.status.event.selection; + core.playSound('光标移动'); + core.drawConfirmBox(core.status.event.ui.text); + return; + } +} + +////// 自定义事件时,放开某个键的操作 ////// +actions.prototype._keyUpAction = function (keycode) { + if (core.status.event.data.type == 'text' && (keycode == 13 || keycode == 32 || keycode == 67)) { + return this._clickAction_text(); + } + if (core.status.event.data.type == 'wait') { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + core.setFlag('type', 0); + core.setFlag('keycode', keycode); + core.setFlag('timeout', timeout); + var executed = core.events.__action_wait_afterGet(core.status.event.data.current); + if (executed || !core.status.event.data.current.forceChild) { + core.status.route.push("input:" + (1e8 * timeout + keycode)); + clearTimeout(core.status.event.interval); + delete core.status.event.timeout; + core.doAction(); + } + return; + } + if (core.status.event.data.type == 'choices') { + var data = core.status.event.data.current; + var choices = data.choices; + if (choices.length > 0) { + this._selectChoices(choices.length, keycode, this._clickAction); + } + return; + } + if (core.status.event.data.type == 'confirm' && (keycode == 13 || keycode == 32 || keycode == 67)) { + var timeout = Math.max(0, core.status.event.timeout - new Date().getTime()) || 0; + delete core.status.event.timeout; + core.setFlag('timeout', timeout); + core.status.route.push("choices:" + (100 * timeout + core.status.event.selection)); + if (core.status.event.selection == 0) + core.insertAction(core.status.event.ui.yes); + else core.insertAction(core.status.event.ui.no); + core.doAction(); + return; + } +} + +////// 怪物手册界面的点击操作 ////// +actions.prototype._clickBook = function (x, y) { + var pageinfo = core.ui._drawBook_pageinfo(); + // 上一页 + if ((x == this._HX_ - 2 || x == this._HX_ - 3) && y===core._HEIGHT_-1) { + core.playSound('光标移动'); + core.ui.drawBook(core.status.event.data - pageinfo.per_page); + return; + } + // 下一页 + if ((x == this._HX_ + 2 || x == this._HX_ + 3) && y===core._HEIGHT_-1) { + core.playSound('光标移动'); + core.ui.drawBook(core.status.event.data + pageinfo.per_page); + return; + } + // 返回 + if (x >= this.LAST - 2 && y===core._HEIGHT_-1) { + core.playSound('取消'); + if (core.events.recoverEvents(core.status.event.interval)) { + return; + } + else if (core.status.event.ui != null) { + core.status.boxAnimateObjs = []; + core.ui._drawViewMaps(core.status.event.ui); + } + else core.ui.closePanel(); + return; + } + // 怪物信息 + var data = core.status.event.data; + if (data != null && y < core._HEIGHT_-1) { + var per_page = pageinfo.per_page, page = parseInt(data / per_page); + var u = (core._HEIGHT_ - 1) / per_page; + for (var i = 0; i < per_page; ++i) { + if (y >= u * i && y < u * (i + 1)) { + var index = per_page * page + i; + core.ui.drawBook(index); + core.ui._drawBookDetail(index); + break; + } + } + return; + } + return; +} + +////// 怪物手册界面时,按下某个键的操作 ////// +actions.prototype._keyDownBook = function (keycode) { + var pageinfo = core.ui._drawBook_pageinfo(); + if (keycode == 37) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - pageinfo.per_page); } + if (keycode == 38) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - 1); } + if (keycode == 39) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + pageinfo.per_page); } + if (keycode == 40) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + 1); } + if (keycode == 33) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data - pageinfo.per_page); } + if (keycode == 34) { core.playSound('光标移动'); core.ui.drawBook(core.status.event.data + pageinfo.per_page); } + return; +} + +////// 怪物手册界面时,放开某个键的操作 ////// +actions.prototype._keyUpBook = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + if (core.events.recoverEvents(core.status.event.interval)) { + return; + } + else if (core.status.event.ui != null) { + core.status.boxAnimateObjs = []; + core.ui._drawViewMaps(core.status.event.ui); + } + else core.ui.closePanel(); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + var data = core.status.event.data; + if (data != null) { + core.ui._drawBookDetail(data); + } + return; + } +} + +////// 怪物手册属性显示界面时的点击操作 ////// +actions.prototype._clickBookDetail = function () { + core.clearMap('data'); + core.playSound('取消'); + core.status.event.id = 'book'; +} + +////// 楼层传送器界面时的点击操作 ////// +actions.prototype._clickFly = function (x, y) { + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ + 3) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-1)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ - 1) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(1)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ + 4) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-10)); } + if ((x == core._WIDTH_ - 2 || x == core._WIDTH_ - 3) && y == this._HY_ - 2) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(10)); } + if (x >= this._HX_ - 1 && x <= this._HX_ + 1 && y===core._HEIGHT_-1) { core.playSound('取消'); core.ui.closePanel(); } + if (x >= 0 && x <= this._HX_ + 3 && y >= 3 && y <= core._HEIGHT_-1 - 1) + core.flyTo(core.floorIds[core.status.event.data]); + return; +} + +////// 楼层传送器界面时,按下某个键的操作 ////// +actions.prototype._keyDownFly = function (keycode) { + if (keycode == 37) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-10)); } + else if (keycode == 38) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(1)); } + else if (keycode == 39) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(10)); } + else if (keycode == 40) { core.playSound('光标移动'); core.ui.drawFly(this._getNextFlyFloor(-1)); } + return; +} + +actions.prototype._getNextFlyFloor = function (delta, index) { + if (index == null) index = core.status.event.data; + if (delta == 0) return index; + var sign = Math.sign(delta); + delta = Math.abs(delta); + var ans = index; + while (true) { + index += sign; + if (index < 0 || index >= core.floorIds.length) break; + var floorId = core.floorIds[index]; + if (core.status.maps[floorId].canFlyTo && core.hasVisitedFloor(floorId)) { + delta--; + ans = index; + } + if (delta == 0) break; + } + return ans; +} + +////// 楼层传送器界面时,放开某个键的操作 ////// +actions.prototype._keyUpFly = function (keycode) { + if (keycode == 71 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + } + if (keycode == 13 || keycode == 32 || keycode == 67) + this._clickFly(this._HX_ - 1, this._HY_ - 1); + return; +} + +////// 查看地图界面时的点击操作 ////// +actions.prototype._clickViewMaps = function (x, y, px, py) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + var now = core.floorIds.indexOf(core.status.floorId); + var index = core.status.event.data.index; + var cx = core.status.event.data.x, cy = core.status.event.data.y; + var floorId = core.floorIds[index], mh = core.floors[floorId].height; + var perpx = core._PX_ / 5, cornerpx = perpx * 3 / 4, perpy = core._PY_ / 5, cornerpy = perpy * 3 / 4; + + if (px <= cornerpx && py <= cornerpy) { + core.status.event.data.damage = !core.status.event.data.damage; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + if (px <= cornerpx && py >= core._PY_ - cornerpy) { + if (core.markedFloorIds[floorId]) delete core.markedFloorIds[floorId]; + else core.markedFloorIds[floorId] = true; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + if (px >= core._PX_ - cornerpx && py <= cornerpy) { + core.status.event.data.all = !core.status.event.data.all; + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy); + return; + } + + if (px >= perpx && px <= core._PX_ - perpx && py <= perpy && (!core.status.event.data.all && mh > core._HEIGHT_)) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy - 1); + return; + } + if (px >= perpx && px <= core._PX_ - perpx && py >= core._PY_ - perpy && (!core.status.event.data.all && mh > core._HEIGHT_)) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx, cy + 1); + return; + } + if (px <= perpx && py >= perpy && py <= core._PY_ - perpy) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx - 1, cy); + return; + } + if (px >= core._PX_ - perpx && py >= perpy && py <= core._PY_ - perpy) { + core.playSound('光标移动'); + core.ui._drawViewMaps(index, cx + 1, cy); + return; + } + + if (py <= 2 * perpy && (mh == core._HEIGHT_ || (px >= perpx && px <= core._PX_ - perpx))) { + core.playSound('光标移动'); + index++; + while (index < core.floorIds.length && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) + index++; + if (index < core.floorIds.length) + core.ui._drawViewMaps(index); + return; + } + if (py >= 3 * perpy && (mh == core._HEIGHT_ || (px >= perpx && px <= core._PX_ - perpx))) { + core.playSound('光标移动'); + index--; + while (index >= 0 && index != now && core.status.maps[core.floorIds[index]].cannotViewMap) + index--; + if (index >= 0) + core.ui._drawViewMaps(index); + return; + } + if (px >= perpx && px <= core._PX_ - perpx && py >= perpy * 2 && py <= perpy * 3) { + core.clearMap('data'); + core.playSound('取消'); + core.ui.closePanel(); + return; + } +} + +////// 查看地图界面时,按下某个键的操作 ////// +actions.prototype._keyDownViewMaps = function (keycode) { + if (core.status.event.data == null) return; + + var floorId = core.floorIds[core.status.event.data.index], mh = core.floors[floorId].height; + + if (keycode == 38 || keycode == 33) this._clickViewMaps(this._HX_ , this._HY_ - 3 , core._PX_ / 2, core._PY_ / 5 * 1.5); + if (keycode == 40 || keycode == 34) this._clickViewMaps(this._HX_ , this._HY_ + 3 , core._PX_ / 2, core._PY_ / 5 * 3.5); + if (keycode == 87 && mh > core._HEIGHT_) this._clickViewMaps(this._HX_ , 0 , core._PX_ / 2, 1 ); + if (keycode == 65) this._clickViewMaps(0 , this._HY_ , 1 , core._PY_ / 2 ); + if (keycode == 83 && mh > core._HEIGHT_) this._clickViewMaps(this._HX_ , core._HEIGHT_ - 1, core._PX_ / 2, core._PY_ - 1 ); + if (keycode == 68) this._clickViewMaps(core._WIDTH_ - 1, this._HY_ , core._PX_ , core._PY_ / 2 - 1 ); + return; +} + +////// 查看地图界面时,放开某个键的操作 ////// +actions.prototype._keyUpViewMaps = function (keycode) { + if (core.status.event.data == null) { + core.ui._drawViewMaps(core.floorIds.indexOf(core.status.floorId)); + return; + } + var floorId = core.floorIds[core.status.event.data.index]; + + if (keycode == 27 || keycode == 13 || keycode == 32 || (!core.isReplaying() && keycode == 67)) { + core.clearMap('data'); + core.playSound('取消'); + core.ui.closePanel(); + return; + } + if (keycode == 86) { + core.status.event.data.damage = !core.status.event.data.damage; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 90) { + core.status.event.data.all = !core.status.event.data.all; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 66) { + if (core.markedFloorIds[floorId]) delete core.markedFloorIds[floorId]; + else core.markedFloorIds[floorId] = true; + core.playSound('光标移动'); + core.ui._drawViewMaps(core.status.event.data); + return; + } + if (keycode == 88 || (core.isReplaying() && keycode == 67)) { + if (core.isReplaying()) { + core.control._replay_book(); + } else { + core.openBook(false); + } + return; + } + if (keycode == 71 && !core.isReplaying()) { + core.useFly(false); + return; + } + return; +} + +////// 快捷商店界面时的点击操作 ////// +actions.prototype._clickQuickShop = function (x, y) { + var shopIds = core.listShopIds(); + if (this._out(x)) return; + var topIndex = this._HY_ - parseInt(shopIds.length / 2) + (core.status.event.ui.offset || 0); + if (y >= topIndex && y < topIndex + shopIds.length) { + var shopId = shopIds[y - topIndex]; + if (!core.canOpenShop(shopId)) { + core.playSound('操作失败'); + core.drawTip('当前项尚未开启'); + return; + } + var message = core.canUseQuickShop(shopId); + if (message == null) { + // core.ui.closePanel(); + core.openShop(shopIds[y - topIndex], false); + } else { + core.playSound('操作失败'); + core.drawTip(message); + } + } + // 离开 + else if (y == topIndex + shopIds.length) { + core.playSound('取消'); + core.ui.closePanel(); + } +} + +////// 快捷商店界面时,放开某个键的操作 ////// +actions.prototype._keyUpQuickShop = function (keycode) { + if (keycode == 27 || keycode == 75 || keycode == 88 || keycode == 86) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.listShopIds().length + 1, keycode, this._clickQuickShop); + return; +} + +////// 工具栏界面时的点击操作 ////// +actions.prototype._clickToolbox = function (x, y) { + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + + // 装备栏 + if (x >= this.LAST - 2 && y == 0) { + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_equipbox(); + else + core.openEquipbox(); + return; + } + if (x >= this.LAST - 2 && y===core._HEIGHT_-1) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + + var toolsPage = core.status.event.data.toolsPage; + var constantsPage = core.status.event.data.constantsPage; + // 上一页 + if (x == this._HX_ - 2 || x == this._HX_ - 3) { + if (y===core._HEIGHT_-1 - 5 && toolsPage > 1) { + core.status.event.data.toolsPage--; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + if (y===core._HEIGHT_-1 && constantsPage > 1) { + core.status.event.data.constantsPage--; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + } + // 下一页 + if (x == this._HX_ + 2 || x == this._HX_ + 3) { + if (y===core._HEIGHT_-1 - 5 && toolsPage < Math.ceil(tools.length / this.LAST)) { + core.status.event.data.toolsPage++; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + if (y===core._HEIGHT_-1 && constantsPage < Math.ceil(constants.length / this.LAST)) { + core.status.event.data.constantsPage++; + core.playSound('光标移动'); + core.ui._drawToolbox(core.status.event.selection); + } + } + + var index = parseInt(x / 2); + if (y===core._HEIGHT_-1 - 8) index += 0; + else if (y===core._HEIGHT_-1 - 6) index += this._HX_; + else if (y===core._HEIGHT_-1 - 3) index += this.LAST; + else if (y===core._HEIGHT_-1 - 1) index += this.LAST + this._HX_; + else index = -1; + if (index >= 0) + this._clickToolboxIndex(index); +} + +////// 选择工具栏界面中某个Index后的操作 ////// +actions.prototype._clickToolboxIndex = function (index) { + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + + var items = null; + var select; + if (index < this.LAST) { + select = index + this.LAST * (core.status.event.data.toolsPage - 1); + items = tools; + } + else { + select = index % this.LAST + this.LAST * (core.status.event.data.constantsPage - 1); + items = constants; + } + if (items == null) return; + if (select >= items.length) return; + var itemId = items[select]; + if (itemId == core.status.event.data.selectId) { + if (core.isReplaying()) return; + core.events.tryUseItem(itemId); + } + else { + core.playSound('光标移动'); + core.ui._drawToolbox(index); + } +} + +////// 工具栏界面时,按下某个键的操作 ////// +actions.prototype._keyDownToolbox = function (keycode) { + if (core.status.event.data == null) return; + + var last_index = this.LAST - 1; + + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + var index = core.status.event.selection; + var toolsPage = core.status.event.data.toolsPage; + var constantsPage = core.status.event.data.constantsPage; + var toolsTotalPage = Math.ceil(tools.length / this.LAST); + var constantsTotalPage = Math.ceil(constants.length / this.LAST); + var toolsLastIndex = toolsPage < toolsTotalPage ? last_index : (tools.length + last_index) % this.LAST; + var constantsLastIndex = this.LAST + (constantsPage < constantsTotalPage ? last_index : (constants.length + last_index) % this.LAST); + + if (keycode == 37) { // left + if (index == 0) { // 处理向前翻页 + if (toolsPage > 1) { + core.status.event.data.toolsPage--; + index = last_index; + } + else return; // 第一页不向前翻 + } + else if (index == this.LAST) { + if (constantsPage == 1) { + if (toolsTotalPage == 0) return; + core.status.event.data.toolsPage = toolsTotalPage; + index = (tools.length + last_index) % this.LAST; + } + else { + core.status.event.data.constantsPage--; + index = 2 * this.LAST - 1; + } + } + else index -= 1; + this._clickToolboxIndex(index); + return; + } + if (keycode == 38) { // up + if (index >= this.LAST && index < this.LAST + this._HX_) { // 进入tools + if (toolsTotalPage == 0) return; + if (toolsLastIndex >= this._HX_) index = Math.min(toolsLastIndex, index - this._HX_); + else index = Math.min(toolsLastIndex, index - this.LAST); + } + else if (index < this._HX_) return; // 第一行没有向上 + else index -= this._HX_; + this._clickToolboxIndex(index); + return; + } + if (keycode == 39) { // right + if (toolsPage < toolsTotalPage && index == last_index) { + core.status.event.data.toolsPage++; + index = 0; + } + else if (constantsPage < constantsTotalPage && index == 2 * this.LAST - 1) { + core.status.event.data.constantsPage++; + index = this.LAST; + } + else if (index == toolsLastIndex) { + if (constantsTotalPage == 0) return; + core.status.event.data.constantsPage = 1; + index = this.LAST; + } + else if (index == constantsLastIndex) // 一个物品无操作 + return; + else index++; + this._clickToolboxIndex(index); + return; + } + if (keycode == 40) { // down + var nextIndex = null; + if (index < this._HX_) { + if (toolsLastIndex >= this._HX_) nextIndex = Math.min(toolsLastIndex, index + this._HX_); + else index += this._HX_; + } + if (nextIndex == null && index < this.LAST) { + if (constantsTotalPage == 0) return; + nextIndex = Math.min(index + this._HX_, constantsLastIndex); + } + if (nextIndex == null && index < this.LAST + this._HX_) { + if (constantsLastIndex >= this.LAST + this._HX_) + nextIndex = Math.min(constantsLastIndex, index + this._HX_); + } + if (nextIndex != null) { + this._clickToolboxIndex(nextIndex); + } + return; + } +} + +////// 工具栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpToolbox = function (keycode) { + if (keycode == 81) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_equipbox(); + else + core.openEquipbox(); + return; + } + if (keycode == 84 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if (core.status.event.data == null) return; + + if (keycode == 13 || keycode == 32 || keycode == 67) { + this._clickToolboxIndex(core.status.event.selection); + return; + } +} + +////// 装备栏界面时的点击操作 ////// +actions.prototype._clickEquipbox = function (x, y, px, py) { + if (x >= this.LAST - 2 && y == 0) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_toolbox(); + else + core.openToolbox(); + return; + } + if (x >= this.LAST - 2 && y===core._HEIGHT_-1) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if ((x == this._HX_ - 2 || x == this._HX_ - 3) && y===core._HEIGHT_-1) { + if (core.status.event.data.page > 1) { + core.status.event.data.page--; + core.playSound('光标移动'); + core.ui._drawEquipbox(core.status.event.selection); + } + return; + } + if ((x == this._HX_ + 2 || x == this._HX_ + 3) && y===core._HEIGHT_-1) { + var lastPage = Math.ceil(core.getToolboxItems('equips').length / this.LAST); + if (core.status.event.data.page < lastPage) { + core.status.event.data.page++; + core.playSound('光标移动'); + core.ui._drawEquipbox(core.status.event.selection); + } + return; + } + var per_page = this._HX_ - 3, v = core._WIDTH_ / per_page; + if (y === core._HEIGHT_ - 9) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(i); + } else if (y === core._HEIGHT_ - 7) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(per_page + i); + } else if (Math.abs(core._HEIGHT_ - 5 - py / 32) < 0.5) { + for (var i = 0; i < per_page; ++i) if (x >= i * v && x <= (i + 1) * v) return this._clickEquipboxIndex(2 * per_page + i); + } else if (y === core._HEIGHT_ - 4) this._clickEquipboxIndex(this.LAST + parseInt(x / 2)) + else if (y === core._HEIGHT_ - 2) this._clickEquipboxIndex(this.LAST + this._HX_ + parseInt(x / 2)); +} + +////// 选择装备栏界面中某个Index后的操作 ////// +actions.prototype._clickEquipboxIndex = function (index) { + if (index < this.LAST) { + if (index >= core.status.globalAttribute.equipName.length) return; + if (index == core.status.event.selection && core.status.hero.equipment[index]) { + if (core.isReplaying()) return; + core.unloadEquip(index); + core.status.route.push("unEquip:" + index); + } else core.playSound('光标移动'); + } + else { + var equips = core.getToolboxItems('equips'); + if (index == core.status.event.selection) { + if (core.isReplaying()) return; + var equipId = equips[index - this.LAST + (core.status.event.data.page - 1) * this.LAST]; + core.loadEquip(equipId); + core.status.route.push("equip:" + equipId); + } else core.playSound('光标移动'); + } + core.ui._drawEquipbox(index); +} + +////// 装备栏界面时,按下某个键的操作 ////// +actions.prototype._keyDownEquipbox = function (keycode) { + if (core.status.event.data == null) return; + + var last_index = this.LAST - 1; + var per_line = this._HX_ - 3; + var equipCapacity = core.status.globalAttribute.equipName.length; + var ownEquipment = core.getToolboxItems('equips'); + var index = core.status.event.selection; + var page = core.status.event.data.page; + var totalPage = Math.ceil(ownEquipment.length / this.LAST); + var totalLastIndex = this.LAST + (page < totalPage ? last_index : (ownEquipment.length + last_index) % this.LAST); + + if (keycode == 37) { // left + if (index == 0) return; + if (index == this.LAST) { + if (page > 1) { + core.status.event.data.page--; + core.playSound('光标移动'); + index = this.LAST + last_index; + } + else if (page == 1) + index = equipCapacity - 1; + else return; + } + else index -= 1; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 38) { // up + if (index < per_line) return; + else if (index < 2 * per_line) index -= per_line; + else if (index < this.LAST + this._HX_) { + index = parseInt((index - this.LAST) / 2); + if (equipCapacity > per_line) index = Math.min(equipCapacity - 1, index + per_line); + else index = Math.min(equipCapacity - 1, index); + } + else index -= this._HX_; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 39) { // right + if (page < totalPage && index == this.LAST + last_index) { + core.status.event.data.page++; + core.playSound('光标移动'); + index = this.LAST; + } + else if (index == equipCapacity - 1) { + if (totalPage == 0) return; + index = this.LAST; + } + else if (index == totalLastIndex) + return; + else index++; + this._clickEquipboxIndex(index); + return; + } + if (keycode == 40) { // down + if (index < per_line) { + if (equipCapacity > per_line) index = Math.min(index + per_line, equipCapacity - 1); + else { + if (totalPage == 0) return; + index = Math.min(2 * index + 1 + this.LAST, totalLastIndex); + } + } + else if (index < 2 * per_line) { + if (totalPage == 0) return; + index = Math.min(2 * (index - per_line) + 1 + this.LAST, totalLastIndex); + } + else if (index < this.LAST + this._HX_) + index = Math.min(index + this._HX_, totalLastIndex); + else return; + this._clickEquipboxIndex(index); + return; + } +} + +////// 装备栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpEquipbox = function (keycode, altKey) { + if (altKey && keycode >= 48 && keycode <= 57) { + core.items.quickSaveEquip(keycode - 48); + return; + } + if (keycode == 84) { + core.playSound('确定'); + core.ui.closePanel(); + if (core.isReplaying()) + core.control._replay_toolbox(); + else + core.openToolbox(); + return; + } + if (keycode == 81 || keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + var last = core.status.route[core.status.route.length - 1] || ''; + if (last.startsWith('equip:') || last.startsWith('unEquip:')) { + core.status.route.push('no'); + } + core.checkAutoEvents(); + return; + } + if (!core.status.event.data.selectId) return; + + if (keycode == 13 || keycode == 32 || keycode == 67) { + this._clickEquipboxIndex(core.status.event.selection); + return; + } +} + +////// 存读档界面时的点击操作 ////// +actions.prototype._clickSL = function (x, y) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + // 上一页 + if ((x == this._HX_ - 2 || x == this._HX_ - 3) && y===core._HEIGHT_-1) { + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page - 1) + offset); + return; + } + // 下一页 + if ((x == this._HX_ + 2 || x == this._HX_ + 3) && y===core._HEIGHT_-1) { + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page + 1) + offset); + return; + } + // 返回 + if (x >= this.LAST - 2 && y===core._HEIGHT_-1) { + core.playSound('取消'); + if (core.events.recoverEvents(core.status.event.interval)) + return; + core.ui.closePanel(); + delete core.status.tempRoute; + if (!core.isPlaying()) + core.showStartAnimate(true); + return; + } + // 删除 + if (x >= 0 && x <= 2 && y===core._HEIGHT_-1) { + if (core.status.event.id == 'save') { + core.status.event.selection = !core.status.event.selection; + core.ui._drawSLPanel(index); + } + else { // 显示收藏 + core.status.event.data.mode = core.status.event.data.mode == 'all' ? 'fav' : 'all'; + if (core.status.event.data.mode == 'fav') + core.ui._drawSLPanel(1, true); + else { + page = parseInt((core.saves.saveIndex - 1) / 5); + offset = core.saves.saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset, true); + } + } + return; + } + // 点存档名 + var xLeft = parseInt(core._WIDTH_ / 3), xRight = parseInt(core._WIDTH_ * 2 / 3); + var topY1 = 0, topY2 = this._HY_; + if (y >= topY1 && y <= topY1 + 1) { + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 1); + if (x >= xRight) return this._clickSL_favorite(page, 2); + } + if (y >= topY2 && y <= topY2 + 1) { + if (x < xLeft) return this._clickSL_favorite(page, 3); + if (x >= xLeft && x < xRight) return this._clickSL_favorite(page, 4); + if (x >= xRight) return this._clickSL_favorite(page, 5); + } + + var id = null; + if (y >= topY1 + 2 && y < this._HY_ - 1) { + if (x < xLeft) id = "autoSave"; + if (x >= xLeft && x < xRight) id = 5 * page + 1; + if (x >= xRight) id = 5 * page + 2; + } + if (y >= topY2 + 2 && y < core._HEIGHT_ - 1) { + if (x < xLeft) id = 5 * page + 3; + if (x >= xLeft && x < xRight) id = 5 * page + 4; + if (x >= xRight) id = 5 * page + 5; + } + if (id != null) { + if (core.status.event.selection) { + if (id == 'autoSave') { + core.playSound('操作失败'); + core.drawTip("无法删除自动存档!"); + } else { + core.removeSave(id, function () { + core.ui._drawSLPanel(index, true); + }); + } + } + else { + if (core.status.event.data.mode == 'fav' && id != 'autoSave') + id = core.saves.favorite[id - 1]; + core.doSL(id, core.status.event.id); + } + } +} + +actions.prototype._clickSL_favorite = function (page, offset) { + if (offset == 0) return; + var index = 5 * page + offset; + if (core.status.event.data.mode == 'fav') { // 收藏模式下点击的下标直接对应favorite + index = core.saves.favorite[index - 1]; + core.myprompt("请输入想要显示的存档名(长度不超过5字符)", null, function (value) { + if (value && value.length <= 5) { + core.saves.favoriteName[index] = value; + core.control._updateFavoriteSaves(); + core.ui._drawSLPanel(10 * page + offset); + } else if (value) { + alert("无效的输入!"); + } + }); + } else { + var v = core.saves.favorite.indexOf(index); + core.playSound('确定'); + if (v >= 0) { // 已经处于收藏状态:取消收藏 + core.saves.favorite.splice(v, 1); + delete core.saves.favoriteName[index]; + } + else if (core.hasSave(index)) { // 存在存档则进行收藏 + core.saves.favorite.push(index); + core.saves.favorite = core.saves.favorite.sort(function (a, b) { return a - b; }); // 保证有序 + core.drawTip("收藏成功!"); + } + core.control._updateFavoriteSaves(); + core.ui._drawSLPanel(10 * page + offset); + } +} + +////// 存读档界面时,按下某个键的操作 ////// +actions.prototype._keyDownSL = function (keycode) { + + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + if (keycode == 37) { // left + core.playSound('光标移动'); + if (offset == 0) { + core.ui._drawSLPanel(10 * (page - 1) + 5); + } + else { + core.ui._drawSLPanel(index - 1); + } + return; + } + if (keycode == 38) { // up + core.playSound('光标移动'); + if (offset < 3) { + core.ui._drawSLPanel(10 * (page - 1) + offset + 3); + } + else { + core.ui._drawSLPanel(index - 3); + } + return; + } + if (keycode == 39) { // right + core.playSound('光标移动'); + if (offset == 5) { + core.ui._drawSLPanel(10 * (page + 1) + 1); + } + else { + core.ui._drawSLPanel(index + 1); + } + return; + } + if (keycode == 40) { // down + core.playSound('光标移动'); + if (offset >= 3) { + core.ui._drawSLPanel(10 * (page + 1) + offset - 3); + } + else { + core.ui._drawSLPanel(index + 3); + } + return; + } + if (keycode == 33) { // PAGEUP + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page - 1) + offset); + return; + } + if (keycode == 34) { // PAGEDOWN + core.playSound('光标移动'); + core.ui._drawSLPanel(10 * (page + 1) + offset); + return; + } +} + +////// 存读档界面时,放开某个键的操作 ////// +actions.prototype._keyUpSL = function (keycode) { + var page = core.status.event.data.page, offset = core.status.event.data.offset; + var index = page * 10 + offset; + + if (keycode == 27 || keycode == 88 || (core.status.event.id == 'save' && keycode == 83) + || (core.status.event.id == 'load' && keycode == 68)) { + this._clickSL(core._WIDTH_ - 1, core._HEIGHT_ - 1); + return; + } + if (keycode >= 48 && keycode <= 57) { + if (keycode == 48) keycode = 58; + core.ui._drawSLPanel((keycode - 49) * 1000 + 1); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67) { + if (offset == 0) + core.doSL("autoSave", core.status.event.id); + else { + var id = 5 * page + offset; + if (core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.doSL(id, core.status.event.id); + } + return; + } + if (keycode == 69 && core.status.event.id != 'save') { // E 收藏切换 + this._clickSL(0, core._HEIGHT_ - 1); + return; + } + if (keycode == 46) { + if (offset == 0) { + core.playSound('操作失败'); + core.drawTip("无法删除自动存档!"); + } + else { + var id = 5 * page + offset; + if (core.status.event.data.mode == 'fav') id = core.saves.favorite[id - 1]; + core.removeSave(id, function () { + core.ui._drawSLPanel(index, true); + }); + } + } + if (keycode == 70 && core.status.event.data.mode == 'all') { // F + this._clickSL_favorite(page, offset); + } +} + + +////// 系统设置界面时的点击操作 ////// +actions.prototype._clickSwitchs = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) return; + if (selection >= 0 && selection < choices.length) { + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_sounds(); + case 1: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_display(); + case 2: + core.status.event.selection = 0; + core.playSound('确定'); + return core.ui._drawSwitchs_action(); + case 3: + core.status.event.selection = 0; + core.playSound('取消'); + return core.ui._drawSettings(); + } + } +} + +////// 系统设置界面时,放开某个键的操作 ////// +actions.prototype._keyUpSwitchs = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs); +} + +actions.prototype._clickSwitchs_sounds = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + if (selection != 2) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + return this._clickSwitchs_sounds_bgm(); + case 1: + return this._clickSwitchs_sounds_se(); + case 2: + if (x == leftGrid || x == leftGrid + 1) return this._clickSwitchs_sounds_userVolume(-1); + if (x == rightGrid || x == rightGrid + 1) return this._clickSwitchs_sounds_userVolume(1); + return; + case 3: + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_sounds_bgm = function () { + core.triggerBgm(); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._clickSwitchs_sounds_se = function () { + core.musicStatus.soundStatus = !core.musicStatus.soundStatus; + core.setLocalStorage('soundStatus', core.musicStatus.soundStatus); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._clickSwitchs_sounds_userVolume = function (delta) { + var value = Math.round(Math.sqrt(100 * core.musicStatus.userVolume)); + if (value == 0 && delta < 0) return; + core.musicStatus.userVolume = core.clamp(Math.pow(value + delta, 2) / 100, 0, 1); + //audioContext 音效 不受designVolume 影响 + if (core.musicStatus.gainNode != null) core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + if (core.musicStatus.playingBgm) core.material.bgms[core.musicStatus.playingBgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + core.setLocalStorage('userVolume', core.musicStatus.userVolume); + core.playSound('确定'); + core.ui._drawSwitchs_sounds(); +} + +actions.prototype._keyUpSwitchs_sounds = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 2: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume(-1); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 2: core.playSound('确定'); return this._clickSwitchs_sounds_userVolume(1); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_sounds); +} + +actions.prototype._clickSwitchs_display = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + if (selection != 0) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + if (x == leftGrid || x == leftGrid + 1) return this._clickSwitchs_display_setSize(-1); + if (x == rightGrid || x == rightGrid + 1) return this._clickSwitchs_display_setSize(1); + return; + case 1: + core.playSound('确定'); + return this._clickSwitchs_display_enableHDCanvas(); + case 2: + core.playSound('确定'); + return this._clickSwitchs_display_enableEnemyPoint(); + case 3: + core.playSound('确定'); + return this._clickSwitchs_display_enemyDamage(); + case 4: + core.playSound('确定'); + return this._clickSwitchs_display_critical(); + case 5: + core.playSound('确定'); + return this._clickSwitchs_display_extraDamage(); + case 6: + core.playSound('确定'); + return this._clickSwitchs_display_extraDamageType(); + case 7: + core.playSound('确定'); + core.setLocalStorage('autoScale', core.getLocalStorage('autoScale') ? false : true); + core.ui._drawSwitchs_display(); + break; + case 8: + core.status.event.selection = 1; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_display_setSize = function (delta) { + core.setDisplayScale(delta); + var currentRatio = Math.max(window.devicePixelRatio || 1, core.domStyle.scale); + if (currentRatio > core.domStyle.ratio) { + core.drawTip("需刷新页面以调整UI清晰度"); + } + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enableHDCanvas = function () { + core.flags.enableHDCanvas = !core.flags.enableHDCanvas; + core.setLocalStorage('enableHDCanvas', core.flags.enableHDCanvas); + core.drawTip("开关高清UI,需刷新页面方可生效"); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enableEnemyPoint = function () { + core.flags.enableEnemyPoint = !core.flags.enableEnemyPoint; + core.setLocalStorage('enableEnemyPoint', core.flags.enableEnemyPoint); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_enemyDamage = function () { + core.flags.displayEnemyDamage = !core.flags.displayEnemyDamage; + core.updateDamage(); + core.setLocalStorage('enemyDamage', core.flags.displayEnemyDamage); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_critical = function () { + core.flags.displayCritical = !core.flags.displayCritical; + core.updateDamage(); + core.setLocalStorage('critical', core.flags.displayCritical); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_extraDamage = function () { + core.flags.displayExtraDamage = !core.flags.displayExtraDamage; + core.updateDamage(); + core.setLocalStorage('extraDamage', core.flags.displayExtraDamage); + core.ui._drawSwitchs_display(); +} + +actions.prototype._clickSwitchs_display_extraDamageType = function () { + core.flags.extraDamageType = (core.flags.extraDamageType + 1) % 3; + core.updateDamage(); + core.setLocalStorage('extraDamageType', core.flags.extraDamageType); + core.ui._drawSwitchs_display(); +} + +actions.prototype._keyUpSwitchs_display = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 1; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_display_setSize(-1); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_display_setSize(1); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_display); +} + +actions.prototype._clickSwitchs_action = function (x, y) { + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + var selection = y - topIndex; + if (this._out(x)) { + if (selection != 0 && selection != 1) return; + } + if (selection >= 0 && selection < choices.length) { + var width = choices[selection].width; + var leftPos = (core._PX_ - width) / 2, rightPos = (core._PX_ + width) / 2; + var leftGrid = parseInt(leftPos / 32), rightGrid = parseInt(rightPos / 32) - 1; + core.status.event.selection = selection; + switch (selection) { + case 0: + if (x == leftGrid || x == leftGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(-10); } + if (x == rightGrid || x == rightGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(10); } + return; + case 1: + if (x == leftGrid || x == leftGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(-100); } + if (x == rightGrid || x == rightGrid + 1) { core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(100); } + case 2: + core.playSound('确定'); + return this._clickSwitchs_action_potionNoRouting(); + case 3: + core.playSound('确定'); + return this._clickSwitchs_action_clickMove(); + case 4: + core.playSound('确定'); + return this._clickSwitchs_action_leftHandPrefer(); + case 5: + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + } +} + +actions.prototype._clickSwitchs_action_moveSpeed = function (delta) { + core.values.moveSpeed = core.clamp(core.values.moveSpeed + delta, 50, 200); + core.setLocalStorage("moveSpeed", core.values.moveSpeed); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_floorChangeTime = function (delta) { + core.values.floorChangeTime = core.clamp(core.values.floorChangeTime + delta, 0, 2000); + core.setLocalStorage("floorChangeTime", core.values.floorChangeTime); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_potionNoRouting = function () { + if (core.hasFlag('__potionNoRouting__')) core.removeFlag('__potionNoRouting__'); + else core.setFlag('__potionNoRouting__', true); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_clickMove = function () { + if (core.hasFlag('__noClickMove__')) core.removeFlag('__noClickMove__'); + else core.setFlag('__noClickMove__', true); + core.ui._drawSwitchs_action(); +} + +actions.prototype._clickSwitchs_action_leftHandPrefer = function () { + core.flags.leftHandPrefer = !core.flags.leftHandPrefer; + core.setLocalStorage('leftHandPrefer', core.flags.leftHandPrefer); + if (core.flags.leftHandPrefer) { + core.myconfirm("左手模式已开启!\n此模式下WASD将用于移动勇士,IJKL对应于原始的WASD进行存读档等操作。") + } + core.ui._drawSwitchs_action(); +} + +actions.prototype._keyUpSwitchs_action = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSwitchs(); + return; + } + if (keycode == 37) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(-10); + case 1: core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(-100); + } + } else if (keycode == 39) { + switch (core.status.event.selection) { + case 0: core.playSound('确定'); return this._clickSwitchs_action_moveSpeed(10); + case 1: core.playSound('确定'); return this._clickSwitchs_action_floorChangeTime(100); + } + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSwitchs_action); +} + +////// 系统菜单栏界面时的点击操作 ////// +actions.prototype._clickSettings = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSwitchs(); + break; + case 1: + // core.playSound('确定'); + core.ui._drawKeyBoard(); + break; + case 2: + // core.playSound('确定'); + core.clearUI(); + core.ui._drawViewMaps(); + break; + case 3: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawNotes(); + break; + case 4: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSyncSave(); + break; + case 5: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawGameInfo(); + break; + case 6: + return core.confirmRestart(); + case 7: + core.playSound('取消'); + core.ui.closePanel(); + break; + } + } + return; +} + +////// 系统菜单栏界面时,放开某个键的操作 ////// +actions.prototype._keyUpSettings = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSettings); +} + +////// 存档笔记页面时的点击操作 ////// +actions.prototype._clickNotes = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.playSound('确定'); + this._clickNotes_new(); + break; + case 1: + // core.playSound('确定'); + this._clickNotes_show(); + break; + case 2: + core.playSound('确定'); + this._clickNotes_edit(); + break; + case 3: + core.playSound('确定'); + this._clickNotes_delete(); + break; + case 4: + core.status.event.selection = 3; + core.playSound('取消'); + core.ui._drawSettings(); + break; + } + } +} + +actions.prototype.__clickNotes_replaceText = function (data) { + data = (data || "").replace(/[\${}]/g, "_") + .replace(/(\t|\\t)\[.*?\]/g, "") + .replace("\b", "\\b") + .replace(/\\b\[.*?\]/g, "") + .replace(/\n|\\n/g, " "); + if (data.length > 45) data = data.substring(0, 43) + "..."; + return data; +} + +actions.prototype._clickNotes_new = function () { + core.status.hero.notes = core.status.hero.notes || []; + core.myprompt("请输入一段笔记,不超过45字", null, function (data) { + data = core.actions.__clickNotes_replaceText(data); + if (data) { + core.status.hero.notes.push(data); + core.drawText("存档笔记新增成功!"); + } else { + core.ui.closePanel(); + } + }); +} + +actions.prototype._clickNotes_show = function () { + core.playSound('确定'); + core.status.hero.notes = core.status.hero.notes || []; + var result = []; + for (var i = 0; i < core.status.hero.notes.length; i += 5) { + var v = []; + for (var j = i; j < i + 5 && j < core.status.hero.notes.length; ++j) { + v.push(j + 1 + ". " + this.__clickNotes_replaceText(core.status.hero.notes[j])); + } + result.push("\t[存档笔记]" + v.join("\n")); + } + if (result.length == 0) result.push("当前没有存档笔记,试着新增一个吧!\n(菜单栏 -> 存档笔记 -> 新增存档笔记)"); + core.drawText(result); +} + +actions.prototype._clickNotes_edit = function () { + core.status.hero.notes = core.status.hero.notes || []; + if (core.status.hero.notes.length == 0) { + core.drawText("当前没有存档笔记,试着新增一个吧!"); + } else { + core.myprompt("请输入要编辑的存档笔记编号(1 - " + core.status.hero.notes.length + ")", "1", function (data) { + if (!data) core.ui.closePanel(); + var value = parseInt(data) || 0; + if (!value || value <= 0 || value > core.status.hero.notes.length) { + core.drawText("不合法的输入!"); + } else { + core.myprompt("请输入新内容,不超过45字", core.status.hero.notes[value - 1], function (data) { + data = core.actions.__clickNotes_replaceText(data); + if (data) { + core.status.hero.notes[value - 1] = data; + core.drawText("存档笔记编辑成功!"); + } else { + core.ui.closePanel(); + } + }); + } + }) + } +} + +actions.prototype._clickNotes_delete = function () { + core.status.hero.notes = core.status.hero.notes || []; + if (core.status.hero.notes.length == 0) { + core.stopSound(); + core.playSound('操作失败'); + core.drawText("当前没有存档笔记,无法删除!"); + } else { + core.myprompt("请输入要删除的所有存档笔记编号,以逗号分隔。不填则代表删除全部笔记。", null, function (data) { + if (data == null) { + core.ui.closePanel(); + return; + } + else if (!data) { + core.status.hero.notes = []; + core.drawText("所有存档笔记删除成功!"); + } else { + data = data.split(",").map(function (one) { return parseInt(one); }) + .filter(function (one) { return one && one > 0 && one <= core.status.hero.notes.length }); + if (data.length == 0) { + core.drawText("没有要删除的笔记!"); + } else { + data.sort(function (a, b) { return b - a; }) + .forEach(function (index) { + core.status.hero.notes.splice(index - 1, 1); + }); + core.drawText("已删除 " + data.sort().join(",") + " 号笔记"); + } + } + }) + } +} + +////// 存档笔记页面时,放开某个键的操作 ////// +actions.prototype._keyUpNotes = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 3; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickNotes); +} + +////// 同步存档界面时的点击操作 ////// +actions.prototype._clickSyncSave = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawSyncSelect(); + break; + case 1: + core.playSound('确定'); + core.syncLoad(); + break; + case 2: + core.playSound('确定'); + core.status.event.selection = 0; + core.ui._drawLocalSaveSelect(); + break; + case 3: + core.playSound('确定'); + return this._clickSyncSave_readFile(); + case 4: + // core.playSound('确定'); + return this._clickSyncSave_replay(); + case 5: + core.status.event.selection = 0; + core.playSound('确定'); + core.ui._drawStorageRemove(); + break; + case 6: + core.status.event.selection = 4; + core.playSound('取消'); + core.ui._drawSettings(); + break; + + } + } + return; +} + +actions.prototype._clickSyncSave_readFile = function () { + core.readFile(function (obj) { + if (obj.name != core.firstData.name) return alert("存档和游戏不一致!"); + if (obj.version != core.firstData.version) return alert("游戏版本不一致!"); + if (!obj.data) return alert("无效的存档!"); + core.control._syncLoad_write(obj.data); + }, null, ".h5save"); +} + +actions.prototype._clickSyncSave_replay = function () { + core.ui._drawReplay(); +} + +////// 同步存档界面时,放开某个键的操作 ////// +actions.prototype._keyUpSyncSave = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 4; + core.playSound('取消'); + core.ui._drawSettings(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSyncSave); +} + +////// 同步存档选择界面时的点击操作 ////// +actions.prototype._clickSyncSelect = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + core.playSound('确定'); + core.myconfirm('你确定要同步全部存档么?\n这可能在存档较多的时候比较慢。', function () { + core.syncSave('all'); + }); + break; + case 1: + core.playSound('确定'); + core.syncSave(); + break; + case 2: + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSyncSave(); + break; + } + } +} + +////// 同步存档选择界面时,放开某个键的操作 ////// +actions.prototype._keyUpSyncSelect = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 0; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickSyncSelect); +} + +////// 存档下载界面时的点击操作 ////// +actions.prototype._clickLocalSaveSelect = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + if (selection < 2) { + var callback = function (saves) { + if (saves) { + var content = { + "name": core.firstData.name, + "version": core.firstData.version, + "data": saves + } + core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5save", + LZString.compressToBase64(JSON.stringify(content))); + } + }; + if (selection == 0) core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); + } + + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSyncSave(); + } +} + +////// 存档下载界面时,放开某个键的操作 ////// +actions.prototype._keyUpLocalSaveSelect = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 2; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickLocalSaveSelect); +} + +////// 存档删除界面时的点击操作 ////// +actions.prototype._clickStorageRemove = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: + return this._clickStorageRemove_all(); + case 1: + return this._clickStorageRemove_current(); + case 2: + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSyncSave(); + break; + } + } +} + +actions.prototype._clickStorageRemove_all = function () { + core.myconfirm("你确定要清除【全部游戏】的所有本地存档?\n此行为不可逆!!!", function () { + core.ui.drawWaiting("正在清空,请稍候..."); + core.clearLocalForage(function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.saves.autosave.now = 0; + core.saves.cache = {}; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]你的所有存档已被清空。"); + }); + }); +} + +actions.prototype._clickStorageRemove_current = function () { + core.myconfirm("你确定要清除本游戏的所有本地存档?\n此行为不可逆!!!", function () { + var done = function () { + core.saves.ids = {}; + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + core.saves.autosave.now = 0; + core.ui.closePanel(); + core.saves.saveIndex = 1; + core.saves.favorite = []; + core.saves.favoriteName = {}; + core.control._updateFavoriteSaves(); + core.removeLocalStorage('saveIndex'); + core.drawText("\t[操作成功]当前塔的存档已被清空。"); + } + core.ui.drawWaiting("正在清空,请稍候..."); + Object.keys(core.saves.ids).forEach(function (v) { + core.removeLocalForage("save" + v); + }); + core.removeLocalForage("autoSave", done); + }); +} + +////// 存档删除界面时,放开某个键的操作 ////// +actions.prototype._keyUpStorageRemove = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSyncSave(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickStorageRemove); +} + +////// 回放选择界面时的点击操作 ////// +actions.prototype._clickReplay = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: core.playSound('确定'); return this._clickReplay_fromBeginning(); + case 1: core.playSound('确定'); return this._clickReplay_fromLoad(); + case 2: core.playSound('确定'); return this._clickReplay_replayRemain(); + case 3: core.playSound('确定'); return this._clickReplay_replaySince(); + case 4: core.playSound('确定'); return core.chooseReplayFile(); + case 5: core.playSound('确定'); return this._clickReplay_download(); + case 6: core.playSound('取消'); return core.ui.closePanel(); + } + } +} + +actions.prototype._clickReplay_fromBeginning = function () { + core.ui.closePanel(); + core.startGame(core.status.hard, core.getFlag('__seed__'), core.cloneArray(core.status.route)); +} + +actions.prototype._clickReplay_fromLoad = function () { + core.status.event.id = 'replayLoad'; + core.status.event.selection = null; + core.clearUI(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); +} + +actions.prototype._clickReplay_replayRemain = function () { + core.closePanel(); + core.drawText([ + "\t[接续播放录像]该功能允许你播放\r[yellow]两个存档之间的录像\r,常常用于\r[yellow]区域优化\r。\n" + + "例如,有若干个区,已经全部通关;之后重打一区并进行了优化,则可以对剩余区域直接播放录像而无需全部重打。\n\n" + + "详细使用方法参见露珠录制的视频教程:\n\r[yellow]https://bilibili.com/video/BV1az4y1C78x", + "\t[步骤1]请选择一个存档。\n\r[yellow]该存档的坐标必须和当前勇士坐标完全相同。\r\n将尝试从此处开始回放。", + ], function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +actions.prototype._clickReplay_replaySince = function () { + core.closePanel(); + core.drawText([ + "\t[播放存档剩余录像]该功能为【接续播放录像】的简化版本,允许你播放\r[yellow]一个存档中剩余的录像\r,常常用于\r[yellow]录像局部优化\r。\n" + + "在录像正常播放中,你随时可以暂停并按S键进行存档;此时\r[yellow]剩余录像\r也会被记在存档中(在读档界面用\r[yellow][R]\r标识。)\n" + + "之后,你可以选择在路线优化后直接播放该存档的\r[yellow]剩余录像\r,而无需再像接续播放一样选择录像起点和终点。\n\n" + + "详细使用方法参见露珠录制的视频教程:\n\r[yellow]https://bilibili.com/video/BV1az4y1C78x", + "请选择一个存档。\n\n\r[yellow]该存档需为录像播放中存的,且坐标必须和当前勇士坐标完全相同。\r\n将尝试播放此存档的剩余录像。", + ], function () { + core.status.event.id = 'replaySince'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +actions.prototype._clickReplay_download = function () { + // if (core.hasFlag('debug')) return core.drawText("\t[系统提示]调试模式下无法下载录像"); + core.download(core.firstData.name + "_" + core.formatDate2() + ".h5route", + LZString.compressToBase64(JSON.stringify({ + 'name': core.firstData.name, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + }))); + +} + +////// 回放选择界面时,放开某个键的操作 ////// +actions.prototype._keyUpReplay = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickReplay); +} + +////// 游戏信息界面时的点击操作 ////// +actions.prototype._clickGameInfo = function (x, y) { + if (this._out(x)) return; + var choices = core.status.event.ui.choices; + + var topIndex = this._getChoicesTopIndex(choices.length); + + if (y >= topIndex && y < topIndex + choices.length) { + var selection = y - topIndex; + core.status.event.selection = selection; + switch (selection) { + case 0: return core.ui._drawStatistics(); + case 1: return this._clickGameInfo_openProject(); + case 2: return this._clickGameInfo_openComments(); + case 3: return core.ui._drawHelp(); + case 4: return core.ui._drawAbout(); + case 5: return this._clickGameInfo_download(); + case 6: + core.status.event.selection = 5; + core.playSound('取消'); + core.ui._drawSettings(); + break; + } + } +} + +actions.prototype._clickGameInfo_openProject = function () { + if (core.platform.isPC) + window.open("editor.html", "_blank"); + else { + core.myconfirm("即将离开本游戏,跳转至工程页面,确认?", function () { + window.location.href = "editor-mobile.html"; + }); + } +} + +actions.prototype._clickGameInfo_openComments = function () { + if (core.platform.isPC) { + window.open("/score.php?name=" + core.firstData.name, "_blank"); + } + else { + core.myconfirm("即将离开本游戏,跳转至评论页面,确认?", function () { + window.location.href = "/score.php?name=" + core.firstData.name; + }); + } +} + +actions.prototype._clickGameInfo_download = function () { + if (core.platform.isPC) + window.open(core.firstData.name + ".zip"); + else + window.location.href = core.firstData.name + ".zip"; +} + +////// 游戏信息界面时,放开某个键的操作 ////// +actions.prototype._keyUpGameInfo = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.status.event.selection = 5; + core.playSound('取消'); + return core.ui._drawSettings(); + } + this._selectChoices(core.status.event.ui.choices.length, keycode, this._clickGameInfo); +} + +////// “虚拟键盘”界面时的点击操作 ////// +actions.prototype._clickKeyBoard = function (x, y) { + var m = this._HX_; + if (y == this._HY_ - 3 && x >= m - 5 && x <= m + 5) { + core.ui.closePanel(); + core.keyUp(112 + x + 5 - m); + } + if (y == this._HY_ - 2 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + core.keyUp(x == m + 4 ? 48 : 49 + x + 5 - m); // 1-9: 49-57; 0: 48 + } + // 字母 + var lines = [ + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M"], + ]; + if (y == this._HY_ - 1 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + core.keyUp(lines[0][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ && x >= m - 5 && x <= m + 3) { + core.ui.closePanel(); + core.keyUp(lines[1][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ + 1 && x >= m - 5 && x <= m + 1) { + core.ui.closePanel(); + core.keyUp(lines[2][x + 5 - m].charCodeAt(0)); + } + if (y == this._HY_ + 2 && x >= m - 5 && x <= m + 5) { + core.ui.closePanel(); + if (x == m - 5) core.keyUp(189); // - + if (x == m - 4) core.keyUp(187); // = + if (x == m - 3) core.keyUp(219); // [ + if (x == m - 2) core.keyUp(221); // ] + if (x == m - 1) core.keyUp(220); // \ + if (x == m) core.keyUp(186); // ; + if (x == m + 1) core.keyUp(222); // ' + if (x == m + 2) core.keyUp(188); // , + if (x == m + 3) core.keyUp(190); // . + if (x == m + 4) core.keyUp(191); // / + if (x == m + 5) core.keyUp(192); // ` + } + if (y == this._HY_ + 3 && x >= m - 5 && x <= m + 4) { + core.ui.closePanel(); + if (x == m - 5) core.keyUp(27); // ESC + if (x == m - 4) core.keyUp(9); // TAB + if (x == m - 3) core.keyUp(20); // CAPS + if (x == m - 2) core.keyUp(16); // SHIFT + if (x == m - 1) core.keyUp(17); // CTRL + if (x == m) core.keyUp(18); // ALT + if (x == m + 1) core.keyUp(32); // SPACE + if (x == m + 2) core.keyUp(8); // BACKSPACE + if (x == m + 3) core.keyUp(13); // ENTER + if (x == m + 4) core.keyUp(46); // DEL + } + if (y == this._HY_ + 4 && x >= m + 3 && x <= m + 5) { + core.playSound('取消'); + core.ui.closePanel(); + } +} + +////// 光标界面时的点击操作 ////// +actions.prototype._clickCursor = function (x, y, px, py) { + if (x == core.status.automaticRoute.cursorX && y == core.status.automaticRoute.cursorY) { + core.ui.closePanel(); + // 视为按下再放起 + this.doRegisteredAction('ondown', x, y, px, py); + this.doRegisteredAction('onup', x, y, px, py); + return; + } + core.status.automaticRoute.cursorX = x; + core.status.automaticRoute.cursorY = y; + core.ui._drawCursor(); +} + +////// 光标界面时,按下某个键的操作 ////// +actions.prototype._keyDownCursor = function (keycode) { + if (keycode == 37) { // left + core.status.automaticRoute.cursorX--; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 38) { // up + core.status.automaticRoute.cursorY--; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 39) { // right + core.status.automaticRoute.cursorX++; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } + if (keycode == 40) { // down + core.status.automaticRoute.cursorY++; + core.playSound('光标移动'); + core.ui._drawCursor(); + return; + } +} + +////// 光标界面时,放开某个键的操作 ////// +actions.prototype._keyUpCursor = function (keycode) { + if (keycode == 27 || keycode == 88) { + core.playSound('取消'); + core.ui.closePanel(); + return; + } + if (keycode == 13 || keycode == 32 || keycode == 67 || keycode == 69) { + core.playSound('确定'); + core.ui.closePanel(); + var x = core.status.automaticRoute.cursorX; + var y = core.status.automaticRoute.cursorY; + // 视为按下再放起 + this.doRegisteredAction('ondown', x, y, 32 * x + 16, 32 * y + 16); + this.doRegisteredAction('onup', x, y, 32 * x + 16, 32 * y + 16); + return; + } +} diff --git a/libs/control.js b/libs/control.js new file mode 100644 index 0000000..c6e4f30 --- /dev/null +++ b/libs/control.js @@ -0,0 +1,3565 @@ + +/* +control.js:游戏主要逻辑控制 +主要负责status相关内容,以及各种变量获取/存储 +寻路算法和人物行走也在此文件内 + */ + +"use strict"; + +function control () { + this._init(); +} + +control.prototype._init = function () { + this.controldata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.control; + this.renderFrameFuncs = []; + this.replayActions = []; + this.weathers = {}; + this.resizes = []; + this.noAutoEvents = true; + this.updateNextFrame = false; + // --- 注册系统的animationFrame + this.registerAnimationFrame("totalTime", false, this._animationFrame_totalTime); + this.registerAnimationFrame("autoSave", true, this._animationFrame_autoSave); + this.registerAnimationFrame("globalAnimate", true, this._animationFrame_globalAnimate); + this.registerAnimationFrame("animate", true, this._animationFrame_animate); + this.registerAnimationFrame("heroMoving", true, this._animationFrame_heroMoving); + this.registerAnimationFrame("weather", true, this._animationFrame_weather); + this.registerAnimationFrame("tip", true, this._animateFrame_tip); + this.registerAnimationFrame("parallelDo", false, this._animationFrame_parallelDo); + // --- 注册系统的天气 + this.registerWeather("rain", this._weather_rain, this._animationFrame_weather_rain); + this.registerWeather("snow", this._weather_snow, this._animationFrame_weather_snow); + this.registerWeather("fog", this._weather_fog, this.__animateFrame_weather_image); + this.registerWeather("cloud", this._weather_cloud, this.__animateFrame_weather_image); + this.registerWeather("sun", this._weather_sun, this._animationFrame_weather_sun); + // --- 注册系统的replay + this.registerReplayAction("move", this._replayAction_move); + this.registerReplayAction("item", this._replayAction_item); + this.registerReplayAction("equip", this._replayAction_equip); + this.registerReplayAction("unEquip", this._replayAction_unEquip); + this.registerReplayAction("saveEquip", this._replayAction_saveEquip); + this.registerReplayAction("loadEquip", this._replayAction_loadEquip); + this.registerReplayAction("fly", this._replayAction_fly); + this.registerReplayAction("shop", this._replayAction_shop); + this.registerReplayAction("turn", this._replayAction_turn); + this.registerReplayAction("getNext", this._replayAction_getNext); + this.registerReplayAction("moveDirectly", this._replayAction_moveDirectly); + this.registerReplayAction("key", this._replayAction_key); + this.registerReplayAction("click", this._replayAction_click); + this.registerReplayAction("ignoreInput", this._replayAction_ignoreInput); + this.registerReplayAction("no", this._replayAction_no); + // --- 注册系统的resize + this.registerResize("gameGroup", this._resize_gameGroup); + this.registerResize("canvas", this._resize_canvas); + this.registerResize("statusBar", this._resize_statusBar); + this.registerResize("status", this._resize_status); + this.registerResize("toolBar", this._resize_toolBar); + this.registerResize("tools", this._resize_tools); +} + +// ------ requestAnimationFrame 相关 ------ // + +////// 注册一个 animationFrame ////// +// name:名称,可用来作为注销使用;needPlaying:是否只在游戏运行时才执行(在标题界面不执行) +// func:要执行的函数,或插件中的函数名;可接受timestamp(从页面加载完毕到当前所经过的时间)作为参数 +control.prototype.registerAnimationFrame = function (name, needPlaying, func) { + this.unregisterAnimationFrame(name); + this.renderFrameFuncs.push({ name: name, needPlaying: needPlaying, func: func }); +} + +////// 注销一个 animationFrame ////// +control.prototype.unregisterAnimationFrame = function (name) { + this.renderFrameFuncs = this.renderFrameFuncs.filter(function (x) { return x.name != name; }); +} + +////// 设置requestAnimationFrame ////// +control.prototype._setRequestAnimationFrame = function () { + this._checkRequestAnimationFrame(); + core.animateFrame.totalTime = Math.max(core.animateFrame.totalTime, core.getLocalStorage('totalTime', 0)); + var loop = function (timestamp) { + core.control.renderFrameFuncs.forEach(function (b) { + if (b.func) { + try { + if (core.isPlaying() || !b.needPlaying) + core.doFunc(b.func, core.control, timestamp); + } + catch (e) { + console.error(e); + console.error("ERROR in requestAnimationFrame[" + b.name + "]:已自动注销该项。"); + core.unregisterAnimationFrame(b.name); + } + } + }) + window.requestAnimationFrame(loop); + } + window.requestAnimationFrame(loop); +} + +control.prototype._checkRequestAnimationFrame = function () { + (function () { + var lastTime = 0; + var vendors = ['webkit', 'moz']; + for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // Webkit中此取消方法的名字变了 + window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + if (!window.requestAnimationFrame) { + window.requestAnimationFrame = function (callback, element) { + var currTime = new Date().getTime(); + var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); + var id = window.setTimeout(function () { + callback(currTime + timeToCall); + }, timeToCall); + lastTime = currTime + timeToCall; + return id; + }; + } + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function (id) { + clearTimeout(id); + }; + } + }()); +} + +control.prototype._animationFrame_totalTime = function (timestamp) { + core.animateFrame.totalTime += timestamp - core.animateFrame.totalTimeStart; + core.animateFrame.totalTimeStart = timestamp; + if (core.isPlaying()) { + core.status.hero.statistics.totalTime = core.animateFrame.totalTime; + core.status.hero.statistics.currTime += timestamp - (core.status.hero.statistics.start || timestamp); + core.status.hero.statistics.start = timestamp; + } +} + +control.prototype._animationFrame_autoSave = function (timestamp) { + if (timestamp - core.saves.autosave.time <= 5000) return; + core.control.checkAutosave(); + core.saves.autosave.time = timestamp; +} + +control.prototype._animationFrame_globalAnimate = function (timestamp) { + if (timestamp - core.animateFrame.globalTime <= core.values.animateSpeed) return; + core.status.globalAnimateStatus++; + if (core.status.floorId) { + // Global Animate + core.status.globalAnimateObjs.forEach(function (block) { + core.drawBlock(block, core.status.globalAnimateStatus); + }); + + // Global floor images + core.maps._drawFloorImages(core.status.floorId, core.canvas.bg, 'bg', core.status.floorAnimateObjs || [], core.status.globalAnimateStatus); + core.maps._drawFloorImages(core.status.floorId, core.canvas.fg, 'fg', core.status.floorAnimateObjs || [], core.status.globalAnimateStatus); + + // Global Autotile Animate + core.status.autotileAnimateObjs.forEach(function (block) { + core.maps._drawAutotileAnimate(block, core.status.globalAnimateStatus); + }); + + // Global hero animate + if ((core.status.hero || {}).animate && core.status.heroMoving == 0 && main.mode == 'play' && !core.status.preview.enabled) { + core.drawHero('stop', null, core.status.globalAnimateStatus); + } + } + // Box animate + core.drawBoxAnimate(); + core.animateFrame.globalTime = timestamp; +} + +control.prototype._animationFrame_animate = function (timestamp) { + if (timestamp - core.animateFrame.animateTime < 50 || !core.status.animateObjs || core.status.animateObjs.length == 0) return; + core.clearMap('animate'); + // 更新帧 + for (var i = 0; i < core.status.animateObjs.length; i++) { + var obj = core.status.animateObjs[i]; + if (obj.index == obj.animate.frames.length) { + (function (callback) { + setTimeout(function () { + if (callback) callback(); + }); + })(obj.callback); + } + } + core.status.animateObjs = core.status.animateObjs.filter(function (obj) { + return obj.index < obj.animate.frames.length; + }); + core.status.animateObjs.forEach(function (obj) { + if (obj.hero) { + core.maps._drawAnimateFrame('animate', obj.animate, core.status.heroCenter.px, core.status.heroCenter.py, obj.index++); + } else { + core.maps._drawAnimateFrame('animate', obj.animate, obj.centerX, obj.centerY, obj.index++); + } + }); + core.animateFrame.animateTime = timestamp; +} + +control.prototype._animationFrame_heroMoving = function (timestamp) { + if (core.status.heroMoving <= 0) return; + // 换腿 + if (timestamp - core.animateFrame.moveTime > core.values.moveSpeed) { + core.animateFrame.leftLeg = !core.animateFrame.leftLeg; + core.animateFrame.moveTime = timestamp; + } + core.drawHero(core.animateFrame.leftLeg ? 'leftFoot' : 'rightFoot', 4 * core.status.heroMoving); +} + +control.prototype._animationFrame_weather = function (timestamp) { + var weather = core.animateFrame.weather, type = weather.type; + if (!core.dymCanvas.weather || !core.control.weathers[type] || !core.control.weathers[type].frameFunc) return; + try { + core.doFunc(core.control.weathers[type].frameFunc, core.control, timestamp, core.animateFrame.weather.level); + } catch (e) { + console.error(e); + console.error("ERROR in weather[" + type + "]:已自动注销该项。"); + core.unregisterWeather(type); + } +} + +control.prototype._animationFrame_weather_rain = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.strokeStyle = 'rgba(174,194,224,0.8)'; + ctx.lineWidth = 1; + ctx.lineCap = 'round'; + + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.beginPath(); + ctx.moveTo(p.x - ox, p.y - oy); + ctx.lineTo(p.x + p.l * p.xs - ox, p.y + p.l * p.ys - oy); + ctx.stroke(); + + p.x += p.xs; + p.y += p.ys; + if (p.x > core.bigmap.width * 32 || p.y > core.bigmap.height * 32) { + p.x = Math.random() * core.bigmap.width * 32; + p.y = -10; + } + + }); + + ctx.fill(); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animationFrame_weather_snow = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var ctx = core.dymCanvas.weather, ox = core.bigmap.offsetX, oy = core.bigmap.offsetY; + core.clearMap('weather'); + ctx.fillStyle = "rgba(255, 255, 255, 0.8)"; + ctx.beginPath(); + core.animateFrame.weather.data = core.animateFrame.weather.data || 0; + core.animateFrame.weather.data += 0.01; + + var angle = core.animateFrame.weather.data; + core.animateFrame.weather.nodes.forEach(function (p) { + ctx.moveTo(p.x - ox, p.y - oy); + ctx.arc(p.x - ox, p.y - oy, p.r, 0, Math.PI * 2, true); + // update + p.x += Math.sin(angle) * core.animateFrame.weather.level; + p.y += Math.cos(angle + p.d) + 1 + p.r / 2; + if (p.x > core.bigmap.width * 32 + 5 || p.x < -5 || p.y > core.bigmap.height * 32) { + if (Math.random() > 1 / 3) { + p.x = Math.random() * core.bigmap.width * 32; + p.y = -10; + } + else { + if (Math.sin(angle) > 0) + p.x = -5; + else + p.x = core.bigmap.width * 32 + 5; + p.y = Math.random() * core.bigmap.height * 32; + } + } + }); + ctx.fill(); + core.animateFrame.weather.time = timestamp; +} + +control.prototype.__animateFrame_weather_image = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var node = core.animateFrame.weather.nodes[0]; + var image = node.image; + if (!image) return; + core.clearMap('weather'); + core.setAlpha('weather', node.level / 500); + var wind = 1.5; + var width = image.width, height = image.height; + node.x += node.dx * wind; + node.y += (2 * node.dy - 1) * wind; + if (node.x + 3 * width <= core._PX_) { + node.x += 4 * width; + while (node.x > 0) node.x -= width; + } + node.dy += node.delta; + if (node.dy >= 1) { + node.delta = -0.001; + } else if (node.dy <= 0) { + node.delta = 0.001; + } + if (node.y + 3 * height <= core._PY_) { + node.y += 4 * height; + while (node.y > 0) node.y -= height; + } + else if (node.y >= 0) { + node.y -= height; + } + for (var i = 0; i < 3; ++i) { + for (var j = 0; j < 3; ++j) { + if (node.x + (i + 1) * width <= 0 || node.x + i * width >= core._PX_ + || node.y + (j + 1) * height <= 0 || node.y + j * height >= core._PY_) + continue; + core.drawImage('weather', image, node.x + i * width, node.y + j * height); + } + } + core.setAlpha('weather', 1); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animationFrame_weather_sun = function (timestamp, level) { + if (timestamp - core.animateFrame.weather.time < 30) return; + var node = core.animateFrame.weather.nodes[0]; + var opacity = node.opacity + node.delta; + if (opacity > level / 10 + 0.3 || opacity < level / 10 - 0.3) + node.delta = -node.delta; + node.opacity = opacity; + core.setOpacity('weather', core.clamp(opacity, 0, 1)); + core.animateFrame.weather.time = timestamp; +} + +control.prototype._animateFrame_tip = function (timestamp) { + if (core.animateFrame.tip == null) return; + var tip = core.animateFrame.tip; + if (timestamp - tip.time <= 30) return; + var delta = timestamp - tip.time; + tip.time = timestamp; + + core.setFont('data', "16px Arial"); + core.setTextAlign('data', 'left'); + core.clearMap('data', 0, 0, core._PX_, 50); + core.ui._drawTip_drawOne(tip); + if (tip.stage == 1) { + tip.opacity += 0.05; + if (tip.opacity >= 0.6) { + tip.stage = 2; + tip.displayTime = 0; + } + } else if (tip.stage == 2) { + tip.displayTime += delta; + if (tip.displayTime >= 1000) tip.stage = 3; + } else tip.opacity -= 0.05; + + if (tip.opacity <= 0) { + core.animateFrame.tip = null; + } +} + +control.prototype._animationFrame_parallelDo = function (timestamp) { + core.control.controldata.parallelDo(timestamp); +} + +// ------ 标题界面的处理 ------ // + +////// 显示游戏开始界面 ////// +control.prototype.showStartAnimate = function (noAnimate, callback) { + this._showStartAnimate_resetDom(); + if (core.flags.startUsingCanvas || noAnimate) + return this._showStartAnimate_finished(core.flags.startUsingCanvas, callback); + core.hideWithAnimate(core.dom.startTop, 20, function () { + core.control._showStartAnimate_finished(false, callback); + }); +} + +control.prototype._showStartAnimate_resetDom = function () { + core.dom.startPanel.style.opacity = 1; + core.dom.startPanel.style.display = "block"; + core.dom.startTop.style.opacity = 1; + core.dom.startTop.style.display = "block"; + core.dom.startButtonGroup.style.display = 'none'; + core.dom.startButtons.style.display = 'block'; + core.dom.levelChooseButtons.style.display = 'none'; + core.status.played = false; + core.clearStatus(); + core.clearMap('all'); + core.dom.musicBtn.style.display = 'block'; + core.setMusicBtn(); + // 重置音量 + core.events.setVolume(1, 0); + core.updateStatusBar(); +} + +control.prototype._showStartAnimate_finished = function (start, callback) { + core.dom.startTop.style.display = 'none'; + core.dom.startButtonGroup.style.display = 'block'; + main.selectedButton = null; + main.selectButton(0); + if (start) core.startGame(); + if (callback) callback(); +} + +////// 隐藏游戏开始界面 ////// +control.prototype.hideStartAnimate = function (callback) { + core.hideWithAnimate(core.dom.startPanel, 20, callback); +} + +////// 游戏是否已经开始 ////// +control.prototype.isPlaying = function () { + return core.status.played; +} + +////// 清除游戏状态和数据 ////// +control.prototype.clearStatus = function () { + // 停止各个Timeout和Interval + for (var i in core.timeout) { + clearTimeout(core.timeout[i]); + core.timeout[i] = null; + } + for (var i in core.interval) { + clearInterval(core.interval[i]); + core.interval[i] = null; + } + core.status = {}; + core.clearStatusBar(); + core.deleteAllCanvas(); + core.status.played = false; +} + +control.prototype._initStatistics = function (totalTime) { + if (!core.isset(core.status.hero.statistics)) + core.status.hero.statistics = { + 'totalTime': totalTime, + 'currTime': 0, + 'hp': 0, + "battle": 0, + 'money': 0, + 'exp': 0, + 'battleDamage': 0, + 'poisonDamage': 0, + 'extraDamage': 0, + 'moveDirectly': 0, + 'ignoreSteps': 0, + } +} + +// ------ 自动寻路,人物行走 ------ // + +////// 清除自动寻路路线 ////// +control.prototype.clearAutomaticRouteNode = function (x, y) { + core.clearMap('route', x * 32 + 5 - core.status.automaticRoute.offsetX, y * 32 + 5 - core.status.automaticRoute.offsetY, 27, 27); +} + +////// 停止自动寻路操作 ////// +control.prototype.stopAutomaticRoute = function () { + if (!core.status.played) return; + core.status.automaticRoute.autoHeroMove = false; + core.status.automaticRoute.autoStep = 0; + core.status.automaticRoute.destStep = 0; + core.status.automaticRoute.movedStep = 0; + core.status.automaticRoute.autoStepRoutes = []; + core.status.automaticRoute.destX = null; + core.status.automaticRoute.destY = null; + core.status.automaticRoute.lastDirection = null; + core.status.heroStop = true; + if (core.status.automaticRoute.moveStepBeforeStop.length == 0) + core.deleteCanvas('route'); +} + +////// 保存剩下的寻路,并停止 ////// +control.prototype.saveAndStopAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + if (automaticRoute.moveStepBeforeStop.length == 0) { + automaticRoute.moveStepBeforeStop = automaticRoute.autoStepRoutes.slice(automaticRoute.autoStep - 1); + if (automaticRoute.moveStepBeforeStop.length >= 1) + automaticRoute.moveStepBeforeStop[0].step -= automaticRoute.movedStep; + } + this.stopAutomaticRoute(); +} + +////// 继续剩下的自动寻路操作 ////// +control.prototype.continueAutomaticRoute = function () { + // 此函数只应由events.afterOpenDoor和events.afterBattle调用 + var moveStep = core.status.automaticRoute.moveStepBeforeStop; + //core.status.automaticRoute.moveStepBeforeStop = []; + if (moveStep.length === 0 || (moveStep.length === 1 && moveStep[0].step === 1)) { + core.status.automaticRoute.moveStepBeforeStop = []; + } + else { + core.setAutoHeroMove(moveStep); + } +} + +////// 清空剩下的自动寻路列表 ////// +control.prototype.clearContinueAutomaticRoute = function (callback) { + core.deleteCanvas('route'); + core.status.automaticRoute.moveStepBeforeStop = []; + if (callback) callback(); +} + +////// 设置自动寻路路线 ////// +control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) { + if (!core.status.played || core.status.lockControl) return; + if (this._setAutomaticRoute_isMoving(destX, destY)) return; + if (this._setAutomaticRoute_isTurning(destX, destY, stepPostfix)) return; + if (this._setAutomaticRoute_clickMoveDirectly(destX, destY, stepPostfix)) return; + // 找寻自动寻路路线 + var moveStep = core.automaticRoute(destX, destY); + if (moveStep.length == 0 && (destX != core.status.hero.loc.x || destY != core.status.hero.loc.y || stepPostfix.length == 0)) + return; + moveStep = moveStep.concat(stepPostfix); + core.status.automaticRoute.destX = destX; + core.status.automaticRoute.destY = destY; + this._setAutomaticRoute_drawRoute(moveStep); + this._setAutomaticRoute_setAutoSteps(moveStep); + // 立刻移动 + core.setAutoHeroMove(); +} + +control.prototype._setAutomaticRoute_isMoving = function (destX, destY) { + if (core.status.automaticRoute.autoHeroMove) { + var lastX = core.status.automaticRoute.destX, lastY = core.status.automaticRoute.destY; + core.stopAutomaticRoute(); + // 双击瞬移 + if (lastX == destX && lastY == destY) { + core.status.automaticRoute.moveDirectly = true; + setTimeout(function () { + if (core.status.automaticRoute.moveDirectly && core.status.heroMoving == 0) { + core.control.tryMoveDirectly(destX, destY); + } + core.status.automaticRoute.moveDirectly = false; + }, core.values.moveSpeed); + } + return true; + } + return false; +} + +control.prototype._setAutomaticRoute_isTurning = function (destX, destY, stepPostfix) { + if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y && stepPostfix.length == 0) { + if (core.timeout.turnHeroTimeout == null) { + var routeLength = core.status.route.length; + core.timeout.turnHeroTimeout = setTimeout(function () { + if (core.status.route.length == routeLength) core.turnHero(); + clearTimeout(core.timeout.turnHeroTimeout); + core.timeout.turnHeroTimeout = null; + }, 250); + } + else { + clearTimeout(core.timeout.turnHeroTimeout); + core.timeout.turnHeroTimeout = null; + core.getNextItem(); + } + return true; + } + if (core.timeout.turnHeroTimeout != null) return true; + return false; +} + +control.prototype._setAutomaticRoute_clickMoveDirectly = function (destX, destY, stepPostfix) { + // 单击瞬间移动 + if (core.status.heroStop && core.status.heroMoving == 0) { + if (stepPostfix.length <= 1 && !core.hasFlag('__noClickMove__') && core.control.tryMoveDirectly(destX, destY)) + return true; + } + return false; +} + +control.prototype._setAutomaticRoute_drawRoute = function (moveStep) { + // 计算绘制区域的宽高,并尽可能小的创建route层 + var sx = core.bigmap.width * 32, sy = core.bigmap.height * 32, dx = 0, dy = 0; + moveStep.forEach(function (t) { + sx = Math.min(sx, t.x * 32); dx = Math.max(dx, t.x * 32); + sy = Math.min(sy, t.y * 32); dy = Math.max(dy, t.y * 32); + }); + core.status.automaticRoute.offsetX = sx; + core.status.automaticRoute.offsetY = sy; + var ctx = core.createCanvas('route', sx - core.bigmap.offsetX, sy - core.bigmap.offsetY, dx - sx + 32, dy - sy + 32, 95); + ctx.fillStyle = '#bfbfbf'; + ctx.strokeStyle = '#bfbfbf'; + ctx.lineWidth = 8; + for (var m = 0; m < moveStep.length; m++) { + if (m == moveStep.length - 1) { + ctx.fillRect(moveStep[m].x * 32 + 10 - sx, moveStep[m].y * 32 + 10 - sy, 12, 12); + } + else { + ctx.beginPath(); + var cx = moveStep[m].x * 32 + 16 - sx, cy = moveStep[m].y * 32 + 16 - sy; + var currDir = moveStep[m].direction, nextDir = moveStep[m + 1].direction; + ctx.moveTo(cx - core.utils.scan[currDir].x * 11, cy - core.utils.scan[currDir].y * 11); + ctx.lineTo(cx, cy); + ctx.lineTo(cx + core.utils.scan[nextDir].x * 11, cy + core.utils.scan[nextDir].y * 11); + ctx.stroke(); + } + } +} + +control.prototype._setAutomaticRoute_setAutoSteps = function (moveStep) { + // 路线转autoStepRoutes + var step = 0, currStep = null; + moveStep.forEach(function (t) { + var dir = t.direction; + if (currStep == null || currStep == dir) + step++; + else { + core.status.automaticRoute.autoStepRoutes.push({ 'direction': currStep, 'step': step }); + step = 1; + } + currStep = dir; + }); + core.status.automaticRoute.autoStepRoutes.push({ 'direction': currStep, 'step': step }); +} + +////// 设置勇士的自动行走路线 ////// +control.prototype.setAutoHeroMove = function (steps) { + steps = steps || core.status.automaticRoute.autoStepRoutes; + if (steps.length == 0) return; + core.status.automaticRoute.autoStepRoutes = steps; + core.status.automaticRoute.autoHeroMove = true; + core.status.automaticRoute.autoStep = 1; + core.status.automaticRoute.destStep = steps[0].step; + core.moveHero(steps[0].direction); +} + +////// 设置行走的效果动画 ////// +control.prototype.setHeroMoveInterval = function (callback) { + if (core.status.heroMoving > 0) return; + if (core.status.replay.speed == 24) { + if (callback) callback(); + return; + } + + core.status.heroMoving = 1; + + var toAdd = 1; + if (core.status.replay.speed > 3) toAdd = 2; + if (core.status.replay.speed > 6) toAdd = 4; + if (core.status.replay.speed > 12) toAdd = 8; + + core.interval.heroMoveInterval = window.setInterval(function () { + core.status.heroMoving += toAdd; + if (core.status.heroMoving >= 8) { + clearInterval(core.interval.heroMoveInterval); + core.status.heroMoving = 0; + if (callback) callback(); + } + }, core.values.moveSpeed / 8 * toAdd / core.status.replay.speed); +} + +////// 每移动一格后执行的事件 ////// +control.prototype.moveOneStep = function (callback) { + return this.controldata.moveOneStep(callback); +} + +////// 实际每一步的行走过程 ////// +control.prototype.moveAction = function (callback) { + if (core.status.heroMoving > 0) return; + var noPass = core.noPass(core.nextX(), core.nextY()), canMove = core.canMoveHero(); + // 下一个点如果不能走 + if (noPass || !canMove) return this._moveAction_noPass(canMove, callback); + this._moveAction_moving(callback); +} + +control.prototype._moveAction_noPass = function (canMove, callback) { + core.status.route.push(core.getHeroLoc('direction')); + core.status.automaticRoute.moveStepBeforeStop = []; + core.status.automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (canMove) core.trigger(core.nextX(), core.nextY()); + core.drawHero(); + + if (core.status.automaticRoute.moveStepBeforeStop.length == 0) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + if (callback) callback(); +} + +control.prototype._moveAction_moving = function (callback) { + core.setHeroMoveInterval(function () { + core.setHeroLoc('x', core.nextX(), true); + core.setHeroLoc('y', core.nextY(), true); + + var direction = core.getHeroLoc('direction'); + core.control._moveAction_popAutomaticRoute(); + core.status.route.push(direction); + + core.moveOneStep(); + core.checkRouteFolding(); + if (callback) callback(); + }); +} + +control.prototype._moveAction_popAutomaticRoute = function () { + var automaticRoute = core.status.automaticRoute; + // 检查自动寻路是否被弹出 + if (automaticRoute.autoHeroMove) { + automaticRoute.movedStep++; + automaticRoute.lastDirection = core.getHeroLoc('direction'); + if (automaticRoute.destStep == automaticRoute.movedStep) { + if (automaticRoute.autoStep == automaticRoute.autoStepRoutes.length) { + core.clearContinueAutomaticRoute(); + core.stopAutomaticRoute(); + } + else { + automaticRoute.movedStep = 0; + automaticRoute.destStep = automaticRoute.autoStepRoutes[automaticRoute.autoStep].step; + core.setHeroLoc('direction', automaticRoute.autoStepRoutes[automaticRoute.autoStep].direction); + core.status.automaticRoute.autoStep++; + } + } + } +} + +////// 让勇士开始移动 ////// +control.prototype.moveHero = function (direction, callback) { + // 如果正在移动,直接return + if (core.status.heroMoving != 0) return; + if (core.isset(direction)) + core.setHeroLoc('direction', direction); + + if (callback) return this.moveAction(callback); + this._moveHero_moving(); +} + +control.prototype._moveHero_moving = function () { + // ------ 我已经看不懂这个函数了,反正好用就行23333333 + core.status.heroStop = false; + core.status.automaticRoute.moveDirectly = false; + var move = function () { + if (!core.status.heroStop) { + if (core.hasFlag('debug') && core.status.ctrlDown) { + if (core.status.heroMoving != 0) return; + // 检测是否穿出去 + var nx = core.nextX(), ny = core.nextY(); + if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height) return; + core.eventMoveHero([core.getHeroLoc('direction')], core.values.moveSpeed, move); + } + else { + core.moveAction(); + setTimeout(move, 50); + } + } + } + move(); +} + +////// 当前是否正在移动 ////// +control.prototype.isMoving = function () { + return !core.status.heroStop || core.status.heroMoving > 0; +} + +////// 停止勇士的一切行动,等待勇士行动结束后,再执行callback ////// +control.prototype.waitHeroToStop = function (callback) { + var lastDirection = core.status.automaticRoute.lastDirection; + core.stopAutomaticRoute(); + core.clearContinueAutomaticRoute(); + if (callback) { + core.status.replay.animate = true; + core.lockControl(); + core.status.automaticRoute.moveDirectly = false; + setTimeout(function () { + core.status.replay.animate = false; + if (core.isset(lastDirection)) + core.setHeroLoc('direction', lastDirection); + core.drawHero(); + callback(); + }, core.status.replay.speed == 24 ? 1 : 30); + } +} + +////// 转向 ////// +control.prototype.turnHero = function (direction) { + if (direction) { + core.setHeroLoc('direction', direction); + core.drawHero(); + core.status.route.push("turn:" + direction); + return; + } + core.setHeroLoc('direction', core.turnDirection(':right')); + core.drawHero(); + core.status.route.push("turn"); + core.checkRouteFolding(); +} + +////// 瞬间移动 ////// +control.prototype.moveDirectly = function (destX, destY, ignoreSteps) { + return this.controldata.moveDirectly(destX, destY, ignoreSteps); +} + +////// 尝试瞬间移动 ////// +control.prototype.tryMoveDirectly = function (destX, destY) { + if (this.nearHero(destX, destY)) return false; + var canMoveArray = core.maps.generateMovableArray(); + var dirs = [[destX, destY], [destX - 1, destY, "right"], [destX, destY - 1, "down"], [destX, destY + 1, "up"], [destX + 1, destY, "left"]]; + var canMoveDirectlyArray = core.canMoveDirectlyArray(dirs, canMoveArray); + + for (var i = 0; i < dirs.length; ++i) { + var d = dirs[i], dx = d[0], dy = d[1], dir = d[2]; + if (dx < 0 || dx >= core.bigmap.width || dy < 0 || dy >= core.bigmap.height) continue; + if (dir && !core.inArray(canMoveArray[dx][dy], dir)) continue; + if (canMoveDirectlyArray[i] < 0) continue; + if (core.control.moveDirectly(dx, dy, canMoveDirectlyArray[i])) { + if (dir) core.moveHero(dir, function () { }); + return true; + } + } + return false; +} + +////// 绘制勇士 ////// +control.prototype.drawHero = function (status, offset, frame) { + if (!core.isPlaying() || !core.status.floorId || core.status.gameOver) return; + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), direction = core.getHeroLoc('direction'); + status = status || 'stop'; + if (!offset) offset = 0; + + var way = core.utils.scan2[direction]; + var dx = way.x, dy = way.y; + var offsetX = typeof offset == 'number' ? dx * offset : (offset.x || 0); + var offsetY = typeof offset == 'number' ? dy * offset : (offset.y || 0); + offset = { x: offsetX, y: offsetY, offset: offset }; + + core.clearAutomaticRouteNode(x + dx, y + dy); + core.clearMap('hero'); + core.status.heroCenter.px = 32 * x + offsetX + 16; + core.status.heroCenter.py = 32 * y + offsetY + 32 - core.material.icons.hero.height / 2; + + // 重置hero层画布 + core.setGameCanvasTranslate('hero', 0, 0); + delete core.canvas.hero._px; + delete core.canvas.hero._py; + core.status.preview.enabled = false; + if (!core.hasFlag('__lockViewport__')) { + this._drawHero_updateViewport(x, y, offset); + } + + this._drawHero_draw(direction, x, y, status, offset, frame); +} + +control.prototype._drawHero_updateViewport = function (x, y, offset) { + core.bigmap.offsetX = core.clamp((x - core._HALF_WIDTH_) * 32 + offset.x, 0, Math.max(32 * core.bigmap.width - core._PX_, 0)); + core.bigmap.offsetY = core.clamp((y - core._HALF_HEIGHT_) * 32 + offset.y, 0, Math.max(32 * core.bigmap.height - core._PY_, 0)); + core.control.updateViewport(); +} + +control.prototype._drawHero_draw = function (direction, x, y, status, offset, frame) { + offset = offset || { x: 0, y: 0, offset: 0, px: 0, py: 0 }; + var opacity = core.setAlpha('hero', core.getFlag('__heroOpacity__', 1)) + this._drawHero_getDrawObjs(direction, x, y, status, offset).forEach(function (block) { + core.drawImage('hero', block.img, (block.heroIcon[block.status] + (frame || 0)) % 4 * block.width, + block.heroIcon.loc * block.height, block.width, block.height, + block.posx + (32 - block.width) / 2, block.posy + 32 - block.height, block.width, block.height); + }); + core.setAlpha('hero', opacity); +} + +control.prototype._drawHero_getDrawObjs = function (direction, x, y, status, offset) { + var heroIconArr = core.material.icons.hero, drawObjs = [], index = 0; + drawObjs.push({ + "img": core.material.images.hero, + "width": core.material.icons.hero.width || 32, + "height": core.material.icons.hero.height, + "heroIcon": heroIconArr[direction], + "posx": x * 32 - core.bigmap.offsetX + offset.x, + "posy": y * 32 - core.bigmap.offsetY + offset.y, + "status": status, + "index": index++, + }); + if (typeof offset.offset == 'number') { + core.status.hero.followers.forEach(function (t) { + drawObjs.push({ + "img": core.material.images.images[t.name], + "width": core.material.images.images[t.name].width / 4, + "height": core.material.images.images[t.name].height / 4, + "heroIcon": heroIconArr[t.direction], + "posx": 32 * t.x - core.bigmap.offsetX + (t.stop ? 0 : core.utils.scan2[t.direction].x * Math.abs(offset.offset)), + "posy": 32 * t.y - core.bigmap.offsetY + (t.stop ? 0 : core.utils.scan2[t.direction].y * Math.abs(offset.offset)), + "status": t.stop ? "stop" : status, + "index": index++ + }); + }); + } + return drawObjs.sort(function (a, b) { + return a.posy == b.posy ? b.index - a.index : a.posy - b.posy; + }); +} + +control.prototype.setHeroOpacity = function (opacity, moveMode, time, callback) { + time = time || 0; + if (time == 0) { + core.setFlag('__heroOpacity__', opacity); + core.drawHero(); + if (callback) callback(); + return; + } + time /= Math.max(core.status.replay.speed, 1) + + var fromOpacity = core.getFlag('__heroOpacity__', 1); + var step = 0, steps = parseInt(time / 10); + if (steps <= 0) steps = 1; + var moveFunc = core.applyEasing(moveMode); + + var animate = setInterval(function () { + step++; + core.setFlag('__heroOpacity__', fromOpacity + (opacity - fromOpacity) * moveFunc(step / steps)); + core.drawHero(); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, 10); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +// ------ 画布、位置、阻激夹域,显伤 ------ // + +////// 设置画布偏移 +control.prototype.setGameCanvasTranslate = function (canvas, x, y) { + var c = core.dom.gameCanvas[canvas]; + x = x * core.domStyle.scale; + y = y * core.domStyle.scale; + c.style.transform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.webkitTransform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.OTransform = 'translate(' + x + 'px,' + y + 'px)'; + c.style.MozTransform = 'translate(' + x + 'px,' + y + 'px)'; + if (main.mode === 'editor' && editor.isMobile) { + c.style.transform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.webkitTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.OTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + c.style.MozTransform = 'translate(' + (x / core._PX_ * 96) + 'vw,' + (y / core._PY_ * 96) + 'vw)'; + } +}; + +////// 加减画布偏移 +control.prototype.addGameCanvasTranslate = function (x, y) { + for (var ii = 0, canvas; canvas = core.dom.gameCanvas[ii]; ii++) { + var id = canvas.getAttribute('id'); + if (id == 'ui' || id == 'data') continue; // UI层和data层不移动 + var offsetX = x, offsetY = y; + if (core.bigmap.canvas.indexOf(id) >= 0) { + if (core.bigmap.v2) { + offsetX -= (core.bigmap.offsetX - 32 * core.bigmap.posX) + 32; + offsetY -= (core.bigmap.offsetY - 32 * core.bigmap.posY) + 32; + } else { + offsetX -= core.bigmap.offsetX; + offsetY -= core.bigmap.offsetY; + } + } + core.control.setGameCanvasTranslate(id, offsetX, offsetY); + } +} + +////// 更新视野范围 ////// +control.prototype.updateViewport = function () { + // 当前是否应该重绘? + if (core.bigmap.v2) { + if (core.bigmap.offsetX >= core.bigmap.posX * 32 + 32 + || core.bigmap.offsetX <= core.bigmap.posX * 32 - 32 + || core.bigmap.offsetY >= core.bigmap.posY * 32 + 32 + || core.bigmap.offsetY <= core.bigmap.posY * 32 - 32) { + core.bigmap.posX = parseInt(core.bigmap.offsetX / 32); + core.bigmap.posY = parseInt(core.bigmap.offsetY / 32); + core.redrawMap(); + } + } else { + core.bigmap.posX = core.bigmap.posY = 0; + } + var offsetX = core.bigmap.v2 ? -(core.bigmap.offsetX - 32 * core.bigmap.posX) - 32 : -core.bigmap.offsetX; + var offsetY = core.bigmap.v2 ? -(core.bigmap.offsetY - 32 * core.bigmap.posY) - 32 : -core.bigmap.offsetY; + + core.bigmap.canvas.forEach(function (cn) { + core.control.setGameCanvasTranslate(cn, offsetX, offsetY); + }); + // ------ 路线 + core.relocateCanvas('route', core.status.automaticRoute.offsetX - core.bigmap.offsetX, core.status.automaticRoute.offsetY - core.bigmap.offsetY); + // ------ 所有的大怪物也都需要重定位 + for (var one in core.dymCanvas) { + if (one.startsWith('_bigImage_')) { + var ox = core.dymCanvas[one].canvas.getAttribute('_ox'); + var oy = core.dymCanvas[one].canvas.getAttribute('_oy'); + if (ox != null && oy != null) { + core.relocateCanvas(one, parseInt(ox) - core.bigmap.offsetX, parseInt(oy) - core.bigmap.offsetY); + } + } + } + +} + +////// 设置视野范围 ////// +control.prototype.setViewport = function (px, py) { + var originOffsetX = core.bigmap.offsetX, originOffsetY = core.bigmap.offsetY; + core.bigmap.offsetX = core.clamp(px, 0, 32 * core.bigmap.width - core._PX_); + core.bigmap.offsetY = core.clamp(py, 0, 32 * core.bigmap.height - core._PY_); + this.updateViewport(); + // ------ hero层也需要! + var px = parseFloat(core.canvas.hero._px) || 0; + var py = parseFloat(core.canvas.hero._py) || 0; + px += originOffsetX - core.bigmap.offsetX; + py += originOffsetY - core.bigmap.offsetY; + core.control.setGameCanvasTranslate('hero', px, py); + core.canvas.hero._px = px; + core.canvas.hero._py = py; +} + +////// 移动视野范围 ////// +control.prototype.moveViewport = function (x, y, moveMode, time, callback) { + time = time || 0; + time /= Math.max(core.status.replay.speed, 1) + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) { + this.setViewport(32 * x, 32 * y); + if (callback) callback(); + return; + } + var px = core.clamp(32 * x, 0, 32 * core.bigmap.width - core._PX_); + var py = core.clamp(32 * y, 0, 32 * core.bigmap.height - core._PY_); + var cx = core.bigmap.offsetX; + var cy = core.bigmap.offsetY; + var moveFunc = core.applyEasing(moveMode); + + var animate = window.setInterval(function () { + step++; + core.setViewport(cx + moveFunc(step / steps) * (px - cx), cy + moveFunc(step / steps) * (py - cy)); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + core.setViewport(px, py); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 获得勇士面对位置的x坐标 ////// +control.prototype.nextX = function (n) { + if (n == null) n = 1; + return core.getHeroLoc('x') + core.utils.scan[core.getHeroLoc('direction')].x * n; +} + +////// 获得勇士面对位置的y坐标 ////// +control.prototype.nextY = function (n) { + if (n == null) n = 1; + return core.getHeroLoc('y') + core.utils.scan[core.getHeroLoc('direction')].y * n; +} + +////// 某个点是否在勇士旁边 ////// +control.prototype.nearHero = function (x, y, n) { + if (n == null) n = 1; + return Math.abs(x - core.getHeroLoc('x')) + Math.abs(y - core.getHeroLoc('y')) <= n; +} + +////// 聚集跟随者 ////// +control.prototype.gatherFollowers = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), dir = core.getHeroLoc('direction'); + core.status.hero.followers.forEach(function (t) { + t.x = x; + t.y = y; + t.stop = true; + t.direction = dir; + }); +} + +////// 更新跟随者坐标 ////// +control.prototype.updateFollowers = function () { + core.status.hero.followers.forEach(function (t) { + if (!t.stop) { + t.x += core.utils.scan2[t.direction].x; + t.y += core.utils.scan2[t.direction].y; + } + }) + + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + core.status.hero.followers.forEach(function (t) { + t.stop = true; + var dx = nowx - t.x, dy = nowy - t.y; + for (var dir in core.utils.scan2) { + if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) { + t.stop = false; + t.direction = dir; + } + } + nowx = t.x; nowy = t.y; + }) +} + +////// 瞬移更新跟随者坐标 ////// +control.prototype._moveDirectyFollowers = function (x, y) { + var route = core.automaticRoute(x, y); + if (route.length == 0) route = [{ x: x, y: y, direction: core.getHeroLoc('direction') }]; + + var nowx = x, nowy = y; + for (var i = 0; i < core.status.hero.followers.length; ++i) { + var t = core.status.hero.followers[i]; + var index = route.length - i - 2; + if (index < 0) index = 0; + t.stop = true; + t.x = route[index].x; + t.y = route[index].y; + t.direction = route[index].direction; + var dx = nowx - t.x, dy = nowy - t.y; + for (var dir in core.utils.scan2) { + if (core.utils.scan2[dir].x == dx && core.utils.scan2[dir].y == dy) { + t.stop = false; + t.direction = dir; + } + } + nowx = t.x; nowy = t.y; + } +} + +////// 更新领域、夹击、阻击的伤害地图 ////// +control.prototype.updateCheckBlock = function (floorId) { + return this.controldata.updateCheckBlock(floorId); +} + +////// 检查并执行领域、夹击、阻击事件 ////// +control.prototype.checkBlock = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'), loc = x + "," + y; + var damage = core.status.checkBlock.damage[loc]; + if (damage) { + core.status.hero.hp -= damage; + var text = (Object.keys(core.status.checkBlock.type[loc] || {}).join(",")) || "伤害"; + core.drawTip("受到" + text + damage + "点"); + core.drawHeroAnimate("zone"); + this._checkBlock_disableQuickShop(); + core.status.hero.statistics.extraDamage += damage; + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(false, true); + core.events.lose(); + return; + } else { + core.updateStatusBar(false, true); + } + } + this._checkBlock_ambush(core.status.checkBlock.ambush[loc]); + this._checkBlock_repulse(core.status.checkBlock.repulse[loc]); +} + +control.prototype._checkBlock_disableQuickShop = function () { + // 禁用快捷商店 + if (core.flags.disableShopOnDamage) { + Object.keys(core.status.shops).forEach(function (shopId) { + core.setShopVisited(shopId, false); + }); + } +} + +////// 阻击 ////// +control.prototype._checkBlock_repulse = function (repulse) { + if (!repulse || repulse.length == 0) return; + var actions = []; + repulse.forEach(function (t) { + actions.push({ "type": "move", "loc": [t[0], t[1]], "steps": [t[3]], "time": 250, "keep": true, "async": true }); + }); + actions.push({ "type": "waitAsync" }); + core.insertAction(actions); +} + +////// 捕捉 ////// +control.prototype._checkBlock_ambush = function (ambush) { + if (!ambush || ambush.length == 0) return; + // 捕捉效果 + var actions = []; + ambush.forEach(function (t) { + actions.push({ "type": "move", "loc": [t[0], t[1]], "steps": [t[3]], "time": 250, "keep": false, "async": true }); + }); + actions.push({ "type": "waitAsync" }); + // 强制战斗 + ambush.forEach(function (t) { + actions.push({ + "type": "function", "function": "function() { " + + "core.battle('" + t[2] + "', " + t[0] + "," + t[1] + ", true, core.doAction); " + + "}", "async": true + }); + }); + core.insertAction(actions); +} + +////// 更新全地图显伤 ////// +control.prototype.updateDamage = function (floorId, ctx) { + floorId = floorId || core.status.floorId; + if (!floorId || core.status.gameOver || main.mode != 'play') return; + var onMap = ctx == null; + + // 没有怪物手册 + if (!core.hasItem('book')) return; + core.status.damage.posX = core.bigmap.posX; + core.status.damage.posY = core.bigmap.posY; + if (!onMap) { + var width = core.floors[floorId].width, height = core.floors[floorId].height; + // 地图过大的缩略图不绘制显伤 + if (width * height > core.bigmap.threshold) return; + } + this._updateDamage_damage(floorId, onMap); + this._updateDamage_extraDamage(floorId, onMap); + this.drawDamage(ctx); +} + +control.prototype._updateDamage_damage = function (floorId, onMap) { + core.status.damage.data = []; + if (!core.flags.displayEnemyDamage && !core.flags.displayExtraDamage) return; + + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + var x = block.x, y = block.y; + + // v2优化,只绘制范围内的部分 + if (onMap && core.bigmap.v2) { + if (x < core.bigmap.posX - core.bigmap.extend || x > core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + || y < core.bigmap.posY - core.bigmap.extend || y > core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend) { + return; + } + } + + if (!block.disable && block.event.cls.indexOf('enemy') == 0 && block.event.displayDamage !== false) { + if (core.flags.displayEnemyDamage) { + var damageString = core.enemys.getDamageString(block.event.id, x, y, floorId); + core.status.damage.data.push({ text: damageString.damage, px: 32 * x + 1, py: 32 * (y + 1) - 1, color: damageString.color }); + } + if (core.flags.displayCritical) { + var critical = core.enemys.nextCriticals(block.event.id, 1, x, y, floorId); + critical = core.formatBigNumber((critical[0] || [])[0], true); + if (critical == '???') critical = '?'; + core.status.damage.data.push({ text: critical, px: 32 * x + 1, py: 32 * (y + 1) - 11, color: '#FFFFFF' }); + } + } + }); +} + +control.prototype._updateDamage_extraDamage = function (floorId, onMap) { + core.status.damage.extraData = []; + if (!core.flags.displayExtraDamage) return; + + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var startX = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0; + var endX = onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + 1) : width; + var startY = onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0; + var endY = onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend + 1) : height; + + for (var x = startX; x < endX; x++) { + for (var y = startY; y < endY; y++) { + var alpha = 1; + if (core.noPass(x, y, floorId)) { + if (core.flags.extraDamageType == 2) alpha = 0; + else if (core.flags.extraDamageType == 1) alpha = 0.6; + } + var damage = core.status.checkBlock.damage[x + "," + y] || 0; + if (damage > 0) { // 该点伤害 + damage = core.formatBigNumber(damage, true); + core.status.damage.extraData.push({ text: damage, px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); + } + else { // 检查捕捉 + if (core.status.checkBlock.ambush[x + "," + y]) { + core.status.damage.extraData.push({ text: '!', px: 32 * x + 16, py: 32 * (y + 1) - 14, color: '#ffaa33', alpha: alpha }); + } + } + } + } +} + +////// 重绘地图显伤 ////// +control.prototype.drawDamage = function (ctx) { + if (core.status.gameOver || !core.status.damage || main.mode != 'play') return; + var onMap = false; + if (ctx == null) { + ctx = core.canvas.damage; + core.clearMap('damage'); + onMap = true; + } + + if (onMap && core.bigmap.v2) { + // 检查是否需要重算... + if (Math.abs(core.bigmap.posX - core.status.damage.posX) >= core.bigmap.extend - 1 + || Math.abs(core.bigmap.posY - core.status.damage.posY) >= core.bigmap.extend - 1) { + return this.updateDamage(); + } + } + return this._drawDamage_draw(ctx, onMap); +} + +control.prototype._drawDamage_draw = function (ctx, onMap) { + if (!core.hasItem('book')) return; + + core.setFont(ctx, "bold 11px Arial"); + core.setTextAlign(ctx, 'left'); + core.status.damage.data.forEach(function (one) { + var px = one.px, py = one.py; + if (onMap && core.bigmap.v2) { + px -= core.bigmap.posX * 32; + py -= core.bigmap.posY * 32; + if (px < -32 * 2 || px > core._PX_ + 32 || py < -32 || py > core._PY_ + 32) + return; + } + core.fillBoldText(ctx, one.text, px, py, one.color); + }); + + core.setTextAlign(ctx, 'center'); + core.status.damage.extraData.forEach(function (one) { + var px = one.px, py = one.py; + if (onMap && core.bigmap.v2) { + px -= core.bigmap.posX * 32; + py -= core.bigmap.posY * 32; + if (px < -32 || px > core._PX_ + 32 || py < -32 || py > core._PY_ + 32) + return; + } + var alpha = core.setAlpha(ctx, one.alpha); + core.fillBoldText(ctx, one.text, px, py, one.color); + core.setAlpha(ctx, alpha); + }); +} + +// ------ 录像相关 ------ // + +////// 选择录像文件 ////// +control.prototype.chooseReplayFile = function () { + core.readFile(function (obj) { + if (obj.name != core.firstData.name) return alert("存档和游戏不一致!"); + if (!obj.route) return alert("无效的录像!"); + var _replay = function () { + core.startGame(core.flags.startUsingCanvas ? '' : obj.hard || '', obj.seed, core.decodeRoute(obj.route)); + } + if (obj.version && obj.version != core.firstData.version) { + core.myconfirm("游戏版本不一致!\n你仍然想播放录像吗?", _replay); + return; + } + _replay(); + }, null, ".h5route"); +} + +////// 开始播放 ////// +control.prototype.startReplay = function (list) { + if (!core.isPlaying()) return; + core.status.replay.replaying = true; + core.status.replay.pausing = true; + core.status.replay.failed = false; + core.status.replay.speed = 1.0; + core.status.replay.toReplay = core.cloneArray(list); + core.status.replay.totalList = core.status.route.concat(list); + core.status.replay.steps = 0; + core.status.replay.save = []; + core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); + core.setOpacity('replay', 0.6); + this._replay_drawProgress(); + core.updateStatusBar(false, true); + core.drawTip("开始播放"); + this.replay(); +} + +////// 更改播放状态 ////// +control.prototype.triggerReplay = function () { + if (core.status.replay.pausing) this.resumeReplay(); + else this.pauseReplay(); +} + +////// 暂停播放 ////// +control.prototype.pauseReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + core.status.replay.pausing = true; + core.updateStatusBar(false, true); + core.drawTip("暂停播放"); +} + +////// 恢复播放 ////// +control.prototype.resumeReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + core.status.replay.pausing = false; + core.updateStatusBar(false, true); + core.drawTip("恢复播放"); + core.replay(); +} + +////// 单步播放 ////// +control.prototype.stepReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + core.replay(true); +} + +////// 加速播放 ////// +control.prototype.speedUpReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = speeds.length - 2; i >= 0; i--) { + if (speeds[i] <= core.status.replay.speed) { + core.status.replay.speed = speeds[i + 1]; + break; + } + } + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 减速播放 ////// +control.prototype.speedDownReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + var speeds = [0.2, 0.5, 1, 2, 3, 6, 12, 24]; + for (var i = 1; i <= speeds.length; i++) { + if (speeds[i] >= core.status.replay.speed) { + core.status.replay.speed = speeds[i - 1]; + break; + } + } + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 设置播放速度 ////// +control.prototype.setReplaySpeed = function (speed) { + if (!core.isPlaying() || !core.isReplaying()) return; + core.status.replay.speed = speed; + core.drawTip("x" + core.status.replay.speed + "倍"); +} + +////// 停止播放 ////// +control.prototype.stopReplay = function (force) { + if (!core.isPlaying()) return; + if (!core.isReplaying() && !force) return; + core.status.replay.toReplay = []; + core.status.replay.totalList = []; + core.status.replay.replaying = false; + core.status.replay.pausing = false; + core.status.replay.failed = false; + core.status.replay.speed = 1.0; + core.status.replay.steps = 0; + core.status.replay.save = []; + core.deleteCanvas('replay'); + core.updateStatusBar(false, true); + core.drawTip("停止播放并恢复游戏"); +} + +////// 回退 ////// +control.prototype.rewindReplay = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (core.status.replay.save.length == 0) { + core.playSound('操作失败'); + return core.drawTip("无法再回到上一个节点"); + } + var save = core.status.replay.save, data = save.pop(); + core.loadData(data.data, function () { + core.removeFlag('__fromLoad__'); + core.status.replay = { + "replaying": true, + "pausing": true, + "animate": false, + "toReplay": data.replay.toReplay, + "totalList": data.replay.totalList, + "speed": core.status.replay.speed, + "steps": data.replay.steps, + "save": save + } + core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199); + core.setOpacity('replay', 0.6); + core.control._replay_drawProgress(); + core.updateStatusBar(false, true); + core.drawTip("成功回退到上一个节点"); + }); +} + +////// 回放时存档 ////// +control.prototype._replay_SL = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (core.hasFlag('__forbidSave__')) { + core.playSound('操作失败'); + return core.drawTip('当前禁止存档'); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'save'; + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + + core.ui._drawSLPanel(10 * page + offset); +} + +////// 回放时查看怪物手册 ////// +control.prototype._replay_book = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || (core.status.event.id && core.status.event.id != 'viewMaps')) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + if (!core.hasItem('book')) { + core.playSound('操作失败'); + return core.drawTip('你没有' + core.material.items['book'].name, 'book'); + } + this._replay_hideProgress(); + + // 从“浏览地图”页面打开 + if (core.status.event.id == 'viewMaps') + core.status.event.ui = core.status.event.data; + + core.lockControl(); + core.status.event.id = 'book'; + core.useItem('book', true); +} + +////// 回放录像时浏览地图 ////// +control.prototype._replay_viewMap = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'viewMaps'; + core.ui._drawViewMaps(); +} + +control.prototype._replay_toolbox = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'toolbox'; + core.ui._drawToolbox(); +} + +control.prototype._replay_equipbox = function () { + if (!core.isPlaying() || !core.isReplaying()) return; + if (!core.status.replay.pausing) { + core.playSound('操作失败'); + return core.drawTip("请先暂停录像"); + } + if (core.isMoving() || core.status.replay.animate || core.status.event.id) { + core.playSound('操作失败'); + return core.drawTip("请等待当前事件的处理结束"); + } + this._replay_hideProgress(); + + core.lockControl(); + core.status.event.id = 'equipbox'; + core.ui._drawEquipbox(); +} + +////// 是否正在播放录像 ////// +control.prototype.isReplaying = function () { + return (core.status.replay || {}).replaying; +} + +////// 回放 ////// +control.prototype.replay = function (force) { + if (!core.isPlaying() || !core.isReplaying() + || core.status.replay.animate || core.status.event.id || core.status.replay.failed) return; + if (core.status.replay.pausing && !force) return; + this._replay_drawProgress(); + if (core.status.replay.toReplay.length == 0) + return this._replay_finished(); + this._replay_save(); + var action = core.status.replay.toReplay.shift(); + if (this._doReplayAction(action)) return; + this._replay_error(action); +} + +////// 注册一个录像行为 ////// +// name:自定义名称,可用于注销使用 +// func:具体执行录像的函数,可为一个函数或插件中的函数名; +// 需要接受一个action参数,代表录像回放时的下一个操作 +// func返回true代表成功处理了此录像行为,false代表没有处理此录像行为。 +control.prototype.registerReplayAction = function (name, func) { + this.unregisterReplayAction(name); + this.replayActions.push({ name: name, func: func }); +} + +////// 注销一个录像行为 ////// +control.prototype.unregisterReplayAction = function (name) { + this.replayActions = this.replayActions.filter(function (b) { return b.name != name; }); +} + +////// 执行录像行为,会在注册的函数中依次执行直到得到true为止 ////// +control.prototype._doReplayAction = function (action) { + for (var i in this.replayActions) { + try { + if (core.doFunc(this.replayActions[i].func, this, action)) return true; + } catch (e) { + console.error(e); + console.error("ERROR in replayActions[" + this.replayActions[i].name + "]:已自动注销该项。"); + core.unregisterReplayAction(this.replayActions[i].name); + } + } + return false; +} + +control.prototype._replay_finished = function () { + core.status.replay.replaying = false; + core.status.replay.failed = false; + core.status.event.selection = 0; + var str = "录像播放完毕,你想退出播放吗?"; + if (core.status.route.length != core.status.replay.totalList.length + || core.subarray(core.status.route, core.status.replay.totalList) == null) { + str = "录像播放完毕,但记录不一致。\n请检查录像播放时的二次记录问题。\n你想退出播放吗?"; + } + core.ui.drawConfirmBox(str, function () { + core.ui.closePanel(); + core.stopReplay(true); + }, function () { + core.status.replay.replaying = true; + core.ui.closePanel(); + core.pauseReplay(); + }); +} + +control.prototype._replay_save = function () { + core.status.replay.steps++; + if (core.status.replay.steps % 40 == 1) { + if (core.status.replay.save.length == 30) + core.status.replay.save.shift(); + core.status.replay.save.push({ + "data": core.saveData(), "replay": { + "totalList": core.cloneArray(core.status.replay.totalList), + "toReplay": core.cloneArray(core.status.replay.toReplay), + "steps": core.status.replay.steps + } + }); + } +} + +control.prototype._replay_error = function (action, callback) { + core.ui.closePanel(); + core.status.replay.replaying = false; + core.status.replay.failed = true; + var len = core.status.replay.toReplay.length; + var prevList = core.status.replay.totalList.slice(-len - 11, -len - 1); + var nextList = core.status.replay.toReplay.slice(0, 10); + console.log("录像文件出错,当前操作:" + action); + console.log("之前的10个操作是:\n" + prevList.toString()); + console.log("接下来10个操作是:\n" + nextList.toString()); + core.ui.drawConfirmBox("录像文件出错,你想回到上个节点吗?", function () { + core.status.replay.failed = false; + core.ui.closePanel(); + if (core.status.replay.save.length > 0) { + core.status.replay.replaying = true; + core.status.replay.pausing = true; + core.rewindReplay(); + } + else { + core.playSound('操作失败'); + core.stopReplay(true); + core.drawTip("无法回到上一个节点"); + if (callback) callback(); + } + }, function () { + core.status.replay.failed = false; + core.ui.closePanel(); + core.stopReplay(true); + if (callback) callback(); + }); +} + +control.prototype._replay_hideProgress = function () { + if (core.dymCanvas.replay) core.dymCanvas.replay.canvas.style.display = 'none'; +} + +control.prototype._replay_drawProgress = function () { + if (!core.dymCanvas.replay) return; + if (core.dymCanvas.replay.canvas.style.display == 'none') core.dymCanvas.replay.canvas.style.display = 'block'; + var total = core.status.replay.totalList.length, left = total - core.status.replay.toReplay.length; + var content = '播放进度:' + left + ' / ' + total + '(' + (left / total * 100).toFixed(2) + '%)'; + var width = 26 + core.calWidth('replay', content, "16px Arial"); + core.clearMap('replay'); + core.fillRect('replay', 0, 0, width, 40, '#000000'); + core.fillText('replay', content, 16, 27, '#FFFFFF'); +} + +control.prototype.__replay_getTimeout = function () { + if (core.status.replay.speed == 24) return 0; + return 750 / Math.max(1, core.status.replay.speed); +} + +control.prototype._replayAction_move = function (action) { + if (["up", "down", "left", "right"].indexOf(action) < 0) return false; + core.moveHero(action, core.replay); + return true; +} + +control.prototype._replayAction_item = function (action) { + if (action.indexOf("item:") != 0) return false; + var itemId = action.substring(5); + if (!core.canUseItem(itemId)) return false; + if (core.material.items[itemId].hideInReplay || core.status.replay.speed == 24) { + core.useItem(itemId, false, core.replay); + return true; + } + var tools = core.getToolboxItems('tools'), + constants = core.getToolboxItems('constants'); + var index, per = core._WIDTH_ - 1; + if ((index = tools.indexOf(itemId)) >= 0) { + core.status.event.data = { "toolsPage": Math.floor(index / per) + 1, "constantsPage": 1 }; + index = index % per; + } + else if ((index = constants.indexOf(itemId)) >= 0) { + core.status.event.data = { "toolsPage": 1, "constantsPage": Math.floor(index / per) + 1 }; + index = index % per + per; + } + if (index < 0) return false; + core.ui._drawToolbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.useItem(itemId, false, core.replay); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_equip = function (action) { + if (action.indexOf("equip:") != 0) return false; + var equipId = action.substring(6); + var ownEquipment = core.getToolboxItems('equips'); + var index = ownEquipment.indexOf(equipId), per = core._WIDTH_ - 1; + if (index < 0) { + core.removeFlag('__doNotCheckAutoEvents__'); + return false; + } + + var cb = function () { + var next = core.status.replay.toReplay[0] || ""; + if (!next.startsWith('equip:') && !next.startsWith('unEquip:')) { + core.removeFlag('__doNotCheckAutoEvents__'); + core.checkAutoEvents(); + } + core.replay(); + } + core.setFlag('__doNotCheckAutoEvents__', true); + + core.status.route.push(action); + if (core.material.items[equipId].hideInReplay || core.status.replay.speed == 24) { + core.loadEquip(equipId, cb); + return true; + } + core.status.event.data = { "page": Math.floor(index / per) + 1, "selectId": null }; + index = index % per + per; + core.ui._drawEquipbox(index); + setTimeout(function () { + core.ui.closePanel(); + core.loadEquip(equipId, cb); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_unEquip = function (action) { + if (action.indexOf("unEquip:") != 0) return false; + var equipType = parseInt(action.substring(8)); + if (!core.isset(equipType)) { + core.removeFlag('__doNotCheckAutoEvents__'); + return false; + } + + var cb = function () { + var next = core.status.replay.toReplay[0] || ""; + if (!next.startsWith('equip:') && !next.startsWith('unEquip:')) { + core.removeFlag('__doNotCheckAutoEvents__'); + core.checkAutoEvents(); + } + core.replay(); + } + core.setFlag('__doNotCheckAutoEvents__', true); + + core.ui._drawEquipbox(equipType); + core.status.route.push(action); + if (core.status.replay.speed == 24) { + core.unloadEquip(equipType, cb); + return true; + } + setTimeout(function () { + core.ui.closePanel(); + core.unloadEquip(equipType, cb); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_saveEquip = function (action) { + if (action.indexOf('saveEquip:') != 0) return false; + core.quickSaveEquip(parseInt(action.substring(10))); + core.replay(); + return true; +} + +control.prototype._replayAction_loadEquip = function (action) { + if (action.indexOf('loadEquip:') != 0) return false; + core.quickLoadEquip(parseInt(action.substring(10))); + core.replay(); + return true; +} + +control.prototype._replayAction_fly = function (action) { + if (action.indexOf("fly:") != 0) return false; + var floorId = action.substring(4); + var toIndex = core.floorIds.indexOf(floorId); + if (!core.canUseItem('fly') || (core.flags.flyNearStair && !core.nearStair())) return false; + core.ui.drawFly(toIndex); + if (core.status.replay.speed == 24) { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + return true; + } + setTimeout(function () { + if (!core.flyTo(floorId, core.replay)) + core.control._replay_error(action); + }, core.control.__replay_getTimeout()); + return true; +} + +control.prototype._replayAction_shop = function (action) { + if (action.indexOf("shop:") != 0) return false; + var shopId = action.substring(5); + if (core.canUseQuickShop(shopId) != null || !core.canOpenShop(shopId)) { + this._replay_error(shopId); + return true; + } + core.openShop(shopId, false); + core.replay(); + return true; +} + +control.prototype._replayAction_turn = function (action) { + if (action != 'turn' && action.indexOf('turn:') != 0) return false; + if (action == 'turn') core.turnHero(); + else core.turnHero(action.substring(5)); + core.replay(); + return true; +} + +control.prototype._replayAction_getNext = function (action) { + if (action != "getNext") return false; + core.getNextItem(); + core.replay(); + return true; +} + +control.prototype._replayAction_moveDirectly = function (action) { + if (action.indexOf("move:") != 0) return false; + // 忽略连续的瞬移事件;如果大地图某一边超过计算范围则不合并 + if (!core.hasFlag('poison') && core.status.thisMap.width < 2 * core.bigmap.extend + core._WIDTH_ + && core.status.thisMap.height < 2 * core.bigmap.extend + core._HEIGHT_) { + while (core.status.replay.toReplay.length > 0 && + core.status.replay.toReplay[0].indexOf('move:') == 0) { + core.status.route.push(action); + action = core.status.replay.toReplay.shift(); + } + } + + var pos = action.substring(5).split(":"); + var x = parseInt(pos[0]), y = parseInt(pos[1]); + var nowx = core.getHeroLoc('x'), nowy = core.getHeroLoc('y'); + var ignoreSteps = core.canMoveDirectly(x, y); + if (!core.moveDirectly(x, y, ignoreSteps)) return false; + if (core.status.replay.speed == 24) { + core.replay(); + return true; + } + + core.ui.drawArrow('ui', 32 * nowx + 16 - core.bigmap.offsetX, 32 * nowy + 16 - core.bigmap.offsetY, + 32 * x + 16 - core.bigmap.offsetX, 32 * y + 16 - core.bigmap.offsetY, '#FF0000', 3); + var timeout = this.__replay_getTimeout(); + if (ignoreSteps < 10) timeout = timeout * ignoreSteps / 10; + setTimeout(function () { + core.clearMap('ui'); + core.replay(); + }, timeout); + return true; +} + +control.prototype._replayAction_key = function (action) { + if (action.indexOf("key:") != 0) return false; + core.actions.keyUp(parseInt(action.substring(4)), false, true); + core.replay(); + return true; +} + +control.prototype._replayAction_click = function (action) { + if (action.indexOf("click:") != 0) return false; + var p = action.split(":"); + if (p.length != 4) return false; + core.actions.doRegisteredAction("onStatusBarClick", parseInt(p[2]), parseInt(p[3]), parseInt(p[1])); + core.replay(); + return true; +} + +control.prototype._replayAction_ignoreInput = function (action) { + if (action.indexOf('input:') == 0 || action.indexOf('input2:') == 0 || action.indexOf('choices:') == 0 || action.indexOf('random:') == 0) { + console.warn('警告!录像播放中出现了未知的 ' + action + '!'); + core.replay(); + return true; + } + return false; +} + +control.prototype._replayAction_no = function (action) { + if (action != 'no') return false; + core.status.route.push(action); + core.replay(); + return true; +} + +// ------ 存读档相关 ------ // + +////// 自动存档 ////// +control.prototype.autosave = function (removeLast) { + if (core.hasFlag('__forbidSave__')) return; + var x = null; + if (removeLast) { + x = core.status.route.pop(); + core.status.route.push("turn:" + core.getHeroLoc('direction')); + } + if (core.status.event.id == 'action' && !removeLast) // 事件中自动存档,读档后是否回到事件触发前 + core.setFlag("__events__", core.clone(core.status.event.data)); + if (core.saves.autosave.data == null) { + core.saves.autosave.data = []; + } + core.saves.autosave.data.splice(core.saves.autosave.now, 0, core.saveData()); + core.saves.autosave.now += 1; + if (core.saves.autosave.data.length > core.saves.autosave.max) { + if (core.saves.autosave.now < core.saves.autosave.max / 2) + core.saves.autosave.data.pop(); + else { + core.saves.autosave.data.shift(); + core.saves.autosave.now = core.saves.autosave.now - 1; + } + } + core.saves.autosave.updated = true; + core.saves.ids[0] = true; + core.removeFlag("__events__"); + if (removeLast) { + core.status.route.pop(); + if (x) core.status.route.push(x); + } +} + +/////// 实际进行自动存档 ////// +control.prototype.checkAutosave = function () { + if (!core.animateFrame || !core.saves || !core.saves.autosave) return; + core.setLocalStorage('totalTime', core.animateFrame.totalTime); + var autosave = core.saves.autosave; + if (autosave.data == null || !autosave.updated || !autosave.storage) return; + autosave.updated = false; + if (autosave.data.length >= 1) { + core.setLocalForage("autoSave", autosave.data[autosave.now - 1]); + } +} + +////// 实际进行存读档事件 ////// +control.prototype.doSL = function (id, type) { + switch (type) { + case 'save': this._doSL_save(id); break; + case 'load': this._doSL_load(id, this._doSL_load_afterGet); break; + case 'reload': this._doSL_reload(id, this._doSL_load_afterGet); break; + case 'replayLoad': this._doSL_load(id, this._doSL_replayLoad_afterGet); break; + case 'replayRemain': this._doSL_load(id, this._doSL_replayRemain_afterGet); break; + case 'replaySince': this._doSL_load(id, this._doSL_replaySince_afterGet); break; + } +} + +control.prototype._doSL_save = function (id) { + if (id == 'autoSave') { + core.playSound('操作失败'); + return core.drawTip('不能覆盖自动存档!'); + } + // 在事件中的存档 + if (core.status.event.interval != null) + core.setFlag("__events__", core.status.event.interval); + var data = core.saveData(); + if (core.isReplaying() && core.status.replay.toReplay.length > 0) { + data.__toReplay__ = core.encodeRoute(core.status.replay.toReplay); + } + core.setLocalForage("save" + id, data, function () { + core.saves.saveIndex = id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); + // 恢复事件 + if (!core.events.recoverEvents(core.status.event.interval)) + core.ui.closePanel(); + core.playSound('存档'); + core.drawTip('存档成功!'); + }, function (err) { + console.error(err); + alert("存档失败,错误信息:\n" + err); + }); + core.removeFlag("__events__"); + return; +} + +control.prototype._doSL_load = function (id, callback) { + if (id == 'autoSave' && core.saves.autosave.data != null) { + core.saves.autosave.now -= 1; + var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0]; + if (core.isPlaying() && !core.status.gameOver) { + core.control.autosave(0); + core.saves.autosave.now -= 1; + } + if (core.saves.autosave.now == 0) { + core.saves.autosave.data.unshift(core.clone(data)); + core.saves.autosave.now += 1; + } + callback(id, data); + } + else { + core.getLocalForage(id == 'autoSave' ? id : "save" + id, null, function (data) { + if (id == 'autoSave' && data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + core.saves.autosave.now = core.saves.autosave.data.length; + return core.control._doSL_load(id, callback); + } + callback(id, data); + }, function (err) { + console.error(err); + alert("无效的存档"); + }) + } + return; +} + +control.prototype._doSL_reload = function (id, callback) { + if (core.saves.autosave.data != null && core.saves.autosave.now < core.saves.autosave.data.length) { + var data = core.saves.autosave.data.splice(core.saves.autosave.now, 1)[0]; + core.control.autosave(false); + callback(id, data); + } + return; +} + +control.prototype._doSL_load_afterGet = function (id, data) { + if (!data) return alert("无效的存档"); + var _replay = function () { + core.startGame(data.hard, data.hero.flags.__seed__, core.decodeRoute(data.route)); + }; + if (data.version != core.firstData.version) { + core.myconfirm("存档版本不匹配!\n你想回放此存档的录像吗?\n可以随时停止录像播放以继续游戏。", _replay); + return; + } + if (data.hero.flags.__events__ && data.guid != core.getGuid()) { + core.myconfirm("此存档可能存在风险,你想要播放录像么?", _replay); + return; + } + core.ui.closePanel(); + core.loadData(data, function () { + core.removeFlag('__fromLoad__'); + core.drawTip("读档成功"); + if (id != "autoSave") { + core.saves.saveIndex = id; + core.setLocalStorage('saveIndex', core.saves.saveIndex); + } + }); +} + +control.prototype._doSL_replayLoad_afterGet = function (id, data) { + if (!data) { + core.playSound('操作失败'); + return core.drawTip("无效的存档"); + } + if (data.version != core.firstData.version) { + core.playSound('操作失败'); + return core.drawTip("存档版本不匹配"); + } + if (data.hero.flags.__events__ && data.guid != core.getGuid()) { + core.playSound('操作失败'); + return core.drawTip("此存档可能存在风险,无法读档"); + } + var route = core.subarray(core.status.route, core.decodeRoute(data.route)); + if (route == null) { + core.playSound('操作失败'); + return core.drawTip("无法从此存档回放录像"); + } + core.loadData(data, function () { + core.removeFlag('__fromLoad__'); + core.startReplay(route); + core.drawTip("回退到存档节点"); + }); +} + +control.prototype._doSL_replayRemain_afterGet = function (id, data) { + if (!data) { + core.playSound('操作失败'); + return core.drawTip("无效的存档"); + } + var route = core.decodeRoute(data.route); + if (core.status.tempRoute) { + var remainRoute = core.subarray(route, core.status.tempRoute); + if (remainRoute == null) + return alert("无法接续播放录像!\n该存档必须是前一个选择的存档的后续内容。"); + delete core.status.tempRoute; + core.ui.closePanel(); + core.startReplay(remainRoute); + core.drawTip("接续播放录像"); + return; + } + else if (data.floorId != core.status.floorId || data.hero.loc.x != core.getHeroLoc('x') || data.hero.loc.y != core.getHeroLoc('y')) + return alert("楼层或坐标不一致!"); + + core.status.tempRoute = route; + core.ui.closePanel(); + core.drawText("\t[步骤2]请选择第二个存档。\n\r[yellow]该存档必须是前一个存档的后续。\r\n将尝试播放到此存档。", function () { + core.status.event.id = 'replayRemain'; + core.lockControl(); + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.ui._drawSLPanel(10 * page + offset); + }); +} + +control.prototype._doSL_replaySince_afterGet = function (id, data) { + if (data.floorId != core.status.floorId || data.hero.loc.x != core.getHeroLoc('x') || data.hero.loc.y != core.getHeroLoc('y')) + return alert("楼层或坐标不一致!"); + if (!data.__toReplay__) return alert('该存档没有剩余录像!'); + core.ui.closePanel(); + core.startReplay(core.decodeRoute(data.__toReplay__)); + core.drawTip("播放存档剩余录像"); + return; +} + +////// 同步存档到服务器 ////// +control.prototype.syncSave = function (type) { + core.ui.drawWaiting("正在同步,请稍候..."); + var callback = function (saves) { + core.control._syncSave_http(type, saves); + } + if (type == 'all') core.getAllSaves(callback); + else core.getSave(core.saves.saveIndex, callback); +} + +control.prototype._syncSave_http = function (type, saves) { + if (!saves) return core.drawText("没有要同步的存档"); + var formData = new FormData(); + formData.append('type', 'save'); + formData.append('name', core.firstData.name); + formData.append('data', LZString.compressToBase64(JSON.stringify(saves))); + formData.append('shorten', '1'); + + core.http("POST", "/games/sync.php", formData, function (data) { + var response = JSON.parse(data); + if (response.code < 0) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:" + response.msg); + } + else { + core.drawText((type == 'all' ? "所有存档" : "存档" + core.saves.saveIndex) + "同步成功!\n\n您的存档编号+密码: \r[yellow]" + + response.code + response.msg + + "\r\n\n请牢记以上信息(如截图等),在从服务器\n同步存档时使用。\n\r[yellow]另外请注意,存档同步只会保存一个月的时间。\r") + } + }, function (e) { + core.drawText("出错啦!\n无法同步存档到服务器。\n错误原因:" + e); + }) +} + +////// 从服务器加载存档 ////// +control.prototype.syncLoad = function () { + core.myprompt("请输入存档编号+密码", null, function (idpassword) { + if (!idpassword) return core.ui._drawSyncSave(); + if (!/^\d{6}\w{4}$/.test(idpassword) && !/^\d{4}\w{3}$/.test(idpassword)) { + core.drawText("不合法的存档编号+密码!"); + return; + } + core.ui.drawWaiting("正在同步,请稍候..."); + if (idpassword.length == 7) { + core.control._syncLoad_http(idpassword.substring(0, 4), idpassword.substring(4)); + } else { + core.control._syncLoad_http(idpassword.substring(0, 6), idpassword.substring(6)); + } + }); +} + +control.prototype._syncLoad_http = function (id, password) { + var formData = new FormData(); + formData.append('type', 'load'); + formData.append('name', core.firstData.name); + formData.append('id', id); + formData.append('password', password); + + core.http("POST", "/games/sync.php", formData, function (data) { + var response = JSON.parse(data); + if (response.code == 0) { + var msg = null; + try { + msg = JSON.parse(LZString.decompressFromBase64(response.msg)); + } catch (e) { } + if (!msg) { + try { + msg = JSON.parse(response.msg); + } catch (e) { } + } + if (msg) { + core.control._syncLoad_write(msg); + } else { + core.drawText("出错啦!\n存档解析失败!"); + } + } + else { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:" + response.msg); + } + }, function (e) { + core.drawText("出错啦!\n无法从服务器同步存档。\n错误原因:" + e); + }); +} + +control.prototype._syncLoad_write = function (data) { + if (data instanceof Array) { + core.status.event.selection = 1; + core.ui.drawConfirmBox("所有本地存档都将被覆盖,确认?", function () { + for (var i = 1; i <= 5 * (main.savePages || 30); i++) { + if (i <= data.length) + core.setLocalForage("save" + i, data[i - 1]); + else if (core.saves.ids[i]) + core.removeLocalForage("save" + i); + } + core.ui.closePanel(); + core.drawText("同步成功!\n你的本地所有存档均已被覆盖。"); + }, function () { + core.status.event.selection = 0; + core.ui._drawSyncSave(); + }); + } + else { + // 只覆盖单存档 + core.setLocalForage("save" + core.saves.saveIndex, data, function () { + core.drawText("同步成功!\n单存档已覆盖至存档" + core.saves.saveIndex); + }); + } +} + +////// 存档到本地 ////// +control.prototype.saveData = function () { + return this.controldata.saveData(); +} + +////// 从本地读档 ////// +control.prototype.loadData = function (data, callback) { + return this.controldata.loadData(data, callback); +} + +control.prototype.getSave = function (index, callback) { + if (index == 0) { + // --- 自动存档先从缓存中获取 + if (core.saves.autosave.data != null) + callback(core.saves.autosave.data); + else { + core.getLocalForage("autoSave", null, function (data) { + if (data != null) { + core.saves.autosave.data = data; + if (!(core.saves.autosave.data instanceof Array)) { + core.saves.autosave.data = [core.saves.autosave.data]; + } + core.saves.autosave.now = core.saves.autosave.data.length; + } + callback(core.saves.autosave.data); + }, function (err) { + console.error(err); + callback(null); + }); + } + return; + } + core.getLocalForage("save" + index, null, function (data) { + if (callback) callback(data); + }, function (err) { + console.error(err); + if (callback) callback(null); + }); +} + +control.prototype.getSaves = function (ids, callback) { + if (!(ids instanceof Array)) return this.getSave(ids, callback); + var count = ids.length, data = {}; + for (var i = 0; i < ids.length; ++i) { + (function (i) { + core.getSave(ids[i], function (result) { + data[i] = result; + if (Object.keys(data).length == count) + callback(data); + }) + })(i); + } +} + +control.prototype.getAllSaves = function (callback) { + var ids = Object.keys(core.saves.ids).filter(function (x) { return x != 0; }) + .sort(function (a, b) { return a - b; }), saves = []; + this.getSaves(ids, function (data) { + for (var i = 0; i < ids.length; ++i) { + if (data[i] != null) + saves.push(data[i]); + } + callback(saves); + }); +} + +////// 获得所有存在存档的存档位 ////// +control.prototype.getSaveIndexes = function (callback) { + var indexes = {}; + core.keysLocalForage(function (err, keys) { + if (err) { + console.error(err); + return callback(indexes); + } + keys.forEach(function (key) { + core.control._getSaveIndexes_getIndex(indexes, key); + }); + callback(indexes); + }); +} + +control.prototype._getSaveIndexes_getIndex = function (indexes, name) { + var e = new RegExp('^' + core.firstData.name + "_(save\\d+|autoSave)$").exec(name); + if (e) { + if (e[1] == 'autoSave') indexes[0] = true; + else indexes[parseInt(e[1].substring(4))] = true; + } +} + +////// 判断某个存档位是否存在存档 ////// +control.prototype.hasSave = function (index) { + return core.saves.ids[index] || false; +} + +////// 删除某个存档 +control.prototype.removeSave = function (index, callback) { + if (index == 0 || index == "autoSave") { + index = "autoSave"; + core.removeLocalForage(index, function () { + core.saves.autosave.data = null; + core.saves.autosave.updated = false; + if (callback) callback(); + }); + return; + } + core.removeLocalForage("save" + index, function () { + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + delete core.saves.favoriteName[index]; + core.control._updateFavoriteSaves(); + if (callback) callback(); + }, function () { + core.playSound('操作失败'); + core.drawTip("无法删除存档!"); + if (callback) callback(); + }); +} + +////// 读取收藏信息 +control.prototype._loadFavoriteSaves = function () { + core.saves.favorite = core.getLocalStorage("favorite", []); + // --- 移除不存在的收藏 + core.saves.favorite = core.saves.favorite.filter(function (i) { return core.hasSave(i); }); + core.saves.favoriteName = core.getLocalStorage("favoriteName", {}); +} + +control.prototype._updateFavoriteSaves = function () { + core.setLocalStorage("favorite", core.saves.favorite); + core.setLocalStorage("favoriteName", core.saves.favoriteName); +} + +// ------ 属性,状态,位置,buff,变量,锁定控制等 ------ // + +////// 设置勇士属性 ////// +control.prototype.setStatus = function (name, value) { + if (!core.status.hero) return; + if (name == 'x' || name == 'y' || name == 'direction') + this.setHeroLoc(name, value); + else + core.status.hero[name] = value; +} + +////// 增减勇士属性 ////// +control.prototype.addStatus = function (name, value) { + this.setStatus(name, this.getStatus(name) + value); +} + +////// 获得勇士属性 ////// +control.prototype.getStatus = function (name) { + if (!core.status.hero) return null; + if (name == 'x' || name == 'y' || name == 'direction') + return this.getHeroLoc(name); + if (main.mode == 'editor' && !core.hasFlag('__statistics__')) { + return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero[name]; + } + return core.status.hero[name]; +} + +////// 从status中获得属性,如果不存在则从勇士属性中获取 ////// +control.prototype.getStatusOrDefault = function (status, name) { + if (status && name in status) + return Math.floor(status[name]); + return Math.floor(this.getStatus(name)); +} + +////// 获得勇士实际属性(增幅后的) ////// +control.prototype.getRealStatus = function (name) { + return this.getRealStatusOrDefault(null, name); +} + +////// 从status中获得实际属性(增幅后的),如果不存在则从勇士属性中获取 ////// +control.prototype.getRealStatusOrDefault = function (status, name) { + return Math.floor(this.getStatusOrDefault(status, name) * this.getBuff(name)); +} + +////// 获得勇士原始属性(无装备和衰弱影响) ////// +control.prototype.getNakedStatus = function (name) { + var value = this.getStatus(name); + if (value == null) return value; + // 装备增幅 + core.status.hero.equipment.forEach(function (v) { + if (!v || !(core.material.items[v] || {}).equip) return; + value -= core.material.items[v].equip.value[name] || 0; + }); + // 衰弱扣除 + if (core.hasFlag('weak') && core.values.weakValue >= 1 && (name == 'atk' || name == 'def')) { + value += core.values.weakValue; + } + return value; +} + +////// 获得某个属性的名字 ////// +control.prototype.getStatusLabel = function (name) { + if (this.controldata.getStatusLabel) { + return this.controldata.getStatusLabel(name) || name; + } + return { + name: "名称", lv: "等级", hpmax: "生命上限", hp: "生命", manamax: "魔力上限", mana: "魔力", + atk: "攻击", def: "防御", mdef: "护盾", money: "金币", exp: "经验", point: "加点", steps: "步数" + }[name] || name; +} + +////// 设置某个属性的增幅值 ////// +control.prototype.setBuff = function (name, value) { + // 仅保留三位有效buff值 + value = parseFloat(value.toFixed(3)); + this.setFlag('__' + name + '_buff__', value); +} + +////// 加减某个属性的增幅值 ////// +control.prototype.addBuff = function (name, value) { + var buff = this.getBuff(name) + value; + // 仅保留三位有效buff值 + buff = parseFloat(buff.toFixed(3)); + this.setFlag('__' + name + '_buff__', buff); +} + +////// 获得某个属性的增幅值 ////// +control.prototype.getBuff = function (name) { + return core.getFlag('__' + name + '_buff__', 1); +} + +////// 获得或移除毒衰咒效果 ////// +control.prototype.triggerDebuff = function (action, type) { + return this.controldata.triggerDebuff(action, type); +} + +////// 设置勇士的位置 ////// +control.prototype.setHeroLoc = function (name, value, noGather) { + if (!core.status.hero) return; + core.status.hero.loc[name] = value; + if ((name == 'x' || name == 'y') && !noGather) { + this.gatherFollowers(); + } + core.ui.drawStatusBar(); +} + +////// 获得勇士的位置 ////// +control.prototype.getHeroLoc = function (name) { + if (!core.status.hero) return; + if (main.mode == 'editor') { + if (name == null) return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc; + return data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData.hero.loc[name]; + } + if (name == null) return core.status.hero.loc; + return core.status.hero.loc[name]; +} + +////// 获得某个等级的名称 ////// +control.prototype.getLvName = function (lv) { + if (!core.status.hero) return null; + if (lv == null) lv = core.status.hero.lv; + return ((core.firstData.levelUp || [])[lv - 1] || {}).title || lv; +} + +////// 获得下个等级所需经验;如果不存在下个等级,返回null。 ////// +control.prototype.getNextLvUpNeed = function () { + if (!core.status.hero) return null; + if (core.status.hero.lv >= core.firstData.levelUp.length) return null; + var need = core.calValue(core.firstData.levelUp[core.status.hero.lv].need); + if (core.flags.statusBarItems.indexOf('levelUpLeftMode') >= 0) + return Math.max(need - core.getStatus('exp'), 0); + else return need; +} + +////// 设置某个自定义变量或flag ////// +control.prototype.setFlag = function (name, value) { + if (value == null) return this.removeFlag(name); + if (!core.status.hero) return; + core.status.hero.flags[name] = value; +} + +////// 增加某个flag数值 ////// +control.prototype.addFlag = function (name, value) { + if (!core.status.hero) return; + core.setFlag(name, core.getFlag(name, 0) + value); +} + +////// 获得某个自定义变量或flag ////// +control.prototype.getFlag = function (name, defaultValue) { + if (!core.status.hero) return defaultValue; + var value = core.status.hero.flags[name]; + return value != null ? value : defaultValue; +} + +////// 是否存在某个自定义变量或flag,且值为true ////// +control.prototype.hasFlag = function (name) { + return !!core.getFlag(name); +} + +////// 删除某个自定义变量或flag ////// +control.prototype.removeFlag = function (name) { + if (!core.status.hero) return; + delete core.status.hero.flags[name]; +} + +////// 获得某个点的独立开关 ////// +control.prototype.getSwitch = function (x, y, floorId, name, defaultValue) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.getFlag(prefix + "@" + name, defaultValue); +} + +////// 设置某个点的独立开关 ////// +control.prototype.setSwitch = function (x, y, floorId, name, value) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.setFlag(prefix + "@" + name, value); +} + +////// 增加某个点的独立开关 ////// +control.prototype.addSwitch = function (x, y, floorId, name, value) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.addFlag(prefix + "@" + name, value); +} + +////// 判定某个点的独立开关 ////// +control.prototype.hasSwitch = function (x, y, floorId, name) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.hasFlag(prefix + "@" + name); +} + +////// 删除某个点的独立开关 ////// +control.prototype.removeSwitch = function (x, y, floorId, name) { + var prefix = [floorId || core.status.floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + return this.removeFlag(prefix + "@" + name); +} + +////// 锁定状态栏,常常用于事件处理 ////// +control.prototype.lockControl = function () { + core.status.lockControl = true; +} + +////// 解锁状态栏 ////// +control.prototype.unlockControl = function () { + core.status.lockControl = false; +} + +////// 开启debug模式 ////// +control.prototype.debug = function () { + core.setFlag('debug', true); + core.drawText("\t[调试模式开启]此模式下按住Ctrl键(或Ctrl+Shift键)可以穿墙并忽略一切事件。\n此模式下将无法上传成绩。"); +} + +control.prototype._bindRoutePush = function () { + core.status.route.push = function (element) { + // 忽视移动、转向、瞬移 + if (["up", "down", "left", "right", "turn"].indexOf(element) < 0 && !element.startsWith("move:")) { + core.clearRouteFolding(); + } + Array.prototype.push.call(core.status.route, element); + } +} + +////// 清除录像折叠信息 ////// +control.prototype.clearRouteFolding = function () { + core.status.routeFolding = {}; +} + +////// 检查录像折叠 ////// +control.prototype.checkRouteFolding = function () { + // 未开启、未开始游戏、录像播放中、正在事件中:不执行 + if (!core.flags.enableRouteFolding || !core.isPlaying() || core.isReplaying() || core.status.event.id) { + return this.clearRouteFolding(); + } + var hero = core.clone(core.status.hero, function (name, value) { + return name != 'steps' && typeof value == 'number'; + }); + var index = [core.getHeroLoc('x'), core.getHeroLoc('y'), core.getHeroLoc('direction').charAt(0)].join(','); + core.status.routeFolding = core.status.routeFolding || {}; + if (core.status.routeFolding[index]) { + var one = core.status.routeFolding[index]; + if (core.same(one.hero, hero) && one.length < core.status.route.length) { + Object.keys(core.status.routeFolding).forEach(function (v) { + if (core.status.routeFolding[v].length >= one.length) delete core.status.routeFolding[v]; + }); + core.status.route = core.status.route.slice(0, one.length); + this._bindRoutePush(); + } + } + core.status.routeFolding[index] = { hero: hero, length: core.status.route.length }; +} + +// ------ 天气,色调,BGM ------ // + +control.prototype.getMappedName = function (name) { + return core.getFlag('__nameMap__', {})[name] || (main.nameMap || {})[name] || name; +} + +////// 更改天气效果 ////// +control.prototype.setWeather = function (type, level) { + // 非雨雪 + if (type == null || !this.weathers[type]) { + core.deleteCanvas('weather') + core.animateFrame.weather.type = null; + core.animateFrame.weather.nodes = []; + return; + } + if (level == null) level = core.animateFrame.weather.level; + level = core.clamp(parseInt(level) || 5, 1, 10); + // 当前天气:则忽略 + if (type == core.animateFrame.weather.type && level == core.animateFrame.weather.level) return; + + // 计算当前的宽高 + core.createCanvas('weather', 0, 0, core._PX_, core._PY_, 80); + core.setOpacity('weather', 1.0); + core.animateFrame.weather.type = type; + core.animateFrame.weather.level = level; + core.animateFrame.weather.nodes = []; + try { + core.doFunc(this.weathers[type].initFunc, this, level); + } catch (e) { + console.error(e); + console.error("ERROR in weather[" + type + "]:已自动注销该项。"); + core.unregisterWeather(type); + } +} + +////// 注册一个天气 ////// +// name为天气类型,如 sun, rain, snow 等 +// initFunc 为设置为此天气时的初始化,接受level参数 +// frameFunc 为该天气下每帧的效果,接受和timestamp参数(从页面加载完毕到当前经过的时间) +control.prototype.registerWeather = function (name, initFunc, frameFunc) { + this.unregisterWeather(name); + this.weathers[name] = { initFunc: initFunc, frameFunc: frameFunc }; +} + +////// 取消注册一个天气 ////// +control.prototype.unregisterWeather = function (name) { + delete this.weathers[name]; + if (core.animateFrame.weather.type == name) { + this.setWeather(null); + } +} + +control.prototype._weather_rain = function (level) { + var number = level * parseInt(20 * core.bigmap.width * core.bigmap.height / (core._WIDTH_ * core._HEIGHT_)); + for (var a = 0; a < number; a++) { + core.animateFrame.weather.nodes.push({ + 'x': Math.random() * core.bigmap.width * 32, + 'y': Math.random() * core.bigmap.height * 32, + 'l': Math.random() * 2.5, + 'xs': -4 + Math.random() * 4 + 2, + 'ys': Math.random() * 10 + 10 + }) + } +} + +control.prototype._weather_snow = function (level) { + var number = level * parseInt(20 * core.bigmap.width * core.bigmap.height / (core._WIDTH_ * core._HEIGHT_)); + for (var a = 0; a < number; a++) { + core.animateFrame.weather.nodes.push({ + 'x': Math.random() * core.bigmap.width * 32, + 'y': Math.random() * core.bigmap.height * 32, + 'r': Math.random() * 5 + 1, + 'd': Math.random() * Math.min(level, 200), + }) + } +} + +control.prototype._weather_fog = function (level) { + if (!core.animateFrame.weather.fog) return; + core.animateFrame.weather.nodes = [{ + 'image': core.animateFrame.weather.fog, + 'level': 40 * level, + 'x': 0, + 'y': -core._PY_ / 2, + 'dx': -Math.random() * 1.5, + 'dy': Math.random(), + 'delta': 0.001, + }]; +} + +control.prototype._weather_cloud = function (level) { + if (!core.animateFrame.weather.cloud) return; + core.animateFrame.weather.nodes = [{ + 'image': core.animateFrame.weather.cloud, + 'level': 40 * level, + 'x': 0, + 'y': -core._PY_ / 2, + 'dx': -Math.random() * 1.5, + 'dy': Math.random(), + 'delta': 0.001, + }]; +} + +control.prototype._weather_sun = function (level) { + if (!core.animateFrame.weather.sun) return; + // 直接绘制 + core.clearMap('weather'); + core.drawImage( + 'weather', core.animateFrame.weather.sun, 0, 0, core.animateFrame.weather.sun.width, core.animateFrame.weather.sun.height, 0, 0, core._PX_, core._PY_ + ); + core.setOpacity('weather', level / 10); + core.animateFrame.weather.nodes = [{ opacity: level / 10, delta: 0.01 }]; +} + +////// 更改画面色调 ////// +control.prototype.setCurtain = function (color, time, moveMode, callback) { + if (time == null) time = 750; + if (time <= 0) time = 0; + if (!core.status.curtainColor) + core.status.curtainColor = [0, 0, 0, 0]; + if (!color) color = [0, 0, 0, 0]; + if (color[3] == null) color[3] = 1; + color[3] = core.clamp(color[3], 0, 1); + + if (time == 0) { + // 直接变色 + core.clearMap('curtain'); + core.fillRect('curtain', 0, 0, core._PX_, core._PY_, core.arrayToRGBA(color)); + core.status.curtainColor = color; + if (callback) callback(); + return; + } + + this._setCurtain_animate(core.status.curtainColor, color, time, moveMode, callback); +} + +control.prototype._setCurtain_animate = function (nowColor, color, time, moveMode, callback) { + time /= Math.max(core.status.replay.speed, 1) + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) steps = 1; + var curr = nowColor; + var moveFunc = core.applyEasing(moveMode); + + var cb = function () { + core.status.curtainColor = curr; + if (callback) callback(); + } + var animate = setInterval(function () { + step++; + curr = [ + nowColor[0] + (color[0] - nowColor[0]) * moveFunc(step / steps), + nowColor[1] + (color[1] - nowColor[1]) * moveFunc(step / steps), + nowColor[2] + (color[2] - nowColor[2]) * moveFunc(step / steps), + nowColor[3] + (color[3] - nowColor[3]) * moveFunc(step / steps), + ] + core.clearMap('curtain'); + core.fillRect('curtain', 0, 0, core._PX_, core._PY_, core.arrayToRGBA(curr)); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +////// 画面闪烁 ////// +control.prototype.screenFlash = function (color, time, times, moveMode, callback) { + times = times || 1; + time = time / 3; + var nowColor = core.clone(core.status.curtainColor); + core.setCurtain(color, time, moveMode, function () { + core.setCurtain(nowColor, time * 2, moveMode, function () { + if (times > 1) + core.screenFlash(color, time * 3, times - 1, moveMode, callback); + else { + if (callback) callback(); + } + }); + }); +} + +////// 播放背景音乐 ////// +control.prototype.playBgm = function (bgm, startTime) { + bgm = core.getMappedName(bgm); + if (main.mode != 'play' || !core.material.bgms[bgm]) return; + // 如果不允许播放 + if (!core.musicStatus.bgmStatus) { + try { + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; + core.material.bgms[bgm].pause(); + } + catch (e) { + console.error(e); + } + return; + } + this.setMusicBtn(); + + try { + this._playBgm_play(bgm, startTime); + } + catch (e) { + console.log("无法播放BGM " + bgm); + console.error(e); + core.musicStatus.playingBgm = null; + } +} + +control.prototype._playBgm_play = function (bgm, startTime) { + // 如果当前正在播放,且和本BGM相同,直接忽略 + if (core.musicStatus.playingBgm == bgm && !core.material.bgms[core.musicStatus.playingBgm].paused) { + return; + } + // 如果正在播放中,暂停 + if (core.musicStatus.playingBgm) { + core.material.bgms[core.musicStatus.playingBgm].pause(); + } + // 缓存BGM + core.loader.loadBgm(bgm); + // 播放当前BGM + core.material.bgms[bgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + core.material.bgms[bgm].currentTime = startTime || 0; + core.material.bgms[bgm].play(); + core.musicStatus.playingBgm = bgm; + core.musicStatus.lastBgm = bgm; + core.setBgmSpeed(100); +} + +///// 设置当前背景音乐的播放速度 ////// +control.prototype.setBgmSpeed = function (speed, usePitch) { + var bgm = core.musicStatus.playingBgm; + if (main.mode != 'play' || !core.material.bgms[bgm]) return; + bgm = core.material.bgms[bgm]; + if (speed < 30 || speed > 300) return; + bgm.playbackRate = speed / 100; + core.musicStatus.bgmSpeed = speed; + + if (bgm.preservesPitch != null) { + if (bgm.__preservesPitch == null) bgm.__preservesPitch = bgm.preservesPitch; + if (usePitch == null) bgm.preservesPitch = bgm.__preservesPitch; + else if (usePitch) bgm.preservesPitch = false; + else bgm.preservesPitch = true; + core.musicStatus.bgmUsePitch = usePitch; + } +} + +////// 暂停背景音乐的播放 ////// +control.prototype.pauseBgm = function () { + if (main.mode != 'play') return; + try { + if (core.musicStatus.playingBgm) { + core.musicStatus.pauseTime = core.material.bgms[core.musicStatus.playingBgm].currentTime; + core.material.bgms[core.musicStatus.playingBgm].pause(); + core.musicStatus.playingBgm = null; + } + } + catch (e) { + console.log("无法暂停BGM"); + console.error(e); + } + this.setMusicBtn(); +} + +////// 恢复背景音乐的播放 ////// +control.prototype.resumeBgm = function (resumeTime) { + if (main.mode != 'play') return; + try { + var speed = core.musicStatus.bgmSpeed; + var usePitch = core.musicStatus.bgmUsePitch; + core.playBgm(core.musicStatus.playingBgm || core.musicStatus.lastBgm || main.startBgm, + resumeTime ? core.musicStatus.pauseTime : 0); + if (resumeTime) { + core.setBgmSpeed(speed, usePitch); + } + } + catch (e) { + console.log("无法恢复BGM"); + console.error(e); + } + this.setMusicBtn(); +} + +control.prototype.setMusicBtn = function () { + if (core.musicStatus.bgmStatus) + core.dom.musicBtn.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABWVBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL///8AAAC5ubn+/v6xsbEtLS0MDAxmZmZoaGhvb2/c3Nzd3d38/Pz9/f0oKCgpKSl0dHR1dXW6urrb29v7+/v09PTv7+/39/cgICACAgImJibh4eGFhYWGhoaHh4eOjo5paWm7u7vDw8PMzMwyMjI7OztAQEDe3t5FRUVMTEzj4+Pl5eXm5ubp6enr6+tcXFzi4uL19fVeXl74+PgjIyNkZGQGBgaSkpKYmJiampqenp4DAwMwMDBnZ2cICAivr68eHh63t7cLCwsSEhLw8PBhYWEUFBQVFRXNzc3Pz8/Z2dna2toaGhqkpKSlpaWpqamrq6tFOUNAAAAAc3RSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyA0IuUgAAAVdJREFUeF5NkVVbw0AQRTcQrLR4IIEGcidJoaUuQHF3d3d3+P/CkuxCzss8nG++mbnDBJXhNt2CpbeFK1kQpSEKidlc8S9qdATRa6UIdQMoxEpDA0Ov3wUAPfW+qLWACydNv9zMrzkJwPK6FB3oHyOfXfuNxvoBQ+GmBYinhHB77TmiVBxoYUw1AYcEq332AS8OYKosAuTT0nza9uU2USYPRJgGxEiSOFywJ3mNARozgBJJzkfLvfu8JgGDWcC9FEsjWzR+y80gYDEAA8QZ3N6kmP1Fs3fEASB7pob7Hh+Wz5L0ci17Or05J7bH6B6dZv05XWK3rG+myV05Ert592Qo55sPuoIr7hEZHHtieIPWy0RU9DLwc3Mnck/vi8/E8XNrDWQtEVnL/ySKMrv0jPwPp870fprcyYifmiEmqGpHkI5q9ofSFIUk2qiwIGpEMyxYhhZRRcMPz89RJ2s9W8wAAAAASUVORK5CYII="; + else + core.dom.musicBtn.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABYlBMVEX///9iYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmL////8/PwAAABmZmZoaGihoaGioqKxsbG5ubnb29vc3Nzd3d3h4eHi4uL9/f3+/v4tLS1nZ2d0dHSUlJSenp66uroMDAz7+/spKSkoKCgUFBRpaWkVFRVvb291dXU7OzuVlZWYmJhkZGQgICAjIyOkpKQCAgK3t7cGBgbv7++pqamrq6seHh4mJiZhYWGamprp6enr6+saGhpeXl7j4+Pl5eXm5uZKSkrw8PD09PT19fW7u7vDw8PMzMwICAgwMDAyMjILCwtAQECGhoaHh4eBgYGFhYUSEhJXV1dZWVlcXFyOjo6SkpLNzc339/fPz8/Z2dna2tqTk5OlpaWxOPeTAAAAdnRSTlMAAwQFBhUWGxwkJSYyO0dISVBRUmpvj5CSk5SVoaOlpqiysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKyNuo+uwAAAWJJREFUeF5NkmV34zAQReUm7WbTuJBNunY3bvXGDjNTkZkZlpn5/9eR5FPfbzr3jGb0RkwRiMQMDm7EIgHmRxtLwMOaHHoQjwz4MUKeCM8AWMrmd7u7f/aXAMyOShHiQD1n04DtN5e5FMBFlSauIsm585dKi4CpuSYKJIv1tBDVmvOSqJgEoowFLSBHaQh10XHWiCgHWEGmAw2blPrvOK/KRJUGoLM4kCVSKrWz7HwgoiwQZyaQJ0+9PvxV23BNATAZB25IqX9b3+jTW9fcApwB6NLgUD5NY3mPXnwmFwBezff1ztzRFzTp94FXMy36HDuCa2RafdnnmZqtL818Gl9/qNnEeyrUk2aTPiKj3qMyWBVi/YSuWq5qiwxkbtX3vYWzdz/l8M0k8ERlvViiB1Ygslb7SbVtJezncj+Cx5bYaeGuonZqhZlieAp+no74/s5EAh6JcY35Cepxk4ObcT3IJPe/1lKsDpFCFQAAAABJRU5ErkJggg=="; +} + +////// 更改背景音乐的播放 ////// +control.prototype.triggerBgm = function () { + if (main.mode != 'play') return; + + core.musicStatus.bgmStatus = !core.musicStatus.bgmStatus; + if (core.musicStatus.bgmStatus) + this.resumeBgm(); + else + this.pauseBgm(); + core.setLocalStorage('bgmStatus', core.musicStatus.bgmStatus); +} + +////// 播放音频 ////// +control.prototype.playSound = function (sound, pitch, callback) { + sound = core.getMappedName(sound); + if (main.mode != 'play' || !core.musicStatus.soundStatus || !core.material.sounds[sound]) return; + try { + if (core.musicStatus.audioContext != null) { + var source = core.musicStatus.audioContext.createBufferSource(); + source.__name = sound; + source.buffer = core.material.sounds[sound]; + source.connect(core.musicStatus.gainNode); + var id = setTimeout(null); + if (pitch && pitch >= 30 && pitch <= 300) { + source.playbackRate.setValueAtTime(pitch / 100, 0); + } + source.onended = function () { + delete core.musicStatus.playingSounds[id]; + if (callback) callback(); + } + core.musicStatus.playingSounds[id] = source; + if (source.start) source.start(0); + else if (source.noteOn) source.noteOn(0); + return id; + } + else { + core.material.sounds[sound].volume = core.musicStatus.userVolume; + core.material.sounds[sound].play(); + if (callback) callback(); + } + } + catch (e) { + console.log("无法播放SE " + sound); + console.error(e); + } +} + +////// 停止所有音频 ////// +control.prototype.stopSound = function (id) { + if (id == null) { + Object.keys(core.musicStatus.playingSounds).forEach(function (id) { + core.control.stopSound(id); + }); + return; + } + var source = core.musicStatus.playingSounds[id]; + if (!source) return; + try { + if (source.stop) source.stop(); + else if (source.noteOff) source.noteOff(); + } + catch (e) { + console.error(e); + } + delete core.musicStatus.playingSounds[id]; +} + +////// 获得当前正在播放的所有(指定)音效的id列表 ////// +control.prototype.getPlayingSounds = function (name) { + name = core.getMappedName(name); + return Object.keys(core.musicStatus.playingSounds).filter(function (one) { + return name == null || core.musicStatus.playingSounds[one].__name == name + }); +} + +////// 检查bgm状态 ////// +control.prototype.checkBgm = function () { + core.playBgm(core.musicStatus.playingBgm || main.startBgm); +} + +///// 设置屏幕放缩 ////// +control.prototype.setDisplayScale = function (delta) { + var index = core.domStyle.availableScale.indexOf(core.domStyle.scale); + if (index < 0) return; + index = (index + delta + core.domStyle.availableScale.length) % core.domStyle.availableScale.length; + core.domStyle.scale = core.domStyle.availableScale[index]; + core.setLocalStorage('scale', core.domStyle.scale); + core.resize(); +} + +// ------ 状态栏,工具栏等相关 ------ // + +////// 清空状态栏 ////// +control.prototype.clearStatusBar = function () { + Object.keys(core.statusBar).forEach(function (e) { + if (core.statusBar[e].innerHTML != null) { + core.statusBar[e].innerHTML = " "; + core.statusBar[e].removeAttribute('_style'); + core.statusBar[e].removeAttribute('_value'); + } + }) + core.statusBar.image.book.style.opacity = 0.3; + if (!core.flags.equipboxButton) + core.statusBar.image.fly.style.opacity = 0.3; +} + +////// 更新状态栏 ////// +control.prototype.updateStatusBar = function (doNotCheckAutoEvents, immediate) { + if (!core.isPlaying()) return; + if (immediate) { + return this.updateStatusBar_update(); + } + if (!doNotCheckAutoEvents) this.noAutoEvents = false; + if (core.isReplaying()) return this.updateStatusBar_update(); + if (!core.control.updateNextFrame) { + core.control.updateNextFrame = true; + requestAnimationFrame(this.updateStatusBar_update); + } +}; + +control.prototype.updateStatusBar_update = function () { + core.control.updateNextFrame = false; + if (!core.isPlaying() || core.hasFlag('__statistics__')) return; + core.control.controldata.updateStatusBar(); + if (!core.control.noAutoEvents) core.checkAutoEvents(); + core.control._updateStatusBar_setToolboxIcon(); + core.clearRouteFolding(); + core.control.noAutoEvents = true; +}; + +control.prototype._updateStatusBar_setToolboxIcon = function () { + if (core.isReplaying()) { + core.statusBar.image.book.src = core.status.replay.pausing ? core.statusBar.icons.play.src : core.statusBar.icons.pause.src; + core.statusBar.image.book.style.opacity = 1; + core.statusBar.image.fly.src = core.statusBar.icons.stop.src; + core.statusBar.image.fly.style.opacity = 1; + core.statusBar.image.toolbox.src = core.statusBar.icons.rewind.src; + core.statusBar.image.keyboard.src = core.statusBar.icons.book.src; + core.statusBar.image.shop.src = core.statusBar.icons.floor.src; + core.statusBar.image.save.src = core.statusBar.icons.speedDown.src; + core.statusBar.image.save.style.opacity = 1; + core.statusBar.image.load.src = core.statusBar.icons.speedUp.src; + core.statusBar.image.settings.src = core.statusBar.icons.save.src; + } + else { + core.statusBar.image.book.src = core.statusBar.icons.book.src; + core.statusBar.image.book.style.opacity = core.hasItem('book') ? 1 : 0.3; + if (!core.flags.equipboxButton) { + core.statusBar.image.fly.src = core.statusBar.icons.fly.src; + core.statusBar.image.fly.style.opacity = core.hasItem('fly') ? 1 : 0.3; + } + else { + core.statusBar.image.fly.src = core.statusBar.icons.equipbox.src; + core.statusBar.image.fly.style.opacity = 1; + } + core.statusBar.image.toolbox.src = core.statusBar.icons.toolbox.src; + core.statusBar.image.keyboard.src = core.statusBar.icons.keyboard.src; + core.statusBar.image.shop.src = core.statusBar.icons.shop.src; + core.statusBar.image.save.src = core.statusBar.icons.save.src; + core.statusBar.image.save.style.opacity = core.hasFlag('__forbidSave__') ? 0.3 : 1; + core.statusBar.image.load.src = core.statusBar.icons.load.src; + core.statusBar.image.settings.src = core.statusBar.icons.settings.src; + } +} + +control.prototype.showStatusBar = function () { + if (main.mode == 'editor') return; + if (core.domStyle.showStatusBar) return; + var statusItems = core.dom.status; + core.domStyle.showStatusBar = true; + core.removeFlag('hideStatusBar'); + // 显示 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 1; + this.setToolbarButton(false); + core.dom.tools.hard.style.display = 'block'; + core.dom.toolBar.style.display = 'block'; +} + +control.prototype.hideStatusBar = function (showToolbox) { + if (main.mode == 'editor') return; + + // 如果原本就是隐藏的,则先显示 + if (!core.domStyle.showStatusBar) + this.showStatusBar(); + if (core.isReplaying()) showToolbox = true; + + var statusItems = core.dom.status, toolItems = core.dom.tools; + core.domStyle.showStatusBar = false; + core.setFlag('hideStatusBar', true); + core.setFlag('showToolbox', showToolbox || null); + // 隐藏 + for (var i = 0; i < statusItems.length; ++i) + statusItems[i].style.opacity = 0; + if ((!core.domStyle.isVertical && !core.flags.extendToolbar) || !showToolbox) { + for (var i = 0; i < toolItems.length; ++i) + toolItems[i].style.display = 'none'; + } + if (!core.domStyle.isVertical && !core.flags.extendToolbar) { + core.dom.toolBar.style.display = 'none'; + } +} + +////// 更新状态栏的勇士图标 ////// +control.prototype.updateHeroIcon = function (name) { + name = name || "hero.png"; + if (core.statusBar.icons.name == name) return; + core.statusBar.icons.name = name; + + var image = core.material.images.hero; + // 全身图 + var w = core.material.icons.hero.width || 32; + var h = core.material.icons.hero.height || 48; + var ratio = Math.min(w / h, 1), width = 32 * ratio, left = 16 - width / 2; + + var canvas = document.createElement("canvas"); + var ctx = canvas.getContext("2d"); + canvas.width = 32; + canvas.height = 32; + core.drawImage(ctx, image, 0, 0, w, h, left, 0, width, 32); + + core.statusBar.image.name.src = canvas.toDataURL("image/png"); +} + +////// 改变工具栏为按钮1-8 ////// +control.prototype.setToolbarButton = function (useButton) { + if (!core.domStyle.showStatusBar) { + // 隐藏状态栏时检查竖屏 + if (!core.domStyle.isVertical && !core.flags.extendToolbar) { + for (var i = 0; i < core.dom.tools.length; ++i) + core.dom.tools[i].style.display = 'none'; + return; + } + if (!core.hasFlag('showToolbox')) return; + else core.dom.tools.hard.style.display = 'block'; + } + + if (useButton == null) useButton = core.domStyle.toolbarBtn; + if ((!core.domStyle.isVertical && !core.flags.extendToolbar)) useButton = false; + core.domStyle.toolbarBtn = useButton; + + if (useButton) { + ["book", "fly", "toolbox", "keyboard", "shop", "save", "load", "settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }) + main.statusBar.image.btn8.style.filter = core.getLocalStorage('altKey') ? 'sepia(1) contrast(1.5)' : ''; + } + else { + ["btn1", "btn2", "btn3", "btn4", "btn5", "btn6", "btn7", "btn8"].forEach(function (t) { + core.statusBar.image[t].style.display = 'none'; + }); + ["book", "fly", "toolbox", "save", "load", "settings"].forEach(function (t) { + core.statusBar.image[t].style.display = 'block'; + }); + core.statusBar.image.keyboard.style.display + = core.statusBar.image.shop.style.display + = core.domStyle.isVertical || core.flags.extendToolbar ? "block" : "none"; + } +} + +////// ------ resize处理 ------ // + +control.prototype._shouldDisplayStatus = function (id) { + if (id == null) { + var toDraw = [], status = core.dom.status; + for (var i = 0; i < status.length; ++i) { + var dom = core.dom.status[i], idCol = dom.id; + if (idCol.indexOf("Col") != idCol.length - 3) continue; + var id = idCol.substring(0, idCol.length - 3); + if (!this._shouldDisplayStatus(id)) continue; + toDraw.push(id); + } + return toDraw; + } + var obj = {}; + core.flags.statusBarItems.forEach(function (v) { obj[v] = true; }) + switch (id) { + case 'floor': return obj.enableFloor; + case 'name': return obj.enableName; + case 'lv': return obj.enableLv; + case 'hp': return obj.enableHP; + case 'hpmax': return obj.enableHPMax; + case 'mana': return obj.enableMana; + case 'atk': return obj.enableAtk; + case 'def': return obj.enableDef; + case 'mdef': return obj.enableMDef; + case 'money': return obj.enableMoney; + case 'exp': return obj.enableExp && !obj.levelUpLeftMode; + case 'up': return obj.enableLevelUp; + case 'skill': return obj.enableSkill; + case 'key': return obj.enableKeys; + case 'pzf': return obj.enablePZF; + case 'debuff': return obj.enableDebuff; + default: return true; + } +} + +////// 注册一个resize函数 ////// +// name为名称,可供注销使用 +// func可以是一个函数,或者是插件中的函数名;可以接受obj参数,详见resize函数。 +control.prototype.registerResize = function (name, func) { + this.unregisterResize(name); + this.resizes.push({ name: name, func: func }); +} + +////// 注销一个resize函数 ////// +control.prototype.unregisterResize = function (name) { + this.resizes = this.resizes.filter(function (b) { return b.name != name; }); +} + +control.prototype._doResize = function (obj) { + for (var i in this.resizes) { + try { + if (core.doFunc(this.resizes[i].func, this, obj)) return true; + } catch (e) { + console.error(e); + console.error("ERROR in resizes[" + this.resizes[i].name + "]:已自动注销该项。"); + this.unregisterResize(this.resizes[i].name); + } + } + return false; +} + +////// 屏幕分辨率改变后重新自适应 ////// +control.prototype.resize = function () { + if (main.mode == 'editor') return; + var clientWidth = main.dom.body.clientWidth, clientHeight = main.dom.body.clientHeight; + var BORDER = 3; + var extendToolbar = core.flags.extendToolbar; + let hideLeftStatusBar = core.flags.hideLeftStatusBar; + var BAR_WIDTH = hideLeftStatusBar ? 0 : Math.round(core._PY_ * 0.31); + + var horizontalMaxRatio = (clientHeight - 2 * BORDER - (hideLeftStatusBar ? BORDER : 0)) / (core._PY_ + (hideLeftStatusBar ? 38 : 0)); + + if (clientWidth - 3 * BORDER >= core._PX_ + BAR_WIDTH || (clientWidth > clientHeight && horizontalMaxRatio < 1)) { + // 横屏 + core.domStyle.isVertical = false; + + core.domStyle.availableScale = []; + [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5].forEach(function (v) { + if (clientWidth - 3 * BORDER >= v * (core._PX_ + BAR_WIDTH) && horizontalMaxRatio >= v) { + core.domStyle.availableScale.push(v); + } + }); + if (core.domStyle.availableScale.indexOf(core.domStyle.scale) < 0) { + core.domStyle.scale = Math.min(1, horizontalMaxRatio); + } + } + else { + // 竖屏 + core.domStyle.isVertical = true; + core.domStyle.scale = Math.min((clientWidth - 2 * BORDER) / core._PX_); + core.domStyle.availableScale = []; + extendToolbar = false; + hideLeftStatusBar = false; + BAR_WIDTH = Math.round(core._PX_ * 0.3); + } + + var statusDisplayArr = this._shouldDisplayStatus(), count = statusDisplayArr.length; + var statusCanvas = core.flags.statusCanvas, statusCanvasRows = core.values.statusCanvasRowsOnMobile || 3; + var col = statusCanvas ? statusCanvasRows : Math.ceil(count / 3); + if (col > 5) { + if (statusCanvas) alert("自绘状态栏的在竖屏下的行数应不超过5!"); + else alert("当前状态栏数目(" + count + ")大于15,请调整到不超过15以避免手机端出现显示问题。"); + } + var globalAttribute = core.status.globalAttribute || core.initStatus.globalAttribute; + + var obj = { + clientWidth: clientWidth, + clientHeight: clientHeight, + BORDER: BORDER, + BAR_WIDTH: BAR_WIDTH, + TOOLBAR_HEIGHT: 38, + outerWidth: core._PX_ * core.domStyle.scale + 2 * BORDER, + outerHeight: core._PY_ * core.domStyle.scale + 2 * BORDER, + globalAttribute: globalAttribute, + border: '3px ' + core.arrayToRGBA(globalAttribute.borderColor) + ' solid', + statusDisplayArr: statusDisplayArr, + count: count, + col: col, + statusBarHeightInVertical: core.domStyle.isVertical ? (32 * col + 6) * core.domStyle.scale + 2 * BORDER : 0, + toolbarHeightInVertical: core.domStyle.isVertical ? 38 * core.domStyle.scale + 2 * BORDER : 0, + extendToolbar: extendToolbar, + is15x15: false, + hideLeftStatusBar + }; + + this._doResize(obj); + this.setToolbarButton(); + core.updateStatusBar(); +} + +control.prototype._resize_gameGroup = function (obj) { + var startBackground = core.domStyle.isVertical ? (main.styles.startVerticalBackground || main.styles.startBackground) : main.styles.startBackground; + if (main.dom.startBackground.getAttribute('__src__') != startBackground) { + main.dom.startBackground.setAttribute('__src__', startBackground); + main.dom.startBackground.src = startBackground; + } + + var gameGroup = core.dom.gameGroup; + var totalWidth, totalHeight; + if (core.domStyle.isVertical) { + totalWidth = obj.outerWidth; + totalHeight = obj.outerHeight + obj.statusBarHeightInVertical + obj.toolbarHeightInVertical + } + else { + totalWidth = obj.outerWidth + obj.BAR_WIDTH * core.domStyle.scale + (obj.hideLeftStatusBar ? 0 : obj.BORDER); + totalHeight = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0); + } + gameGroup.style.width = totalWidth + "px"; + gameGroup.style.height = totalHeight + "px"; + gameGroup.style.left = (obj.clientWidth - totalWidth) / 2 + "px"; + gameGroup.style.top = (obj.clientHeight - totalHeight) / 2 + "px"; + // floorMsgGroup + var floorMsgGroup = core.dom.floorMsgGroup; + floorMsgGroup.style = obj.globalAttribute.floorChangingStyle; + floorMsgGroup.style.width = obj.outerWidth - 2 * obj.BORDER + "px"; + floorMsgGroup.style.height = totalHeight - 2 * obj.BORDER + "px"; + floorMsgGroup.style.fontSize = 16 * core.domStyle.scale + "px"; + // startPanel + core.dom.startPanel.style.fontSize = 16 * core.domStyle.scale + "px"; + // musicBtn + if (core.domStyle.isVertical || core.domStyle.scale < 1) { + core.dom.musicBtn.style.right = core.dom.musicBtn.style.bottom = "3px"; + } + else { + core.dom.musicBtn.style.right = (obj.clientWidth - totalWidth) / 2 + "px"; + core.dom.musicBtn.style.bottom = (obj.clientHeight - totalHeight) / 2 - 27 + "px"; + } +} + +control.prototype._resize_canvas = function (obj) { + var innerWidth = (core._PX_ * core.domStyle.scale) + "px", innerHeight = (core._PY_ * core.domStyle.scale) + "px"; + if (!core.isPlaying()) { + for (var i = 0; i < core.dom.gameCanvas.length; ++i) { + var ctx = core.dom.gameCanvas[i].getContext('2d'); + core.resizeCanvas(ctx, core._PX_, core._PY_); + core.dom.gameCanvas[i].style.width = innerWidth; + core.dom.gameCanvas[i].style.height = innerHeight; + } + } else { + requestAnimationFrame(function () { + for (var i = 0; i < core.dom.gameCanvas.length; ++i) { + core.dom.gameCanvas[i].style.width = innerWidth; + core.dom.gameCanvas[i].style.height = innerHeight; + } + }); + } + core.dom.gif.style.width = innerWidth; + core.dom.gif.style.height = innerHeight; + core.dom.gif2.style.width = innerWidth; + core.dom.gif2.style.height = innerHeight; + core.dom.gameDraw.style.width = innerWidth; + core.dom.gameDraw.style.height = innerHeight; + core.dom.gameDraw.style.top = obj.statusBarHeightInVertical + "px"; + core.dom.gameDraw.style.right = 0; + core.dom.gameDraw.style.border = obj.border; + // resize bigmap + core.bigmap.canvas.forEach(function (cn) { + var ratio = core.canvas[cn].canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1; + core.canvas[cn].canvas.style.width = core.canvas[cn].canvas.width / ratio * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = core.canvas[cn].canvas.height / ratio * core.domStyle.scale + "px"; + }); + // resize dynamic canvas + if (!core.isPlaying()) { + for (var name in core.dymCanvas) { + var ctx = core.dymCanvas[name], canvas = ctx.canvas; + // core.maps._setHDCanvasSize(ctx, parseFloat(canvas.getAttribute('_width')), parseFloat(canvas.getAttribute('_height'))); + canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + var scale = canvas.getAttribute('_scale') || 1; + core.resizeCanvas(canvas, canvas.width * scale / core.domStyle.scale, canvas.height * scale / core.domStyle.scale); + } + } else { + for (var name in core.dymCanvas) { + var ctx = core.dymCanvas[name], canvas = ctx.canvas; + core.resizeCanvas(ctx, parseFloat(canvas.getAttribute("_width")), parseFloat(canvas.getAttribute("_height"))) + canvas.style.left = parseFloat(canvas.getAttribute("_left")) * core.domStyle.scale + "px"; + canvas.style.top = parseFloat(canvas.getAttribute("_top")) * core.domStyle.scale + "px"; + } + } + // resize next + main.dom.next.style.width = main.dom.next.style.height = 5 * core.domStyle.scale + "px"; + main.dom.next.style.borderBottomWidth = main.dom.next.style.borderRightWidth = 4 * core.domStyle.scale + "px"; +} + +control.prototype._resize_statusBar = function (obj) { + // statusBar + var statusBar = core.dom.statusBar; + if (core.domStyle.isVertical) { + statusBar.style.width = obj.outerWidth + "px"; + statusBar.style.height = obj.statusBarHeightInVertical + "px"; + statusBar.style.background = obj.globalAttribute.statusTopBackground; + statusBar.style.fontSize = 16 * core.domStyle.scale + "px"; + } + else { + statusBar.style.width = (obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER) + "px"; + statusBar.style.height = obj.outerHeight + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px"; + statusBar.style.background = obj.globalAttribute.statusLeftBackground; + // --- 计算文字大小 + if (obj.hideLeftStatusBar) { + statusBar.style.fontSize = 16 * core.domStyle.scale + "px"; + } else { + statusBar.style.fontSize = 16 * Math.min(1, (core._HEIGHT_ - 4) / obj.count) * core.domStyle.scale + "px"; + } + } + statusBar.style.display = obj.hideLeftStatusBar ? 'none' : 'block'; + statusBar.style.borderTop = statusBar.style.borderLeft = obj.border; + statusBar.style.borderRight = core.domStyle.isVertical ? obj.border : ''; + statusBar.style.borderBottom = core.domStyle.isVertical ? '' : obj.border; + // 自绘状态栏 + if (core.domStyle.isVertical) { + core.dom.statusCanvas.style.width = core._PX_ * core.domStyle.scale + "px"; + core.dom.statusCanvas.style.height = obj.statusBarHeightInVertical - 3 + "px"; + core.maps._setHDCanvasSize(core.dom.statusCanvasCtx, core._PX_, obj.col * 32 + 9); + } + else { + core.dom.statusCanvas.style.width = obj.BAR_WIDTH * core.domStyle.scale + "px"; + core.dom.statusCanvas.style.height = obj.outerHeight - 2 * obj.BORDER + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER : 0) + "px"; + core.maps._setHDCanvasSize(core.dom.statusCanvasCtx, obj.BAR_WIDTH, core._PY_ + (obj.extendToolbar ? obj.TOOLBAR_HEIGHT + obj.BORDER : 0)); + } + core.dom.statusCanvas.style.display = core.flags.statusCanvas && !obj.hideLeftStatusBar ? "block" : "none"; +} + +control.prototype._resize_status = function (obj) { + var statusHeight; + if (core.domStyle.isVertical) { + statusHeight = 32 * core.domStyle.scale * 0.8; + } else { + statusHeight = (obj.hideLeftStatusBar ? core._HEIGHT_ : core._HEIGHT_ - 4) / obj.count * 32 * core.domStyle.scale * 0.8; + } + // status + for (var i = 0; i < core.dom.status.length; ++i) { + var id = core.dom.status[i].id, style = core.dom.status[i].style; + if (id.endsWith("Col")) id = id.substring(0, id.length - 3); + style.display = core.flags.statusCanvas || obj.statusDisplayArr.indexOf(id) < 0 ? 'none' : 'block'; + style.margin = 3 * core.domStyle.scale + "px"; + style.height = statusHeight + "px"; + style.maxWidth = obj.BAR_WIDTH * core.domStyle.scale * (core.domStyle.isVertical ? 0.95 : 1) + obj.BORDER + "px"; + if (obj.is15x15 && !core.domStyle.isVertical) + style.marginLeft = 11 * core.domStyle.scale + "px"; + } + // statusLabels, statusTexts + for (var i = 0; i < core.dom.statusLabels.length; ++i) { + core.dom.statusLabels[i].style.lineHeight = statusHeight + "px"; + core.dom.statusLabels[i].style.marginLeft = 6 * core.domStyle.scale + "px"; + } + for (var i = 0; i < core.dom.statusTexts.length; ++i) { + core.dom.statusTexts[i].style.color = core.arrayToRGBA(obj.globalAttribute.statusBarColor); + } + // keys + if (core.flags.statusBarItems.indexOf('enableGreenKey') >= 0) { + core.dom.keyCol.style.fontSize = '0.75em'; + core.statusBar.greenKey.style.display = ''; + } else { + core.dom.keyCol.style.fontSize = ''; + core.statusBar.greenKey.style.display = 'none'; + } +} + +control.prototype._resize_toolBar = function (obj) { + // toolBar + var toolBar = core.dom.toolBar; + if (core.domStyle.isVertical) { + toolBar.style.left = 0; + toolBar.style.right = ""; + toolBar.style.width = obj.outerWidth + "px"; + toolBar.style.top = obj.statusBarHeightInVertical + obj.outerHeight + "px"; + toolBar.style.height = obj.toolbarHeightInVertical + "px"; + toolBar.style.background = obj.globalAttribute.toolsBackground; + } + else { + if (obj.extendToolbar || obj.hideLeftStatusBar) { + toolBar.style.left = ""; + toolBar.style.right = 0; + toolBar.style.width = obj.outerWidth + "px"; + toolBar.style.top = obj.outerHeight + "px"; + toolBar.style.height = obj.TOOLBAR_HEIGHT * core.domStyle.scale + obj.BORDER + "px"; + toolBar.style.background = obj.globalAttribute.toolsBackground; + } else { + toolBar.style.left = 0; + toolBar.style.right = ""; + toolBar.style.width = obj.BAR_WIDTH * core.domStyle.scale + obj.BORDER + "px"; + toolBar.style.top = 0.75 * obj.outerHeight + "px"; + toolBar.style.height = 0.25 * obj.outerHeight + "px"; + toolBar.style.background = 'transparent'; + } + } + toolBar.style.borderLeft = obj.border; + toolBar.style.borderRight = toolBar.style.borderBottom = core.domStyle.isVertical || obj.extendToolbar ? obj.border : ''; + toolBar.style.fontSize = 16 * core.domStyle.scale + "px"; + + if (!core.domStyle.showStatusBar && !core.domStyle.isVertical && !obj.extendToolbar) { + toolBar.style.display = 'none'; + } else { + toolBar.style.display = 'block'; + } +} + +control.prototype._resize_tools = function (obj) { + var toolsHeight = 32 * core.domStyle.scale * ((core.domStyle.isVertical || obj.extendToolbar) && !obj.is15x15 ? 0.95 : 1); + var toolsMarginLeft; + if (core.domStyle.isVertical || obj.extendToolbar) + toolsMarginLeft = (core._HALF_WIDTH_ - 3) * 3 * core.domStyle.scale; + else + toolsMarginLeft = (obj.BAR_WIDTH * core.domStyle.scale - 9 - toolsHeight * 3) / 4; + for (var i = 0; i < core.dom.tools.length; ++i) { + var style = core.dom.tools[i].style; + style.height = toolsHeight + "px"; + style.marginLeft = toolsMarginLeft + "px"; + style.marginTop = 3 * core.domStyle.scale + "px" + } + core.dom.hard.style.lineHeight = toolsHeight + "px"; + if (core.domStyle.isVertical || obj.extendToolbar) { + core.dom.hard.style.width = obj.outerWidth - 9 * toolsMarginLeft - 8.5 * toolsHeight - 12 + "px"; + } + else { + core.dom.hard.style.width = obj.BAR_WIDTH * core.domStyle.scale - 9 - 2 * toolsMarginLeft + "px"; + if (!obj.is15x15) core.dom.hard.style.marginTop = 0; + } +} diff --git a/libs/core.js b/libs/core.js new file mode 100644 index 0000000..a6da0df --- /dev/null +++ b/libs/core.js @@ -0,0 +1,547 @@ + +/** + * 初始化 start + */ + +"use strict"; + +// /** +// * @type {CoreMixin} +// */ +// const core = (() => { + +function core () { + this._WIDTH_ = 13; + this._HEIGHT_ = 13; + this._PX_ = this._WIDTH_ * 32; + this._PY_ = this._HEIGHT_ * 32; + this._HALF_WIDTH_ = Math.floor(this._WIDTH_ / 2); + this._HALF_HEIGHT_ = Math.floor(this._HEIGHT_ / 2); + + this.__SIZE__ = main.mode == 'editor' ? 13 : this._HEIGHT_; + this.__PIXELS__ = this.__SIZE__ * 32; + this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2); + this.material = { + 'animates': {}, + 'images': {}, + 'bgms': {}, + 'sounds': {}, + 'items': {}, + 'enemys': {}, + 'icons': {}, + 'ground': null, + 'grundCanvas': null, + 'groundPattern': null, + 'autotileEdges': {}, + } + this.timeout = { + 'turnHeroTimeout': null, + 'onDownTimeout': null, + 'sleepTimeout': null, + } + this.interval = { + 'heroMoveInterval': null, + 'onDownInterval': null, + } + this.animateFrame = { + 'totalTime': 0, + 'totalTimeStart': 0, + 'globalAnimate': false, + 'globalTime': 0, + 'selectorTime': 0, + 'selectorUp': true, + 'animateTime': 0, + 'moveTime': 0, + 'lastLegTime': 0, + 'leftLeg': true, + 'weather': { + 'time': 0, + 'type': null, + 'level': 1, + 'nodes': [], + 'data': null, + 'fog': null, + 'cloud': null, + 'sun': null + }, + "tip": null, + "asyncId": {}, + "lastAsyncId": null + } + this.musicStatus = { + 'audioContext': null, // WebAudioContext + 'bgmStatus': false, // 是否播放BGM + 'soundStatus': true, // 是否播放SE + 'playingBgm': null, // 正在播放的BGM + 'pauseTime': 0, // 上次暂停的时间 + 'lastBgm': null, // 上次播放的bgm + 'gainNode': null, + 'playingSounds': {}, // 正在播放的SE + 'userVolume': 1.0, // 用户音量 + 'designVolume': 1.0, //设计音量 + 'bgmSpeed': 100, // 背景音乐速度 + 'bgmUsePitch': null, // 是否同时修改音调 + 'cachedBgms': [], // 缓存BGM内容 + 'cachedBgmCount': 8, // 缓存的bgm数量 + } + this.platform = { + 'isOnline': true, // 是否http + 'isPC': true, // 是否是PC + 'isAndroid': false, // 是否是Android + 'isIOS': false, // 是否是iOS + 'string': 'PC', + 'isWeChat': false, // 是否是微信 + 'isQQ': false, // 是否是QQ + 'isChrome': false, // 是否是Chrome + 'supportCopy': false, // 是否支持复制到剪切板 + + 'fileInput': null, // FileInput + 'fileReader': null, // 是否支持FileReader + 'successCallback': null, // 读取成功 + 'errorCallback': null, // 读取失败 + } + // 样式 + this.domStyle = { + scale: 1.0, + ratio: 1.0, + hdCanvas: ["damage", "ui", "data"], + availableScale: [], + isVertical: false, + showStatusBar: true, + toolbarBtn: false, + } + this.bigmap = { + canvas: ["bg", "event", "event2", "fg", "damage"], + offsetX: 0, // in pixel + offsetY: 0, + posX: 0, // + posY: 0, + width: main.mode == 'editor' ? this.__SIZE__ : this._WIDTH_, // map width and height + height: main.mode == 'editor' ? this.__SIZE__ : this._HEIGHT_, + v2: false, + threshold: 1024, + extend: 10, + scale: 1.0, + tempCanvas: null, // A temp canvas for drawing + cacheCanvas: null, // A cache canvas + } + this.saves = { + "saveIndex": null, + "ids": {}, + "autosave": { + "data": null, + "time": 0, + "updated": false, + "storage": true, // 是否把自动存档写入文件a + "max": 50, // 自动存档最大回退数 + "now": 0, + }, + "favorite": [], + "favoriteName": {}, + "cache": {} + } + this.initStatus = { + 'played': false, + 'gameOver': false, + + // 勇士属性 + 'hero': {}, + 'heroCenter': { 'px': null, 'py': null }, + + // 当前地图 + 'floorId': null, + 'thisMap': null, + 'maps': null, + 'bgmaps': {}, + 'fgmaps': {}, + 'mapBlockObjs': {}, + 'checkBlock': {}, // 每个点的阻激夹域信息 + 'damage': { // 每个点的显伤绘制 + 'posX': 0, + 'posY': 0, + 'data': [], + 'extraData': [], + }, + + 'lockControl': false, + + // 勇士移动状态 + 'heroMoving': 0, + 'heroStop': true, + + // 自动寻路相关 + 'automaticRoute': { + 'autoHeroMove': false, + 'autoStep': 0, + 'movedStep': 0, + 'destStep': 0, + 'destX': null, + 'destY': null, + 'offsetX': null, + 'offsetY': null, + 'autoStepRoutes': [], + 'moveStepBeforeStop': [], + 'lastDirection': null, + 'cursorX': 0, + 'cursorY': 0, + "moveDirectly": false, + }, + + // 按下键的时间:为了判定双击 + 'downTime': null, + 'ctrlDown': false, + 'preview': { + 'enabled': false, + 'prepareDragging': false, + 'dragging': false, + 'px': 0, + 'py': 0, + }, + + // 路线&回放 + 'route': [], + 'replay': { + 'replaying': false, + 'pausing': false, + 'animate': false, // 正在某段动画中 + 'failed': false, + 'toReplay': [], + 'totalList': [], + 'speed': 1.0, + 'steps': 0, + 'save': [], + }, + // 录像折叠 + 'routeFolding': {}, + + // event事件 + 'shops': {}, + 'event': { + 'id': null, + 'data': null, + 'selection': null, + 'ui': null, + 'interval': null, + }, + 'autoEvents': [], + 'textAttribute': { + 'position': "center", + "offset": 0, + "title": [255, 215, 0, 1], + "background": [0, 0, 0, 0.85], + "text": [255, 255, 255, 1], + "titlefont": 22, + "textfont": 16, + "bold": false, + "time": 0, + "letterSpacing": 0, + "animateTime": 0, + }, + "globalAttribute": { + 'equipName': main.equipName || [], + "statusLeftBackground": main.styles.statusLeftBackground || "url(project/materials/ground.png) repeat", + "statusTopBackground": main.styles.statusTopBackground || "url(project/materials/ground.png) repeat", + "toolsBackground": main.styles.toolsBackground || "url(project/materials/ground.png) repeat", + "borderColor": main.styles.borderColor || [204, 204, 204, 1], + "statusBarColor": main.styles.statusBarColor || [255, 255, 255, 1], + "floorChangingStyle": main.styles.floorChangingStyle || "background-color: black; color: white", + "selectColor": main.styles.selectColor || [255, 215, 0, 1], + "font": main.styles.font || "Verdana" + }, + 'curtainColor': null, + + // 动画 + 'globalAnimateObjs': [], + 'floorAnimateObjs': [], + 'boxAnimateObjs': [], + 'autotileAnimateObjs': [], + "globalAnimateStatus": 0, + 'animateObjs': [], + }; + // 标记的楼层列表,用于数据统计 + this.markedFloorIds = {}; + this.status = {}; + this.dymCanvas = {}; + + if (main.mode == 'editor') { + document.documentElement.style.setProperty('--size', this.__SIZE__); + document.documentElement.style.setProperty('--pixel', this.__PIXELS__ + 'px'); + } +} + +/////////// 系统事件相关 /////////// + +////// 初始化 ////// +core.prototype.init = function (coreData, callback) { + this._forwardFuncs(); + for (var key in coreData) + core[key] = coreData[key]; + this._init_flags(); + this._init_platform(); + this._init_others(); + this._init_plugins(); + var b = main.mode == 'editor'; + // 初始化画布 + for (var name in core.canvas) { + if (core.domStyle.hdCanvas.indexOf(name) >= 0) + core.maps._setHDCanvasSize(core.canvas[name], b ? core.__PIXELS__ : core._PX_, b ? core.__PIXELS__ : core._PY_); + else { + core.canvas[name].canvas.width = (b ? core.__PIXELS__ : core._PX_); + core.canvas[name].canvas.height = (b ? core.__PIXELS__ : core._PY_); + } + } + + core.loader._load(function () { + core.extensions._load(function () { + core._afterLoadResources(callback); + }); + }); + core.dom.musicBtn.style.display = 'block'; + core.setMusicBtn(); +} + +core.prototype._init_flags = function () { + core.flags = core.clone(core.data.flags); + core.values = core.clone(core.data.values); + core.firstData = core.clone(core.data.firstData); + this._init_sys_flags(); + + // 让你总是拼错! + window.on = true; + window.off = false; + window.ture = true; + window.flase = false; + + core.dom.versionLabel.innerText = core.firstData.version; + core.dom.logoLabel.innerText = core.firstData.title; + document.title = core.firstData.title + " - HTML5魔塔"; + document.getElementById("startLogo").innerText = core.firstData.title; + (core.firstData.shops || []).forEach(function (t) { core.initStatus.shops[t.id] = t; }); + + core.maps._initFloors(); + // 初始化怪物、道具等 + core.material.enemys = core.enemys.getEnemys(); + core.material.items = core.items.getItems(); + core.material.icons = core.icons.getIcons(); + + // 初始化自动事件 + for (var floorId in core.floors) { + var autoEvents = core.floors[floorId].autoEvent || {}; + for (var loc in autoEvents) { + var locs = loc.split(","), x = parseInt(locs[0]), y = parseInt(locs[1]); + for (var index in autoEvents[loc]) { + var autoEvent = core.clone(autoEvents[loc][index]); + if (autoEvent && autoEvent.condition && autoEvent.data) { + autoEvent.floorId = floorId; + autoEvent.x = x; + autoEvent.y = y; + autoEvent.index = index; + autoEvent.symbol = floorId + "@" + x + "@" + y + "@" + index; + autoEvent.condition = core.replaceValue(autoEvent.condition); + autoEvent.data = core.precompile(autoEvent.data); + core.initStatus.autoEvents.push(autoEvent); + } + } + } + } + // 道具的穿上/脱下,视为自动事件 + for (var equipId in core.material.items) { + var equip = core.material.items[equipId]; + if (equip.cls != 'equips' || !equip.equip) continue; + if (!equip.equip.equipEvent && !equip.equip.unequipEvent) continue; + var equipFlag = '_equipEvent_' + equipId; + var autoEvent1 = { + symbol: "_equipEvent_" + equipId, + currentFloor: false, + multiExecute: true, + condition: "core.hasEquip('" + equipId + "') && !core.hasFlag('" + equipFlag + "')", + data: core.precompile([{ "type": "setValue", "name": "flag:" + equipFlag, "value": "true" }].concat(equip.equip.equipEvent || [])), + }; + var autoEvent2 = { + symbol: "_unequipEvent_" + equipId, + currentFloor: false, + multiExecute: true, + condition: "!core.hasEquip('" + equipId + "') && core.hasFlag('" + equipFlag + "')", + data: core.precompile([{ "type": "setValue", "name": "flag:" + equipFlag, "value": "null" }].concat(equip.equip.unequipEvent || [])), + }; + core.initStatus.autoEvents.push(autoEvent1); + core.initStatus.autoEvents.push(autoEvent2); + } + + core.initStatus.autoEvents.sort(function (e1, e2) { + if (e1.floorId == null) return 1; + if (e2.floorId == null) return -1; + if (e1.priority != e2.priority) return e2.priority - e1.priority; + if (e1.floorId != e2.floorId) return core.floorIds.indexOf(e1.floorId) - core.floorIds.indexOf(e2.floorId); + if (e1.x != e2.x) return e1.x - e2.x; + if (e1.y != e2.y) return e1.y - e2.y; + return e1.index - e2.index; + }) + +} + +core.prototype._init_sys_flags = function () { + if (core.flags.equipboxButton) core.flags.equipment = true; + core.flags.displayEnemyDamage = core.getLocalStorage('enemyDamage', true); + core.flags.displayCritical = core.getLocalStorage('critical', true); + core.flags.displayExtraDamage = core.getLocalStorage('extraDamage', true); + core.flags.enableEnemyPoint = core.getLocalStorage('enableEnemyPoint', core.flags.enableEnemyPoint); + core.flags.leftHandPrefer = core.getLocalStorage('leftHandPrefer', false); + core.flags.extraDamageType = core.getLocalStorage('extraDamageType', 2); + // 行走速度 + core.values.moveSpeed = core.getLocalStorage('moveSpeed', core.values.moveSpeed || 100); + core.values.floorChangeTime = core.getLocalStorage('floorChangeTime', core.values.floorChangeTime); + if (core.values.floorChangeTime == null) core.values.floorChangeTime = 500; + core.flags.enableHDCanvas = core.getLocalStorage('enableHDCanvas', !core.platform.isIOS); +} + +core.prototype._init_platform = function () { + core.platform.isOnline = location.protocol.indexOf("http") == 0; + if (!core.platform.isOnline) alert("请勿直接打开html文件!使用启动服务或者APP进行离线游戏。"); + window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; + core.musicStatus.bgmStatus = core.getLocalStorage('bgmStatus', true); + core.musicStatus.soundStatus = core.getLocalStorage('soundStatus', true); + //新增 userVolume 默认值0.09 + core.musicStatus.userVolume = core.getLocalStorage('userVolume', 0.09); + try { + core.musicStatus.audioContext = new window.AudioContext(); + core.musicStatus.gainNode = core.musicStatus.audioContext.createGain(); + core.musicStatus.gainNode.gain.value = core.musicStatus.userVolume; + core.musicStatus.gainNode.connect(core.musicStatus.audioContext.destination); + } catch (e) { + console.log("该浏览器不支持AudioContext"); + core.musicStatus.audioContext = null; + } + ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"].forEach(function (t) { + if (navigator.userAgent.indexOf(t) >= 0) { + if (t == 'iPhone' || t == 'iPad' || t == 'iPod') core.platform.isIOS = true; + if (t == 'Android') core.platform.isAndroid = true; + core.platform.isPC = false; + } + }); + core.platform.string = core.platform.isPC ? "PC" : core.platform.isAndroid ? "Android" : core.platform.isIOS ? "iOS" : ""; + core.platform.supportCopy = document.queryCommandSupported && document.queryCommandSupported("copy"); + var chrome = /Chrome\/(\d+)\./i.exec(navigator.userAgent); + if (chrome && parseInt(chrome[1]) >= 50) core.platform.isChrome = true; + core.platform.isSafari = /Safari/i.test(navigator.userAgent) && !/Chrome/i.test(navigator.userAgent); + core.platform.isQQ = /QQ/i.test(navigator.userAgent); + core.platform.isWeChat = /MicroMessenger/i.test(navigator.userAgent); + if (window.FileReader) { + core.platform.fileReader = new FileReader(); + core.platform.fileReader.onload = function () { + core.readFileContent(core.platform.fileReader.result); + }; + core.platform.fileReader.onerror = function () { + if (core.platform.errorCallback) + core.platform.errorCallback(); + } + } + + core.flags.enableHDCanvas = core.getLocalStorage('enableHDCanvas', !core.platform.isIOS); + if (main.mode != 'editor') { + core.domStyle.scale = core.getLocalStorage('scale', 1); + if (core.flags.enableHDCanvas) core.domStyle.ratio = Math.max(window.devicePixelRatio || 1, core.domStyle.scale); + } +} + +core.prototype._init_others = function () { + // 一些额外的东西 + core.material.groundCanvas = document.createElement('canvas').getContext('2d'); + core.material.groundCanvas.canvas.width = core.material.groundCanvas.canvas.height = 32; + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); + core.bigmap.tempCanvas = document.createElement('canvas').getContext('2d'); + core.bigmap.cacheCanvas = document.createElement('canvas').getContext('2d'); + core.loadImage("materials", 'fog', function (name, img) { core.animateFrame.weather.fog = img; }); + core.loadImage("materials", "cloud", function (name, img) { core.animateFrame.weather.cloud = img; }) + core.loadImage("materials", "sun", function (name, img) { core.animateFrame.weather.sun = img; }) + core.loadImage("materials", 'keyboard', function (name, img) { core.material.images.keyboard = img; }); + // 记录存档编号 + core.saves.saveIndex = core.getLocalStorage('saveIndex', 1); + core.control.getSaveIndexes(function (indexes) { core.saves.ids = indexes; }); +} + +core.prototype._afterLoadResources = function (callback) { + // 初始化地图 + core.initStatus.maps = core.maps._initMaps(); + core.control._setRequestAnimationFrame(); + // 图片裁剪 + (main.splitImages || []).forEach(function (one) { + var name = core.getMappedName(one.name); + if (!core.material.images.images[name]) { + console.warn('找不到图片:' + name + ',无法裁剪'); + return; + } + if (!name.endsWith('.png')) { + console.warn('无法裁剪非png格式图片:' + name); + return; + } + var arr = core.splitImage(core.material.images.images[name], one.width, one.height); + for (var i = 0; i < arr.length; ++i) { + core.material.images.images[(one.prefix || "") + i + '.png'] = arr[i]; + } + }); + + if (core.plugin._afterLoadResources) + core.plugin._afterLoadResources(); + core.showStartAnimate(); + if (callback) callback(); +} + +core.prototype._init_plugins = function () { + core.plugin = new function () { }; + + for (var name in plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1) { + if (plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name] instanceof Function) { + try { + plugins_bb40132b_638b_4a9f_b028_d3fe47acc8d1[name].apply(core.plugin); + } + catch (e) { + console.error(e); + console.error("无法初始化插件" + name); + } + } + } + + core._forwardFunc("plugin"); +} + +core.prototype._forwardFuncs = function () { + for (var i = 0; i < main.loadList.length; ++i) { + var name = main.loadList[i]; + if (name == 'core') continue; + this._forwardFunc(name); + } +} + +core.prototype._forwardFunc = function (name, funcname) { + if (funcname == null) { + for (funcname in core[name]) { + if (funcname.charAt(0) != "_" && core[name][funcname] instanceof Function) { + this._forwardFunc(name, funcname); + } + } + return; + } + + if (core[funcname]) { + console.error("ERROR: 无法转发 " + name + " 中的函数 " + funcname + " 到 core 中!同名函数已存在。"); + return; + } + 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, ', '); + // core[funcname] = new Function(parameters, "return core."+name+"."+funcname+"("+parameters+");"); + eval("core." + funcname + " = function (" + parameters + ") {\n\treturn core." + name + "." + funcname + ".apply(core." + name + ", arguments);\n}"); +} + +core.prototype.doFunc = function (func, _this) { + if (typeof func == 'string') { + func = core.plugin[func]; + _this = core.plugin; + } + return func.apply(_this, Array.prototype.slice.call(arguments, 2)); +} + +// return new Core(); + +// })(); +var core = new core(); diff --git a/libs/data.js b/libs/data.js new file mode 100644 index 0000000..8fbc63f --- /dev/null +++ b/libs/data.js @@ -0,0 +1,13 @@ + +"use strict"; + +function data () { + this._init(); +} + +data.prototype._init = function () { + this.firstData = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.firstData; + this.values = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.values; + this.flags = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d.flags; + //delete(data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d); +} \ No newline at end of file diff --git a/libs/enemys.js b/libs/enemys.js new file mode 100644 index 0000000..122657a --- /dev/null +++ b/libs/enemys.js @@ -0,0 +1,523 @@ + +"use strict"; + +function enemys () { + this._init(); +} + +////// 初始化 ////// +enemys.prototype._init = function () { + this.enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80; + this.enemydata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.enemys; + for (var enemyId in this.enemys) { + this.enemys[enemyId].id = enemyId; + } + if (main.mode == 'play') { + this.enemydata.hasSpecial = function (a, b) { + return core.enemys.hasSpecial(a, b) + }; + } +} + +enemys.prototype.getEnemys = function () { + var enemys = core.clone(this.enemys); + var enemyInfo = core.getFlag('enemyInfo'); + if (enemyInfo) { + for (var id in enemyInfo) { + for (var name in enemyInfo[id]) { + enemys[id][name] = core.clone(enemyInfo[id][name]); + } + } + } + // 将所有怪物的各项属性映射到朝下的 + for (var id in enemys) { + if (enemys[id].faceIds) { + var downId = enemys[id].faceIds.down; + if (downId != null && downId != id && enemys[downId]) { + enemys[id] = { id: id }; + for (var property in enemys[downId]) { + if (property != 'id' && enemys[downId].hasOwnProperty(property)) { + (function (id, downId, property) { + Object.defineProperty(enemys[id], property, { + get: function () { return enemys[downId][property] }, + set: function (v) { enemys[downId][property] = v }, + enumerable: true + }) + })(id, downId, property); + } + } + } + } + } + return enemys; +} + +////// 判断是否含有某特殊属性 ////// +enemys.prototype.hasSpecial = function (special, test) { + if (special == null) return false; + + if (special instanceof Array) { + return special.indexOf(test) >= 0; + } + + if (typeof special == 'number') { + return special === test; + } + + if (typeof special == 'string') { + return this.hasSpecial(core.material.enemys[special], test); + } + + if (special.special != null) { + return this.hasSpecial(special.special, test); + } + + return false; +} + +enemys.prototype.getSpecials = function () { + return this.enemydata.getSpecials(); +} + +////// 获得所有特殊属性的名称 ////// +enemys.prototype.getSpecialText = function (enemy) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (!enemy) return []; + var special = enemy.special; + var text = []; + + var specials = this.getSpecials(); + if (specials) { + for (var i = 0; i < specials.length; i++) { + if (this.hasSpecial(special, specials[i][0])) + text.push(this._calSpecialContent(enemy, specials[i][1])); + } + } + return text; +} + +////// 获得所有特殊属性的颜色 ////// +enemys.prototype.getSpecialColor = function (enemy) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (!enemy) return []; + var special = enemy.special; + var colors = []; + + var specials = this.getSpecials(); + if (specials) { + for (var i = 0; i < specials.length; i++) { + if (this.hasSpecial(special, specials[i][0])) + colors.push(specials[i][3] || null); + } + } + return colors; + +} + +////// 获得所有特殊属性的额外标记 ////// +enemys.prototype.getSpecialFlag = function (enemy) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (!enemy) return []; + var special = enemy.special; + var flag = 0; + + var specials = this.getSpecials(); + if (specials) { + for (var i = 0; i < specials.length; i++) { + if (this.hasSpecial(special, specials[i][0])) + flag |= (specials[i][4] || 0); + } + } + return flag; +} + +////// 获得每个特殊属性的说明 ////// +enemys.prototype.getSpecialHint = function (enemy, special) { + var specials = this.getSpecials(); + + if (special == null) { + if (specials == null) return []; + var hints = []; + for (var i = 0; i < specials.length; i++) { + if (this.hasSpecial(enemy, specials[i][0])) + hints.push("\r[" + core.arrayToRGBA(specials[i][3] || "#FF6A6A") + "]\\d" + this._calSpecialContent(enemy, specials[i][1]) + + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2])); + } + return hints; + } + + if (specials == null) return ""; + for (var i = 0; i < specials.length; i++) { + if (special == specials[i][0]) + return "\r[#FF6A6A]\\d" + this._calSpecialContent(enemy, specials[i][1]) + ":\\d\r[]" + this._calSpecialContent(enemy, specials[i][2]); + } + return ""; +} + +enemys.prototype._calSpecialContent = function (enemy, content) { + if (typeof content == 'string') return content; + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (content instanceof Function) { + return content(enemy); + } + return ""; +} + +////// 获得某个点上某个怪物的某项属性 ////// +enemys.prototype.getEnemyValue = function (enemy, name, x, y, floorId) { + floorId = floorId || core.status.floorId; + if ((((flags.enemyOnPoint || {})[floorId] || {})[x + "," + y] || {})[name] != null) { + return flags.enemyOnPoint[floorId][x + "," + y][name]; + } + if (enemy == null) { + var block = core.getBlock(x, y, floorId); + if (block == null) return null; + enemy = core.material.enemys[block.event.id]; + } + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (enemy == null) return null; + return enemy[name]; +} + +////// 能否获胜 ////// +enemys.prototype.canBattle = function (enemy, x, y, floorId) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + var damage = this.getDamage(enemy, x, y, floorId); + return damage != null && damage < core.status.hero.hp; +} + +enemys.prototype.getDamageString = function (enemy, x, y, floorId) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + var damage = this.getDamage(enemy, x, y, floorId); + + var color = '#000000'; + + if (damage == null) { + damage = "???"; + color = '#FF2222'; + } + else { + if (damage <= 0) color = '#11FF11'; + else if (damage < core.status.hero.hp / 3) color = '#FFFFFF'; + else if (damage < core.status.hero.hp * 2 / 3) color = '#FFFF00'; + else if (damage < core.status.hero.hp) color = '#FF9933'; + else color = '#FF2222'; + + damage = core.formatBigNumber(damage, true); + if (core.enemys.hasSpecial(enemy, 19)) + damage += "+"; + if (core.enemys.hasSpecial(enemy, 21)) + damage += "-"; + if (core.enemys.hasSpecial(enemy, 11)) + damage += "^"; + } + + return { + "damage": damage, + "color": color + }; +} + +////// 接下来N个临界值和临界减伤计算 ////// +enemys.prototype.nextCriticals = function (enemy, number, x, y, floorId) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + number = number || 1; + + var specialCriticals = this._nextCriticals_special(enemy, number, x, y, floorId); + if (specialCriticals != null) return specialCriticals; + var info = this.getDamageInfo(enemy, null, x, y, floorId); + if (info == null) { // 如果未破防... + var overAtk = this._nextCriticals_overAtk(enemy, x, y, floorId); + if (overAtk == null) return []; + if (typeof overAtk[1] == 'number') return [[overAtk[0], -overAtk[1]]]; + info = overAtk[1]; + info.__over__ = true; + info.__overAtk__ = overAtk[0]; + } + + if (typeof info == 'number') return [[0, 0]]; + if (info.damage <= 0 && !core.flags.enableNegativeDamage) { + return [[info.__overAtk__ || 0, 0]]; + } + + if (core.flags.useLoop) { + if (core.status.hero.atk <= (main.criticalUseLoop || 1)) { + return this._nextCriticals_useLoop(enemy, info, number, x, y, floorId); + } + else { + return this._nextCriticals_useBinarySearch(enemy, info, number, x, y, floorId); + } + } + else { + return this._nextCriticals_useTurn(enemy, info, number, x, y, floorId); + } +} + +/// 未破防临界采用二分计算 +enemys.prototype._nextCriticals_overAtk = function (enemy, x, y, floorId) { + var calNext = function (currAtk, maxAtk) { + var start = currAtk, end = maxAtk; + if (start > end) return null; + + while (start < end) { + var mid = Math.floor((start + end) / 2); + if (mid - start > end - mid) mid--; + var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": mid }, x, y, floorId); + if (nextInfo != null) end = mid; + else start = mid + 1; + } + var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": start }, x, y, floorId); + return nextInfo == null ? null : [start - core.status.hero.atk, nextInfo]; + } + return calNext(core.status.hero.atk + 1, + core.getEnemyValue(enemy, 'hp', x, y, floorId) + core.getEnemyValue(enemy, 'def', x, y, floorId)); +} + +enemys.prototype._nextCriticals_special = function (enemy, number, x, y, floorId) { + if (this.hasSpecial(enemy.special, 10) || this.hasSpecial(enemy.special, 3)) + return []; // 模仿or坚固临界 + return null; +} + +enemys.prototype._nextCriticals_useLoop = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, pre = info.damage; + var list = []; + var start_atk = hero_atk; + if (info.__over__) { + start_atk += info.__overAtk__; + list.push([info.__overAtk__, -info.damage]); + } + for (var atk = start_atk + 1; atk <= mon_hp + mon_def; atk++) { + var nextInfo = this.getDamageInfo(enemy, { "atk": atk }, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) break; + if (pre > nextInfo.damage) { + pre = nextInfo.damage; + list.push([atk - hero_atk, info.damage - nextInfo.damage]); + if (nextInfo.damage <= 0 && !core.flags.enableNegativeDamage) break; + if (list.length >= number) break; + } + } + if (list.length == 0) list.push([0, 0]); + return list; +} + +enemys.prototype._nextCriticals_useBinarySearch = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, pre = info.damage; + var list = []; + var start_atk = hero_atk; + if (info.__over__) { + start_atk += info.__overAtk__; + list.push([info.__overAtk__, -info.damage]); + } + var calNext = function (currAtk, maxAtk) { + var start = Math.floor(currAtk), end = Math.floor(maxAtk); + if (start > end) return null; + + while (start < end) { + var mid = Math.floor((start + end) / 2); + if (mid - start > end - mid) mid--; + var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": mid }, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) return null; + if (pre > nextInfo.damage) end = mid; + else start = mid + 1; + } + var nextInfo = core.enemys.getDamageInfo(enemy, { "atk": start }, x, y, floorId); + return nextInfo == null || (typeof nextInfo == 'number') || nextInfo.damage >= pre ? null : [start, nextInfo.damage]; + } + var currAtk = start_atk; + while (true) { + var next = calNext(currAtk + 1, mon_hp + mon_def, pre); + if (next == null) break; + currAtk = next[0]; + pre = next[1]; + list.push([currAtk - hero_atk, info.damage - pre]); + if (pre <= 0 && !core.flags.enableNegativeDamage) break; + if (list.length >= number) break; + } + if (list.length == 0) list.push([0, 0]); + return list; +} + +enemys.prototype._nextCriticals_useTurn = function (enemy, info, number, x, y, floorId) { + var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = info.mon_def, turn = info.turn; + // ------ 超大回合数强制使用二分算临界 + // 以避免1攻10e回合,2攻5e回合导致下述循环卡死问题 + if (turn >= 1e6) { // 100w回合以上强制二分计算临界 + return this._nextCriticals_useBinarySearch(enemy, info, number, x, y, floorId); + } + var list = [], pre = null; + var start_atk = hero_atk; + if (info.__over__) { + start_atk += info.__overAtk__; + list.push([info.__overAtk__, -info.damage]); + } + for (var t = turn - 1; t >= 1; t--) { + var nextAtk = Math.ceil(mon_hp / t) + mon_def; + // 装备提升比例的计算临界 + nextAtk = Math.ceil(nextAtk / core.getBuff('atk')); + if (nextAtk <= start_atk) break; + if (nextAtk != pre) { + var nextInfo = this.getDamageInfo(enemy, { "atk": nextAtk }, x, y, floorId); + if (nextInfo == null || (typeof nextInfo == 'number')) break; + list.push([nextAtk - hero_atk, Math.floor(info.damage - nextInfo.damage)]); + if (nextInfo.damage <= 0 && !core.flags.enableNegativeDamage) break; + pre = nextAtk; + } + if (list.length >= number) + break; + } + if (list.length == 0) list.push([0, 0]); + return list; +} + +////// N防减伤计算 ////// +enemys.prototype.getDefDamage = function (enemy, k, x, y, floorId) { + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + k = k || 1; + var nowDamage = this._getDamage(enemy, null, x, y, floorId); + var nextDamage = this._getDamage(enemy, { "def": core.status.hero.def + k }, x, y, floorId); + if (nowDamage == null || nextDamage == null) return "???"; + return nowDamage - nextDamage; +} + +enemys.prototype.getEnemyInfo = function (enemy, hero, x, y, floorId) { + if (enemy == null) return null; + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + return this.enemydata.getEnemyInfo(enemy, hero, x, y, floorId) +} + +////// 获得战斗伤害信息(实际伤害计算函数) ////// +enemys.prototype.getDamageInfo = function (enemy, hero, x, y, floorId) { + if (enemy == null) return null; + // 移动到了脚本编辑 - getDamageInfo中 + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + return this.enemydata.getDamageInfo(enemy, hero, x, y, floorId); +} + +////// 获得在某个勇士属性下怪物伤害 ////// +enemys.prototype.getDamage = function (enemy, x, y, floorId) { + return this._getDamage(enemy, null, x, y, floorId); +} + +enemys.prototype._getDamage = function (enemy, hero, x, y, floorId) { + if (enemy == null) enemy = core.getBlockId(x, y, floorId); + if (typeof enemy == 'string') enemy = core.material.enemys[enemy]; + if (enemy == null) return null; + + var info = this.getDamageInfo(enemy, hero, x, y, floorId); + if (info == null) return null; + if (typeof info == 'number') return info; + return info.damage; +} + +////// 获得当前楼层的怪物列表 ////// +enemys.prototype.getCurrentEnemys = function (floorId) { + floorId = floorId || core.status.floorId; + var enemys = [], used = {}; + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (!block.disable && block.event.cls.indexOf('enemy') == 0) { + this._getCurrentEnemys_addEnemy(block.event.id, enemys, used, block.x, block.y, floorId); + } + }, this); + return this._getCurrentEnemys_sort(enemys); +} + +enemys.prototype._getCurrentEnemys_getEnemy = function (enemyId) { + var enemy = core.material.enemys[enemyId]; + if (!enemy) return null; + + // 检查朝向;displayIdInBook + return core.material.enemys[enemy.displayIdInBook] || core.material.enemys[(enemy.faceIds || {}).down] || enemy; +} + +enemys.prototype._getCurrentEnemys_addEnemy = function (enemyId, enemys, used, x, y, floorId) { + var enemy = this._getCurrentEnemys_getEnemy(enemyId); + if (enemy == null) return; + + var id = enemy.id; + + var enemyInfo = this.getEnemyInfo(enemy, null, null, null, floorId); + var locEnemyInfo = this.getEnemyInfo(enemy, null, x, y, floorId); + + if (!core.flags.enableEnemyPoint || + (locEnemyInfo.atk == enemyInfo.atk && locEnemyInfo.def == enemyInfo.def && locEnemyInfo.hp == enemyInfo.hp)) { + x = null; + y = null; + } else { + // 检查enemys里面是否使用了存在的内容 + for (var i = 0; i < enemys.length; ++i) { + var one = enemys[i]; + if (id == one.id && one.locs != null && + locEnemyInfo.atk == one.atk && locEnemyInfo.def == one.def && locEnemyInfo.hp == one.hp) { + one.locs.push([x, y]); + return; + } + } + enemyInfo = locEnemyInfo; + } + var id = enemy.id + ":" + x + ":" + y; + if (used[id]) return; + used[id] = true; + + var specialText = core.enemys.getSpecialText(enemy); + var specialColor = core.enemys.getSpecialColor(enemy); + + var critical = this.nextCriticals(enemy, 1, x, y, floorId); + if (critical.length > 0) critical = critical[0]; + + var e = core.clone(enemy); + for (var v in enemyInfo) { + e[v] = enemyInfo[v]; + } + if (x != null && y != null) { + e.locs = [[x, y]]; + } + e.name = core.getEnemyValue(enemy, 'name', x, y, floorId); + e.specialText = specialText; + e.specialColor = specialColor; + e.damage = this.getDamage(enemy, x, y, floorId); + e.critical = critical[0]; + e.criticalDamage = critical[1]; + e.defDamage = this._getCurrentEnemys_addEnemy_defDamage(enemy, x, y, floorId); + enemys.push(e); +} + +enemys.prototype._getCurrentEnemys_addEnemy_defDamage = function (enemy, x, y, floorId) { + var ratio = core.status.maps[floorId || core.status.floorId].ratio || 1; + return this.getDefDamage(enemy, ratio, x, y, floorId); +} + +enemys.prototype._getCurrentEnemys_sort = function (enemys) { + return enemys.sort(function (a, b) { + if (a.damage == b.damage) { + return a.money - b.money; + } + if (a.damage == null) { + return 1; + } + if (b.damage == null) { + return -1; + } + return a.damage - b.damage; + }); +} + +enemys.prototype.hasEnemyLeft = function (enemyId, floorId) { + if (floorId == null) floorId = core.status.floorId; + if (!(floorId instanceof Array)) floorId = [floorId]; + var enemyMap = {}; + if (enemyId instanceof Array) enemyId.forEach(function (v) { enemyMap[v] = true; }); + else if (enemyId) enemyMap[enemyId] = true; + else enemyMap = null; + for (var i = 0; i < floorId.length; i++) { + core.extractBlocks(floorId[i]); + var mapBlocks = core.status.maps[floorId[i]].blocks; + for (var b = 0; b < mapBlocks.length; b++) { + if (!mapBlocks[b].disable && mapBlocks[b].event.cls.indexOf('enemy') === 0) { + if (enemyMap === null || enemyMap[core.getFaceDownId(mapBlocks[b])]) return true; + } + } + } + return false; +} diff --git a/libs/events.js b/libs/events.js new file mode 100644 index 0000000..66d9954 --- /dev/null +++ b/libs/events.js @@ -0,0 +1,3788 @@ + +"use strict"; + +function events () { + this._init(); +} + +////// 初始化 ////// +events.prototype._init = function () { + this.eventdata = functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a.events; + this.commonEvent = events_c12a15a8_c380_4b28_8144_256cba95f760.commonEvent; + this.systemEvents = {}; + this.actions = {}; +} + +// ------ 初始化,开始和结束 ------ // + +/// 初始化游戏 +events.prototype.resetGame = function (hero, hard, floorId, maps, values) { + this.eventdata.resetGame(hero, hard, floorId, maps, values); +} + +////// 游戏开始事件 ////// +events.prototype.startGame = function (hard, seed, route, callback) { + main.dom.levelChooseButtons.style.display = 'none'; + main.dom.startButtonGroup.style.display = 'none'; + hard = hard || ""; + + if (main.mode != 'play') return; + + // 无动画的开始游戏 + if (core.flags.startUsingCanvas || route != null) { + core.dom.startPanel.style.display = 'none'; + this._startGame_start(hard, seed, route, callback); + } + else { + core.hideStartAnimate(function () { + core.events._startGame_start(hard, seed, route, callback); + }); + } +} + +events.prototype._startGame_start = function (hard, seed, route, callback) { + core.resetGame(core.firstData.hero, hard, null, core.cloneArray(core.initStatus.maps)); + core.setHeroLoc('x', -1); + core.setHeroLoc('y', -1); + + if (seed != null && seed > 0) { + core.setFlag('__seed__', seed); + core.setFlag('__rand__', seed); + } + else core.utils.__init_seed(); + core.clearStatusBar(); + + var todo = []; + if (core.flags.startUsingCanvas) { + core.hideStatusBar(); + core.dom.musicBtn.style.display = 'block'; + core.push(todo, core.firstData.startCanvas); + } + core.push(todo, { "type": "function", "function": "function() { core.events._startGame_setHard(); }" }) + core.push(todo, core.firstData.startText); + this.insertAction(todo, null, null, function () { + core.events._startGame_afterStart(callback); + }); + + if (route != null) core.startReplay(route); +} + +events.prototype._startGame_setHard = function () { + // 根据难度设置flag:hard + // 这一段应当在startCanvas之后,startText之前做 + var hardValue = 0; + var hardColor = 'red'; + main.levelChoose.forEach(function (one) { + if (one.name == core.status.hard) { + hardValue = one.hard; + hardColor = core.arrayToRGBA(one.color || [255, 0, 0, 1]); + core.insertAction(one.action); + } + }); + core.setFlag('hard', hardValue || 0); + core.setFlag('__hardColor__', hardColor); +} + +events.prototype._startGame_afterStart = function (callback) { + core.ui.closePanel(); + core.changeFloor(core.firstData.floorId, null, core.firstData.hero.loc, null, function () { + // 插入一个空事件避免直接回放录像出错 + core.insertAction([]); + if (callback) callback(); + }); + this._startGame_upload(); +} + +events.prototype._startGame_upload = function () { + // Upload + var formData = new FormData(); + formData.append('type', 'people'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.string); + formData.append('hard', core.encodeBase64(core.status.hard)); + formData.append('hardCode', core.getFlag('hard', 0)); + formData.append('base64', 1); + + core.utils.http("POST", "/games/upload.php", formData); +} + +////// 游戏获胜事件 ////// +events.prototype.win = function (reason, norank, noexit) { + if (!noexit) core.status.gameOver = true; + return this.eventdata.win(reason, norank, noexit); +} + +////// 游戏失败事件 ////// +events.prototype.lose = function (reason) { + if (core.isReplaying()) return core.control._replay_error(reason, function () { core.lose(reason); }); + core.status.gameOver = true; + return this.eventdata.lose(reason); +} + +////// 游戏结束 ////// +events.prototype.gameOver = function (ending, fromReplay, norank) { + if (!core.status.extraEvent) { + core.clearMap('all'); + core.deleteAllCanvas(); + core.dom.gif2.innerHTML = ""; + core.setWeather(); + } + core.ui.closePanel(); + + if (main.isCompetition && ending != null) { + if (ending == "") ending = "恭喜通关"; + ending += "[比赛]"; + } + + var reason = null; + if (fromReplay) reason = "录像回放完毕!"; + else if (core.hasFlag("debug")) reason = "\t[系统提示]调试模式下无法上传成绩"; + + if (reason != null) + core.drawText(reason, core.restart); + else + this._gameOver_confirmUpload(ending, norank); +} + +events.prototype._gameOver_confirmUpload = function (ending, norank) { + core.ui.closePanel(); + + if (ending == null) { + this._gameOver_confirmDownload(ending); + return; + } + core.ui.drawConfirmBox("你想记录你的ID和成绩吗?", function () { + if (main.isCompetition) { + core.events._gameOver_doUpload("", ending, norank); + } + else { + var id = core.getCookie('id') || ""; + var hint = "请输入你的ID:\n(登录状态下输入数字用户编号可成为蓝名成绩并计入用户通关数)"; + if (id) hint = "请输入你的ID:\n(输入数字用户编号" + id + "可成为蓝名成绩并计入用户通关数)"; + core.myprompt(hint, id, function (username) { + core.events._gameOver_doUpload(username, ending, norank); + }); + } + }, function () { + if (main.isCompetition) + core.events._gameOver_confirmDownload(ending); + else + core.events._gameOver_doUpload(null, ending, norank); + }) +} + +events.prototype._gameOver_doUpload = function (username, ending, norank) { + var hp = core.status.hero.hp; + if (username == null) hp = 1; + core.ui.closePanel(); + // upload + var formData = new FormData(); + formData.append('type', 'score'); + formData.append('name', core.firstData.name); + formData.append('version', core.firstData.version); + formData.append('platform', core.platform.string); + formData.append('hard', core.encodeBase64(core.status.hard)); + formData.append('username', core.encodeBase64(username || "")); + formData.append('ending', core.encodeBase64(ending)); + formData.append('lv', core.status.hero.lv); + formData.append('hp', Math.min(hp, Math.pow(2, 63))); + formData.append('atk', core.status.hero.atk); + formData.append('def', core.status.hero.def); + formData.append('mdef', core.status.hero.mdef); + formData.append('money', core.status.hero.money); + formData.append('experience', core.status.hero.exp); + formData.append('steps', core.status.hero.steps); + formData.append('norank', norank ? 1 : 0); + formData.append('seed', core.getFlag('__seed__')); + formData.append('totalTime', Math.floor(core.status.hero.statistics.totalTime / 1000)); + formData.append('route', core.encodeRoute(core.status.route)); + formData.append('base64', 1); + + if (main.isCompetition) + core.http("POST", "/games/competition/upload.php", formData); + else + core.http("POST", "/games/upload.php", formData); + + core.events._gameOver_confirmDownload(ending); +} + +events.prototype._gameOver_confirmDownload = function (ending) { + core.ui.closePanel(); + core.ui.drawConfirmBox("你想下载录像吗?", function () { + var obj = { + 'name': core.firstData.name, + 'version': core.firstData.version, + 'hard': core.status.hard, + 'seed': core.getFlag('__seed__'), + 'route': core.encodeRoute(core.status.route) + } + core.download(core.firstData.name + "_" + core.formatDate2(new Date()) + ".h5route", + LZString.compressToBase64(JSON.stringify(obj))); + core.events._gameOver_askRate(ending); + }, function () { + core.events._gameOver_askRate(ending); + }); +} + +events.prototype._gameOver_askRate = function (ending) { + core.ui.closePanel(); + + // 继续接下来的事件 + if (core.status.extraEvent) { + core.status.event = core.status.extraEvent; + delete core.status.extraEvent; + core.lockControl(); + core.doAction(); + return; + } + + if (ending == null) { + if (!core.hasSave(0)) { + core.ui.closePanel(); + core.restart(); + return; + } + + core.status.event.selection = 0; + core.ui.drawConfirmBox("你想读取自动存档么?", function () { + core.ui.closePanel(); + core.doSL("autoSave", "load"); + }, function () { + core.ui.closePanel(); + core.restart(); + }); + return; + } + + core.ui.drawConfirmBox("恭喜通关!你想查看榜单、评论,\n以及评分和标色投票吗?", function () { + if (core.platform.isPC) { + window.open("/tower/?name=" + core.firstData.name, "_blank"); + core.restart(); + } + else { + window.location.href = "/tower/?name=" + core.firstData.name; + } + }, function () { + core.restart(); + }); +} + +////// 重新开始游戏;此函数将回到标题页面 ////// +events.prototype.restart = function () { + core.showStartAnimate(); + core.playBgm(main.startBgm); +} + +////// 询问是否需要重新开始 ////// +events.prototype.confirmRestart = function () { + core.playSound('打开界面'); + core.status.event.selection = 1; + core.ui.drawConfirmBox("你确定要返回标题页面吗?", function () { + core.playSound('确定'); + core.ui.closePanel(); + core.restart(); + }, function () { + core.playSound('取消'); + core.ui.closePanel(); + }); +} + +// ------ 系统事件的处理 ------ // + +////// 注册一个系统事件 ////// +// type为事件名,func为事件的处理函数,可接受(data, callback)参数 +events.prototype.registerSystemEvent = function (type, func) { + this.systemEvents[type] = func; +} + +////// 注销一个系统事件 ////// +events.prototype.unregisterSystemEvent = function (type) { + delete this.systemEvents[type]; +} + +////// 执行一个系统事件 ////// +events.prototype.doSystemEvent = function (type, data, callback) { + core.clearRouteFolding(); + if (this.systemEvents[type]) { + try { + return core.doFunc(this.systemEvents[type], this, data, callback); + } + catch (e) { + console.error(e); + console.error("ERROR in systemEvents[" + type + "]"); + } + } + if (this["_sys_" + type]) return this["_sys_" + type](data, callback); + console.error("未知的系统事件: " + type + "!"); + if (callback) callback(); +} + +////// 触发(x,y)点的事件 ////// +events.prototype.trigger = function (x, y, callback) { + var _executeCallback = function () { + // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) + // 所以这里强制callback被异步触发 + if (callback) { + setTimeout(callback, 1); // +1是为了录像检测系统 + } + return; + } + if (core.status.gameOver) return _executeCallback(); + if (core.status.event.id == 'action') { + core.insertAction({ "type": "function", "function": "function () { core.events._trigger_inAction(" + x + "," + y + "); }", "async": true }, + null, null, null, true); + return _executeCallback(); + } + if (core.status.event.id) return _executeCallback(); + + var block = core.getBlock(x, y); + if (block == null) return _executeCallback(); + + // 执行该点的脚本 + if (block.event.script) { + core.clearRouteFolding(); + try { + eval(block.event.script); + } catch (ee) { console.error(ee) } + } + + // 碰触事件 + if (block.event.event) { + core.clearRouteFolding(); + core.insertAction(block.event.event, block.x, block.y); + // 不再执行该点的系统事件 + return _executeCallback(); + } + + if (block.event.trigger && block.event.trigger != 'null') { + var noPass = block.event.noPass, trigger = block.event.trigger; + if (noPass) core.clearAutomaticRouteNode(x, y); + + // 转换楼层能否穿透 + if (trigger == 'changeFloor' && !noPass && this._trigger_ignoreChangeFloor(block)) + return _executeCallback(); + core.status.automaticRoute.moveDirectly = false; + this.doSystemEvent(trigger, block); + } + return _executeCallback(); +} + +events.prototype._trigger_inAction = function (x, y) { + if (core.status.gameOver || core.status.event.id != 'action') return; + + var block = core.getBlock(x, y); + if (block == null) return core.doAction(); + + // 执行该点的脚本 + try { + eval(block.event.script); + } catch (ee) { console.error(ee) } + + // 碰触事件 + if (block.event.event) { + core.clearRouteFolding(); + core.insertAction(block.event.event, block.x, block.y); + // 不再执行该点的系统事件 + return core.doAction(); + } + + if (block.event.trigger && block.event.trigger != 'null') { + this.setEvents(null, x, y); + if (block.event.trigger == 'action') { + this.insertAction(block.event.data); + } + else { + this.doSystemEvent(block.event.trigger, block, core.doAction); + return; + } + } + return core.doAction(); +} + +events.prototype._trigger_ignoreChangeFloor = function (block) { + var able = core.flags.ignoreChangeFloor; + if (block.event.data && block.event.data.ignoreChangeFloor != null) + able = block.event.data.ignoreChangeFloor; + if (able) { + if (core.isReplaying()) { + if (core.status.replay.toReplay[0] == 'no') { + core.status.replay.toReplay.shift(); + core.status.route.push("no"); + return true; + } + } + else if (core.status.automaticRoute.autoHeroMove + || core.status.automaticRoute.autoStep < core.status.automaticRoute.autoStepRoutes.length) { + core.status.route.push("no"); + return true; + } + } + return false; +} + +events.prototype._sys_battle = function (data, callback) { + // 检查是否需要改变朝向 + /* if (data.x == core.nextX() && data.y == core.nextY()) { + var dir = core.turnDirection(":back"); + var id = data.event.id, toId = (data.event.faceIds || {})[dir]; + if (toId && id != toId) { + var number = core.getNumberById(toId); + if (number > 0) + core.setBlock(number, data.x, data.y); + } + } */ + + // 检查战前事件 + var beforeBattle = []; + core.push(beforeBattle, core.floors[core.status.floorId].beforeBattle[data.x + "," + data.y]); + core.push(beforeBattle, (core.material.enemys[data.event.id] || {}).beforeBattle); + if (beforeBattle.length > 0) { + core.push(beforeBattle, [{ "type": "battle", "x": data.x, "y": data.y }]); + core.clearContinueAutomaticRoute(); + + // 自动存档 + var inAction = core.status.event.id == 'action'; + if (inAction) { + core.insertAction(beforeBattle, data.x, data.y); + core.doAction(); + } else { + core.autosave(true); + core.insertAction(beforeBattle, data.x, data.y, callback); + } + } else { + this.battle(data.event.id, data.x, data.y, false, callback); + } +} + +////// 战斗 ////// +events.prototype.battle = function (id, x, y, force, callback) { + core.saveAndStopAutomaticRoute(); + id = id || core.getBlockId(x, y); + if (!id) return core.clearContinueAutomaticRoute(callback); + // 非强制战斗 + if (!core.enemys.canBattle(id, x, y) && !force && !core.status.event.id) { + core.stopSound(); + core.playSound('操作失败'); + core.drawTip("你打不过此怪物!", id); + return core.clearContinueAutomaticRoute(callback); + } + // 自动存档 + if (!core.status.event.id) core.autosave(true); + // 战前事件 + if (!this.beforeBattle(id, x, y)) + return core.clearContinueAutomaticRoute(callback); + // 战后事件 + this.afterBattle(id, x, y); + if (callback) callback(); +} + +////// 战斗前触发的事件 ////// +events.prototype.beforeBattle = function (enemyId, x, y) { + return this.eventdata.beforeBattle(enemyId, x, y) +} + +////// 战斗结束后触发的事件 ////// +events.prototype.afterBattle = function (enemyId, x, y) { + return this.eventdata.afterBattle(enemyId, x, y); +} + +events.prototype._sys_openDoor = function (data, callback) { + this.openDoor(data.x, data.y, true, function () { + core.replay(); + if (callback) callback(); + }); +} + +////// 开门 ////// +events.prototype.openDoor = function (x, y, needKey, callback) { + var block = core.getBlock(x, y); + core.saveAndStopAutomaticRoute(); + if (!this._openDoor_check(block, x, y, needKey)) { + var locked = core.status.lockControl; + core.waitHeroToStop(function () { + if (!locked) core.unlockControl(); + if (callback) callback(); + }); + return; + } + if (core.status.replay.speed == 24) { + core.status.replay.animate = true; + core.removeBlock(x, y); + setTimeout(function () { + core.status.replay.animate = false; + core.events.afterOpenDoor(block.event.id, x, y); + if (callback) callback(); + }, 1); // +1是为了录像检测系统 + } else { + this._openDoor_animate(block, x, y, callback); + } +} + +events.prototype._openDoor_check = function (block, x, y, needKey) { + var clearAndReturn = function () { + core.clearContinueAutomaticRoute(); + return false; + } + + if (block == null || block.event == null) return clearAndReturn(); + var id = block.event.id; + + // 是否存在门或暗墙 + if (core.material.icons.animates[id] == null && core.material.icons.npc48[id] == null) { + return clearAndReturn(); + } + + if (id == 'steelDoor' && core.flags.steelDoorWithoutKey) + needKey = false; + var doorInfo = block.event.doorInfo; + if (doorInfo == null) return clearAndReturn(); + // Check all keys + var keyInfo = doorInfo.keys || {}; + if (needKey) { + for (var keyName in keyInfo) { + var keyValue = keyInfo[keyName]; + if (keyName.endsWith(':o')) keyName = keyName.substring(0, keyName.length - 2); + + // --- 如果是一个不存在的道具,则直接认为无法开启 + if (!core.material.items[keyName]) { + core.stopSound(); + core.playSound('操作失败'); + core.drawTip("无法开启此门"); + return clearAndReturn(); + } + if (core.itemCount(keyName) < keyValue) { + core.stopSound(); + core.playSound('操作失败'); + core.drawTip("你的" + ((core.material.items[keyName] || {}).name || "钥匙") + "不足!", null, true); + return false; + } + } + if (!core.status.event.id) core.autosave(true); + for (var keyName in keyInfo) { + if (!keyName.endsWith(':o')) core.removeItem(keyName, keyInfo[keyName]); + } + } + core.playSound(doorInfo.openSound); + return true; +} + +events.prototype._openDoor_animate = function (block, x, y, callback) { + var blockInfo = core.getBlockInfo(block); + blockInfo.opacity = block.opacity; + blockInfo.filter = block.filter; + + var speed = (block.event.doorInfo.time || 160) / 4; + + var locked = core.status.lockControl; + core.lockControl(); + core.status.replay.animate = true; + core.removeBlock(x, y); + + blockInfo.posX = 0; + core.maps._drawBlockInfo(blockInfo, x, y); + + var cb = function () { + core.maps._removeBlockFromMap(core.status.floorId, block); + if (!locked) core.unlockControl(); + core.status.replay.animate = false; + core.events.afterOpenDoor(block.event.id, x, y); + if (callback) callback(); + } + + var animate = window.setInterval(function () { + blockInfo.posX++; + if (blockInfo.posX == 4) { + clearInterval(animate); + delete core.animateFrame.asyncId[animate]; + cb(); + return; + } + core.maps._drawBlockInfo(blockInfo, x, y); + }, core.status.replay.speed == 24 ? 1 : speed / Math.max(core.status.replay.speed, 1)); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +////// 开一个门后触发的事件 ////// +events.prototype.afterOpenDoor = function (doorId, x, y) { + return this.eventdata.afterOpenDoor(doorId, x, y); +} + +events.prototype._sys_getItem = function (data, callback) { + this.getItem(data.event.id, 1, data.x, data.y, false, callback); +} + +////// 获得某个物品 ////// +events.prototype.getItem = function (id, num, x, y, isGentleClick, callback) { + if (num == null) num = 1; + var itemCls = core.material.items[id].cls; + core.removeBlock(x, y); + core.items.getItemEffect(id, num); + var text = '获得 ' + core.material.items[id].name; + if (num > 1) text += "x" + num; + if (itemCls === 'items' && num == 1) text += core.items.getItemEffectTip(id); + core.drawTip(text, id); + + // --- 首次获得道具的提示 + if (!core.hasFlag("__itemHint__")) core.setFlag("__itemHint__", []); + var itemHint = core.getFlag("__itemHint__"); + if (core.flags.itemFirstText && itemHint.indexOf(id) < 0 && itemCls != 'items') { + var hint = core.material.items[id].text || "该道具暂无描述"; + try { + hint = core.replaceText(hint); + } catch (e) { } + if (!core.status.event.id || core.status.event.id == 'action') { + core.insertAction("\t[" + core.material.items[id].name + "," + id + "]" + hint + "\n" + + (id.endsWith('Key') ? "(钥匙类道具,遇到对应的门时自动打开)" + : itemCls == 'tools' ? "(消耗类道具,请按T在道具栏使用)" + : itemCls == 'constants' ? "(永久类道具,请按T在道具栏使用)" + : itemCls == 'equips' ? "(装备类道具,请按Q在装备栏进行装备)" : "")); + } + itemHint.push(id); + } + + this.afterGetItem(id, x, y, isGentleClick); + if (callback) callback(); +} + +events.prototype.afterGetItem = function (id, x, y, isGentleClick) { + this.eventdata.afterGetItem(id, x, y, isGentleClick); +} + +////// 获得面前的物品(轻按) ////// +events.prototype.getNextItem = function (noRoute) { + if (core.isMoving() || !core.flags.enableGentleClick) return false; + if (this._canGetNextItem()) return this._getNextItem(null, noRoute); + + var directions = ["up", "down", "left", "right"].filter(function (dir) { + return core.events._canGetNextItem(dir); + }); + return directions.length > 0 ? this._getNextItem(directions[0], noRoute) : false; +} + +events.prototype._canGetNextItem = function (direction) { + direction = direction || core.getHeroLoc('direction'); + if (!core.canMoveHero(null, null, direction)) return; + var nx = core.getHeroLoc('x') + core.utils.scan[direction].x; + var ny = core.getHeroLoc('y') + core.utils.scan[direction].y; + var block = core.getBlock(nx, ny); + return block != null && !block.event.script && !block.event.event && block.event.trigger == 'getItem'; +} + +events.prototype._getNextItem = function (direction, noRoute) { + direction = direction || core.getHeroLoc('direction'); + var nx = core.getHeroLoc('x') + core.utils.scan[direction].x; + var ny = core.getHeroLoc('y') + core.utils.scan[direction].y; + if (!noRoute) core.status.route.push("getNext"); + this.getItem(core.getBlockId(nx, ny), 1, nx, ny, true); + return true; +} + +events.prototype._sys_changeFloor = function (data, callback) { + data = data.event.data; + var heroLoc = {}; + if (data.loc) heroLoc = { 'x': data.loc[0], 'y': data.loc[1] }; + if (data.direction) heroLoc.direction = data.direction; + if (core.status.event.id != 'action') core.status.event.id = null; + core.changeFloor(data.floorId, data.stair, heroLoc, data.time, function () { + core.replay(); + if (callback) callback(); + }); +} + +////// 楼层切换 ////// +events.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback) { + var info = this._changeFloor_getInfo(floorId, stair, heroLoc, time); + if (info == null) { + if (callback) callback(); + return; + } + floorId = info.floorId; + info.locked = core.status.lockControl; + + core.dom.floorNameLabel.innerText = core.status.maps[floorId].title; + core.lockControl(); + core.stopAutomaticRoute(); + core.clearContinueAutomaticRoute(); + core.status.replay.animate = true; + clearInterval(core.interval.onDownInterval); + core.interval.onDownInterval = 'tmp'; + + this._changeFloor_beforeChange(info, callback); +} + +events.prototype._changeFloor_getInfo = function (floorId, stair, heroLoc, time) { + floorId = floorId || core.status.floorId; + if (floorId == ':before') { + var index = core.floorIds.indexOf(core.status.floorId); + if (index > 0) floorId = core.floorIds[index - 1]; + else floorId = core.status.floorId; + } + else if (floorId == ':next') { + var index = core.floorIds.indexOf(core.status.floorId); + if (index < core.floorIds.length - 1) floorId = core.floorIds[index + 1]; + else floorId = core.status.floorId; + } else if (floorId == ':now') { + floorId = core.status.floorId; + } + if (!core.status.maps[floorId]) { + console.error("不存在的楼层:" + floorId); + return null; + } + + if (main.mode != 'play' || core.isReplaying()) time = 0; + if (time == null) time = core.values.floorChangeTime; + time /= 20; + + return { + floorId: floorId, + time: time, + heroLoc: core.clone(this._changeFloor_getHeroLoc(floorId, stair, heroLoc)) + }; +} + +events.prototype._changeFloor_getHeroLoc = function (floorId, stair, heroLoc) { + if (!heroLoc) + heroLoc = core.clone(core.status.hero.loc); + if (stair) { + // --- 对称 + if (stair == ':now') + heroLoc = core.clone(core.status.hero.loc); + else if (stair == ':symmetry') { + heroLoc.x = core.bigmap.width - 1 - core.getHeroLoc('x'); + heroLoc.y = core.bigmap.height - 1 - core.getHeroLoc('y'); + } + else if (stair == ':symmetry_x') + heroLoc.x = core.bigmap.width - 1 - core.getHeroLoc('x'); + else if (stair == ':symmetry_y') + heroLoc.y = core.bigmap.height - 1 - core.getHeroLoc('y'); + // 检查该层地图的 upFloor & downFloor & flyPoint + else if (core.status.maps[floorId][stair]) { + heroLoc.x = core.status.maps[floorId][stair][0]; + heroLoc.y = core.status.maps[floorId][stair][1]; + } + else { + core.extractBlocks(floorId); + var blocks = core.status.maps[floorId].blocks; + for (var i in blocks) { + if (!blocks[i].disable && blocks[i].event.id === stair) { + heroLoc.x = blocks[i].x; + heroLoc.y = blocks[i].y; + break; + } + } + } + } + ['x', 'y', 'direction'].forEach(function (name) { + if (heroLoc[name] == null) + heroLoc[name] = core.getHeroLoc(name); + }); + return heroLoc; +} + +events.prototype._changeFloor_beforeChange = function (info, callback) { + this._changeFloor_playSound(); + // 需要 setTimeout 执行,不然会出错 + window.setTimeout(function () { + if (info.time == 0) + core.events._changeFloor_changing(info, callback); + else + core.showWithAnimate(core.dom.floorMsgGroup, info.time / 2, function () { + core.events._changeFloor_changing(info, callback); + }); + }, 25) +} + +events.prototype._changeFloor_playSound = function () { + // 播放换层音效 + if (core.hasFlag('__fromLoad__')) // 是否是读档造成的切换 + core.playSound('读档'); + else if (core.hasFlag('__isFlying__')) // 是否是楼传造成的切换 + core.playSound('飞行器'); + else + core.playSound('上下楼'); +} + +events.prototype._changeFloor_changing = function (info, callback) { + this.changingFloor(info.floorId, info.heroLoc); + // 回归视角 + var __lockViewport__ = flags.__lockViewport__; + core.setFlag('__lockViewport__', null); + core.drawHero(); + core.setFlag('__lockViewport__', __lockViewport__); + + if (info.time == 0) + this._changeFloor_afterChange(info, callback); + else + core.hideWithAnimate(core.dom.floorMsgGroup, info.time / 4, function () { + core.events._changeFloor_afterChange(info, callback); + }); +} + +events.prototype._changeFloor_afterChange = function (info, callback) { + if (!info.locked) core.unlockControl(); + core.status.replay.animate = false; + core.events.afterChangeFloor(info.floorId); + + if (callback) callback(); +} + +events.prototype.changingFloor = function (floorId, heroLoc) { + this.eventdata.changingFloor(floorId, heroLoc); +} + +////// 转换楼层结束的事件 ////// +events.prototype.afterChangeFloor = function (floorId) { + if (main.mode != 'play') return; + return this.eventdata.afterChangeFloor(floorId); +} + +////// 是否到达过某个楼层 ////// +events.prototype.hasVisitedFloor = function (floorId) { + if (!core.hasFlag("__visited__")) core.setFlag("__visited__", {}); + return core.getFlag("__visited__")[floorId] || false; +} + +////// 到达某楼层 ////// +events.prototype.visitFloor = function (floorId) { + if (!core.hasFlag("__visited__")) core.setFlag("__visited__", {}); + core.getFlag("__visited__")[floorId] = true; +} + +events.prototype._sys_pushBox = function (data, callback) { + this.pushBox(data); + if (callback) callback(); +} + +////// 推箱子 ////// +events.prototype.pushBox = function (data) { + if (data.event.id != 'box' && data.event.id != 'boxed') return; + + // 判断还能否前进,看看是否存在事件 + var direction = core.getHeroLoc('direction'), + nx = data.x + core.utils.scan[direction].x, ny = data.y + core.utils.scan[direction].y; + + // 检测能否推上去 + if (!core.canMoveHero()) return; + var canGoDeadZone = core.flags.canGoDeadZone; + core.flags.canGoDeadZone = true; + if (!core.canMoveHero(data.x, data.y, direction)) { + core.flags.canGoDeadZone = canGoDeadZone; + return; + } + core.flags.canGoDeadZone = canGoDeadZone; + + var nextId = core.getBlockId(nx, ny); + if (nextId != null && nextId != 'flower') return; + + core.setBlock(nextId == null ? 'box' : 'boxed', nx, ny); + + if (data.event.id == 'box') + core.removeBlock(data.x, data.y); + else + core.setBlock('flower', data.x, data.y); + // 勇士前进一格,然后触发推箱子后事件 + core.insertAction([ + { "type": "moveAction" }, + { "type": "function", "function": "function() { core.afterPushBox(); }" } + ]); +} + +////// 推箱子后的事件 ////// +events.prototype.afterPushBox = function () { + return this.eventdata.afterPushBox(); +} + +events.prototype._sys_ski = function (data, callback) { + core.insertAction(["V2.6后,请将滑冰放在背景层!"], data.x, data.y); + if (callback) callback(); +} + +/// 当前是否在冰上 +events.prototype.onSki = function (number) { + if (number == null) number = core.getBgNumber(); + var block = core.getBlockByNumber(number); + return block && block.event && block.event.trigger == 'ski'; +} + +events.prototype._sys_action = function (data, callback) { + var ev = core.clone(data.event.data), ex = data.x, ey = data.y; + // 检查是否需要改变朝向 + if (ex == core.nextX() && ey == core.nextY()) { + var dir = core.turnDirection(":back"); + var id = data.event.id, toId = (data.event.faceIds || {})[dir]; + if (toId && id != toId) { + var number = core.getNumberById(toId); + if (number > 0) + core.setBlock(number, ex, ey); + } + } + this.insertAction(ev, ex, ey, callback); +} + +events.prototype._sys_custom = function (data, callback) { + core.insertAction(["请使用\r[yellow]core.registerSystemEvent('custom', func)\r来处理自己添加的系统触发器!"], + data.x, data.y, callback); +} + +// ------ 自定义事件的处理 ------ // + +////// 注册一个自定义事件 ////// +// type为事件名,func为事件的处理函数,可接受(data, x, y, prefix)参数 +// data为事件内容,x和y为当前点坐标(可为null),prefix为当前点前缀 +events.prototype.registerEvent = function (type, func) { + this.actions[type] = func; +} + +////// 注销一个自定义事件 +events.prototype.unregisterEvent = function (type) { + delete this.actions[type]; +} + +////// 执行一个自定义事件 +events.prototype.doEvent = function (data, x, y, prefix) { + var type = data.type; + if (this.actions[type]) { + try { + return core.doFunc(this.actions[type], this, data, x, y, prefix); + } + catch (e) { + console.error(e); + console.error("ERROR in actions[" + type + "]"); + } + } + if (this["_action_" + type]) return this["_action_" + type](data, x, y, prefix); + core.insertAction("未知的自定义事件: " + type + "!"); + core.doAction(); +} + +events.prototype.setEvents = function (list, x, y, callback) { + var data = core.status.event.data || {}; + if (list) { + var l = core.clone(list); + if (!(l instanceof Array)) l = [l]; + l.push({ "type": "_label" }); + data.list = [{ todo: l, total: core.clone(l), condition: "false" }]; + // 结束所有正在执行的自动事件 + if (list.length == 0) { + core.status.autoEvents.forEach(function (autoEvent) { + core.autoEventExecuting(autoEvent.symbol, false); + }); + } + } + if (x != null) data.x = x; + if (y != null) data.y = y; + if (callback) data.callback = callback; + if (!data.appendingEvents) data.appendingEvents = []; + if (!data.locStack) data.locStack = []; + core.status.event.id = 'action'; + core.status.event.data = data; +} + +////// 开始执行一系列自定义事件 ////// +events.prototype.startEvents = function (list, x, y, callback) { + if (!list) return; + if (!(list instanceof Array)) { + list = [list]; + } + this.setEvents(list, x, y, callback); + // 停止勇士 + core.waitHeroToStop(function () { + core.lockControl(); + core.doAction(); + }); +} + +////// 执行当前自定义事件列表中的下一个事件 ////// +events.prototype.doAction = function () { + // 清空boxAnimate和UI层 + clearInterval(core.status.event.interval); + clearTimeout(core.status.event.interval); + clearInterval(core.status.event.animateUI); + core.status.event.interval = null; + delete core.status.event.aniamteUI; + if (core.status.gameOver || core.status.replay.failed) return; + // 判定是否执行完毕 + if (this._doAction_finishEvents()) return; + core.clearUI(); + var floorId = core.status.event.data.floorId || core.status.floorId; + // 当前点坐标和前缀 + var x = core.status.event.data.x, y = core.status.event.data.y; + var prefix = [floorId || ":f", x != null ? x : "x", y != null ? y : "y"].join("@"); + var current = core.status.event.data.list[0]; + if (this._popEvents(current, prefix)) return; + // 当前要执行的事件 + var data = current.todo.shift(); + core.status.event.data.current = data; + if (typeof data == "string") + data = { "type": "text", "text": data }; + // 该事件块已经被禁用 + if (data._disabled) return core.doAction(); + data.floorId = data.floorId || floorId; + core.status.event.data.type = data.type; + this.doEvent(data, x, y, prefix); + return; +} + +events.prototype._doAction_finishEvents = function () { + if (core.status.event.id != 'action') return true; + // 事件处理完毕 + if (core.status.event.data.list.length == 0) { + // 检测并执行延迟自动事件 + if (core.status.event.data.appendingEvents.length > 0) { + this.setEvents(core.status.event.data.appendingEvents.shift()); + return false; + } + var callback = core.status.event.data.callback; + core.ui.closePanel(); + if (callback) callback(); + core.replay(); + return true; + } + return false; +} + +events.prototype._popEvents = function (current, prefix) { + if (current.todo.length == 0) { // current list is empty + if (core.calValue(current.condition, prefix)) { // check condition + current.todo = core.clone(current.total); + } + else { + core.status.event.data.list.shift(); // remove stack + } + core.doAction(); + return true; + } + return false; +} + +////// 往当前事件列表之前或之后添加一个或多个事件 ////// +events.prototype.insertAction = function (action, x, y, callback, addToLast) { + if (core.hasFlag("__statistics__")) return; + if (core.status.gameOver) return; + if (!action) return; + core.clearRouteFolding(); + + action = this.precompile(action); + + if (core.status.event.id != 'action') { + this.startEvents(action, x, y, callback); + } + else { + if (addToLast) { + var list = core.status.event.data.list[0].todo; + var index = 0; + for (var index = 0; index < list.length; index++) { + if (list[index].type == '_label') { + list.splice(index, 0, action); + break; + } + } + } + else core.unshift(core.status.event.data.list[0].todo, action); + this.setEvents(null, x, y, callback); + } +} + +////// 往当前事件列表之前或之后添加一个公共事件 ////// +events.prototype.insertCommonEvent = function (name, args, x, y, callback, addToLast) { + var commonEvent = this.getCommonEvent(name); + if (!commonEvent) { + if (callback) callback(); + return; + } + + // 设置参数 + core.setFlag('arg0', name); + if (args instanceof Array) { + for (var i = 0; i < args.length; ++i) { + try { + if (args[i] != null) + core.setFlag('arg' + (i + 1), args[i]); + } catch (ee) { console.error(ee) } + } + } + + this.insertAction({ "type": "dowhile", "condition": "false", "data": commonEvent }, x, y, callback, addToLast); +} + +////// 获得一个公共事件 ////// +events.prototype.getCommonEvent = function (name) { + if (!name || typeof name !== 'string') return null; + return this.commonEvent[name] || null; +} + +////// 恢复一个事件 ////// +events.prototype.recoverEvents = function (data) { + if (data) { + core.ui.closePanel(); + core.lockControl(); + core.status.event.id = 'action'; + core.status.event.data = data; + setTimeout(function () { + core.doAction(); + }, 30); + return true; + } + return false; +} + +////// 检测自动事件 ////// +events.prototype.checkAutoEvents = function () { + // 只有在无操作或事件流中才能执行自动事件! + if (!core.isPlaying() || (core.status.lockControl && core.status.event.id != 'action')) return; + if (core.hasFlag('__doNotCheckAutoEvents__')) return; + var todo = [], delay = []; + core.status.autoEvents.forEach(function (autoEvent) { + var symbol = autoEvent.symbol, x = autoEvent.x, y = autoEvent.y, floorId = autoEvent.floorId; + // 不在当前楼层 or 已经执行过 or 已被分区 or 正在执行中 + if (autoEvent.currentFloor && floorId != core.status.floorId) return; + if (!autoEvent.multiExecute && core.autoEventExecuted(symbol)) return; + if ((flags.__removed__ || []).indexOf(floorId) >= 0) return; + if (core.autoEventExecuting(symbol)) return; + var prefix = floorId + "@" + x + "@" + y; + try { + if (!core.calValue(autoEvent.condition, prefix)) return; + } catch (e) { + return; + } + + core.autoEventExecuting(symbol, true); + core.autoEventExecuted(symbol, true); + + var event; + if (x == null && y == null) { + event = [ + // 用do-while(0)包一层防止break影响事件流 + { "type": "dowhile", "condition": "false", "data": autoEvent.data }, + { + "type": "function", "function": + "function() { core.autoEventExecuting('" + symbol + "', false); }" + } + ]; + } else { + event = [ + { + "type": "function", "function": + "function() { core.pushEventLoc(" + x + ", " + y + ", '" + floorId + "' ); }" + }, + // 用do-while(0)包一层防止break影响事件流 + { "type": "dowhile", "condition": "false", "data": autoEvent.data }, + { + "type": "function", "function": + "function() { core.popEventLoc(); core.autoEventExecuting('" + symbol + "', false); }" + } + ]; + } + + if (autoEvent.delayExecute) + delay.push(event); + else + core.push(todo, event); + }); + + if (todo.length == 0 && delay.length == 0) return; + + if (core.status.event.id == 'action' || todo.length > 0) { + core.insertAction(todo); + core.push(core.status.event.data.appendingEvents, delay); + } else { + core.insertAction(delay[0]); + if (delay.length > 0) { + core.insertAction(delay.slice(1)); + } + } + +} + +events.prototype.autoEventExecuting = function (symbol, value) { + var aei = core.getFlag('__aei__', []); + if (value == null) return aei.indexOf(symbol) >= 0; + else { + aei = aei.filter(function (one) { return one != symbol; }); + if (value) aei.push(symbol); + core.setFlag('__aei__', aei); + } +} + +events.prototype.autoEventExecuted = function (symbol, value) { + var aed = core.getFlag('__aed__', []); + if (value == null) return aed.indexOf(symbol) >= 0; + else { + aed = aed.filter(function (one) { return one != symbol; }); + if (value) aed.push(symbol); + core.setFlag('__aed__', aed); + } +} + +events.prototype.pushEventLoc = function (x, y, floorId) { + if (core.status.event.id != 'action') return; + core.status.event.data.locStack.push({ + x: core.status.event.data.x, + y: core.status.event.data.y, + floorId: core.status.event.data.floorId + }); + core.status.event.data.x = x; + core.status.event.data.y = y; + core.status.event.data.floorId = floorId; +} + +events.prototype.popEventLoc = function () { + if (core.status.event.id != 'action') return; + var loc = core.status.event.data.locStack.shift(); + if (loc) { + core.status.event.data.x = loc.x; + core.status.event.data.y = loc.y; + core.status.event.data.floorId = loc.floorId; + } +} + +events.prototype.precompile = function (data) { + var array = this.__precompile_getArray(); + if (typeof data == 'string') { + return this.__precompile_text(data); + } + if (data instanceof Array) { + for (var i = 0; i < data.length; ++i) { + data[i] = this.precompile(data[i]); + } + return data; + } + if (data && data.type) { + if (this["_precompile_" + data.type]) { + data = this["_precompile_" + data.type](data); + } + if (array.texts.indexOf(data.type) >= 0) { + data.text = this.__precompile_text(data.text); + } + if (array.locs.indexOf(data.type) >= 0) { + data.loc = this.__precompile_array(data.loc); + } + if (array.values.indexOf(data.type) >= 0) { + data.value = core.replaceValue(data.value); + } + if (array.uievents.indexOf(data.type) >= 0) { + data.x = core.replaceValue(data.x); + data.y = core.replaceValue(data.y); + data.width = core.replaceValue(data.width); + data.height = core.replaceValue(data.height); + } + if (data.type in array.others) { + array.others[data.type].forEach(function (field) { + data[field] = core.replaceValue(data[field]); + }) + } + } + return data; +} + +events.prototype.__precompile_getArray = function () { + var texts = [ + "text", "autoText", "scrollText", "tip", "textImage", "input", "input2", + "choices", "confirm", "fillText", "fillBoldText", "drawTextContent" + ]; + var locs = [ + "show", "hide", "setBlock", "setBlockOpacity", "showFloorImg", "hideFloorImg", "showBgFgMap", + "hideBgFgMap", "setBgFgBlock", "animate", "setViewport", "move", "jumoHero", + "changeFloor", "changePos", "showTextImage", "showGif", "openDoor", + "closeDoor", "battle", "trigger", "insert", "setEnemyOnPoint", "resetEnemyOnPoint" + ]; + var values = [ + "setValue", "setEnemy", "setEnemyOnPoint", "setEquip", "setFloor", "setGlobalValue", + ]; + var uievents = [ + "clearMap", "fillText", "fillBoldText", "fillRect", "strokeRect", "fillEllipse", "strokeEllipse", + "fillArc", "strokeArc", "drawIcon", "drawSelector", "drawBackground", + ]; + var others = { + "fillEllipse": ["a", "b", "angle"], + "strokeEllipse": ["a", "b", "angle"], + "fillRect": ["radius", "angle"], + "strokeRect": ["radius", "angle"], + "fillArc": ["r", "start", "end"], + "strokeArc": ["r", "start", "end"], + "drawLine": ["x1", "y1", "x2", "y2"], + "drawArrow": ["x1", "y1", "x2", "y2"], + "drawImage": ["x", "y", "w", "h", "x1", "y1", "w1", "h1", "angle"], + "drawTextContent": ["left", "top"], + }; + return { + texts: texts, + locs: locs, + values: values, + uievents: uievents, + others: others + }; +} + +events.prototype.__precompile_text = function (text) { + if (typeof text != 'string') return text; + return text.replace(/\${(.*?)}/g, function (word, value) { + return "${" + core.replaceValue(value) + "}"; + }); +} + +events.prototype.__precompile_array = function (value) { + if (typeof value == 'string') { + value = core.replaceValue(value); + return value; + } + if (value instanceof Array) { + for (var i = 0; i < value.length; ++i) { + value[i] = this.__precompile_array(value[i]); + } + } + return value; +} + +// ------ 样板提供的的自定义事件 ------ // + +events.prototype.__action_checkReplaying = function () { + if (core.isReplaying()) { + core.doAction(); + return true; + } + return false; +} + +events.prototype.__action_getLoc = function (loc, x, y, prefix) { + if (loc) { + x = core.calValue(loc[0], prefix); + y = core.calValue(loc[1], prefix); + } + return [x, y]; +} + +events.prototype.__action_getHeroLoc = function (loc, prefix) { + return this.__action_getLoc(loc, core.getHeroLoc('x'), core.getHeroLoc('y'), prefix); +} + +events.prototype.__action_getLoc2D = function (loc, x, y, prefix) { + if (!(loc && loc[0] instanceof Array)) + loc = [this.__action_getLoc(loc, x, y, prefix)]; + return loc; +} + +events.prototype.__action_doAsyncFunc = function (isAsync, func) { + var parameters = Array.prototype.slice.call(arguments, 2); + if (isAsync) { + func.apply(this, parameters); + core.doAction(); + } + else { + func.apply(this, parameters.concat(core.doAction)); + } +} + +events.prototype._action_text = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + data.text = core.replaceText(data.text, prefix); + var ctx = data.code ? ('__text__' + data.code) : null;; + data.ctx = ctx; + if (core.getContextByName(ctx) && !data.showAll) { + core.ui._animateUI('hide', ctx, function () { + core.ui.drawTextBox(data.text, data); + core.ui._animateUI('show', ctx, function () { + if (data.async) core.doAction(); + }); + }); + return; + } + core.ui.drawTextBox(data.text, data); + if (!data.showAll) { + core.ui._animateUI('show', ctx, function () { + if (data.async) core.doAction(); + }); + } +} + +events.prototype._action_moveTextBox = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, core.moveTextBox, + data.code, this.__action_getLoc(data.loc, x, y, prefix), data.relative, data.moveMode, data.time); +} + +events.prototype._action_clearTextBox = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + core.clearTextBox(data.code, core.doAction); +} + +events.prototype._action_autoText = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + data.text = core.replaceText(data.text, prefix); + core.ui.drawTextBox(data.text); + setTimeout(core.doAction, data.time || 3000); +} + +events.prototype._action_scrollText = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + data.text = core.replaceText(data.text, prefix); + this.__action_doAsyncFunc(data.async, core.drawScrollText, data.text, data.time || 5000, data.lineHeight || 1.4); +} + +events.prototype._action_comment = function (data, x, y, prefix) { + core.doAction(); +} + +events.prototype._action__label = function (data, x, y, prefix) { + core.doAction(); +} + +events.prototype._action_setText = function (data, x, y, prefix) { + this.setTextAttribute(data); + core.doAction(); +} + +events.prototype._action_tip = function (data, x, y, prefix) { + core.drawTip(core.replaceText(data.text, prefix), data.icon); + core.doAction(); +} + +events.prototype._action_show = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + if (data.time > 0 && data.floorId == core.status.floorId) { + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, 'show', data.time); + } + else { + data.loc.forEach(function (t) { + core.showBlock(t[0], t[1], data.floorId); + }); + core.doAction(); + } +} + +events.prototype._action_hide = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + if (data.time > 0 && data.floorId == core.status.floorId) { + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, data.remove ? 'remove' : 'hide', data.time); + } + else { + data.loc.forEach(function (t) { + if (data.remove) core.removeBlock(t[0], t[1], data.floorId); + else core.hideBlock(t[0], t[1], data.floorId); + }); + core.doAction(); + } +} + +events.prototype._action_setBlock = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.time = data.time || 0; + data.floorId = data.floorId || core.status.floorId; + if (data.time > 0 && data.floorId == core.status.floorId) { + this.__action_doAsyncFunc(data.async, core.animateSetBlocks, data.number, data.loc, data.floorId, data.time); + } else { + data.loc.forEach(function (loc) { + core.setBlock(data.number, loc[0], loc[1], data.floorId); + }); + core.doAction(); + } +} + +events.prototype._action_setBlockOpacity = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + if (data.time > 0 && data.floorId == core.status.floorId) { + this.__action_doAsyncFunc(data.async, core.animateBlock, data.loc, data.opacity, data.time); + } + else { + data.loc.forEach(function (t) { + core.setBlockOpacity(data.opacity, t[0], t[1], data.floorId); + }); + core.doAction(); + } +} + +events.prototype._action_setBlockFilter = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.setBlockFilter(data, t[0], t[1], data.floorId); + }); + core.doAction(); +} + +events.prototype._action_turnBlock = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.turnBlock(data.direction, t[0], t[1], data.floorId); + }); + core.doAction(); +} + +events.prototype._action_showFloorImg = function (data, x, y, prefix) { + core.maps.showFloorImage(this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_hideFloorImg = function (data, x, y, prefix) { + core.maps.hideFloorImage(this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_showBgFgMap = function (data, x, y, prefix) { + core.maps.showBgFgMap(data.name, this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction) +} + +events.prototype._action_hideBgFgMap = function (data, x, y, prefix) { + core.maps.hideBgFgMap(data.name, this.__action_getLoc2D(data.loc, x, y, prefix), data.floorId, core.doAction); +} + +events.prototype._action_setBgFgBlock = function (data, x, y, prefix) { + data.loc = this.__action_getLoc2D(data.loc, x, y, prefix); + data.loc.forEach(function (t) { + core.setBgFgBlock(data.name, data.number, t[0], t[1], data.floorId); + }); + core.doAction(); +} + +events.prototype._action_follow = function (data, x, y, prefix) { + this.follow(data.name); + core.doAction(); +} + +events.prototype._action_unfollow = function (data, x, y, prefix) { + this.unfollow(data.name); + core.doAction(); +} + +events.prototype._action_animate = function (data, x, y, prefix) { + if (data.loc == 'hero') { + this.__action_doAsyncFunc(data.async, core.drawHeroAnimate, data.name); + } else { + data.loc = this.__action_getLoc(data.loc, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.drawAnimate, data.name, data.loc[0], data.loc[1], data.alignWindow); + } +} + +events.prototype._action_stopAnimate = function (data, x, y, prefix) { + core.stopAnimate(null, data.doCallback); + core.doAction(); +} + +events.prototype._action_setViewport = function (data, x, y, prefix) { + if (data.dxy != null) { + data.loc = [core.bigmap.offsetX / 32 + (core.calValue(data.dxy[0], prefix) || 0), core.bigmap.offsetY / 32 + (core.calValue(data.dxy[1], prefix) || 0)]; + } else if (data.loc == null) { + data.loc = [core.getHeroLoc('x') - core._HALF_WIDTH_, core.getHeroLoc('y') - core._HALF_HEIGHT_]; + } else { + data.loc = this.__action_getLoc(data.loc, x, y, prefix); + } + this.__action_doAsyncFunc(data.async, core.moveViewport, data.loc[0], data.loc[1], data.moveMode, data.time); +} + +events.prototype._action_lockViewport = function (data, x, y, prefix) { + core.setFlag('__lockViewport__', data.lock || null); + core.doAction(); +} + +events.prototype._action_move = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.moveBlock, loc[0], loc[1], data.steps, data.time, data.keep); +} + +events.prototype._action_moveAction = function (data, x, y, prefix) { + // 检查下一个点是否可通行 + if (core.canMoveHero()) { + var nx = core.nextX(), ny = core.nextY(); + // 检查noPass决定是撞击还是移动 + if (core.noPass(nx, ny)) { + core.insertAction([ + { "type": "trigger", "loc": [nx, ny] } + ]); + } else { + // 先移动一格,然后尝试触发事件 + core.insertAction([ + { "type": "moveHero", "steps": ["forward"] }, + { "type": "function", "function": "function() { core.moveOneStep(core.doAction); }", "async": true }, + { "type": "_label" }, + ]); + } + } + core.doAction(); +} + +events.prototype._action_moveHero = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.eventMoveHero, data.steps, data.time); +} + +events.prototype._action_jump = function (data, x, y, prefix) { + var from = this.__action_getLoc(data.from, x, y, prefix), to; + if (data.dxy) { + to = [from[0] + (core.calValue(data.dxy[0], prefix) || 0), from[1] + (core.calValue(data.dxy[1], prefix) || 0)]; + } else { + to = this.__action_getLoc(data.to, x, y, prefix); + } + this.__action_doAsyncFunc(data.async, core.jumpBlock, from[0], from[1], to[0], to[1], data.time, data.keep); +} + +events.prototype._precompile_jump = function (data) { + data.from = this.__precompile_array(data.from); + data.to = this.__precompile_array(data.to); + data.dxy = this.__precompile_array(data.dxy); + return data; +} + +events.prototype._action_jumpHero = function (data, x, y, prefix) { + var loc; + if (data.dxy) { + loc = [core.getHeroLoc('x') + (core.calValue(data.dxy[0], prefix) || 0), core.getHeroLoc('y') + (core.calValue(data.dxy[1], prefix) || 0)]; + } else { + loc = this.__action_getHeroLoc(data.loc, prefix); + } + this.__action_doAsyncFunc(data.async, core.jumpHero, loc[0], loc[1], data.time); +} + +events.prototype._action_changeFloor = function (data, x, y, prefix) { + var loc = this.__action_getHeroLoc(data.loc, prefix); + var heroLoc = { x: loc[0], y: loc[1], direction: data.direction }; + core.changeFloor(data.floorId, data.stair, heroLoc, data.time, core.doAction); +} + +events.prototype._action_changePos = function (data, x, y, prefix) { + core.clearMap('hero'); + if (!data.loc && data.direction) { + core.setHeroLoc('direction', core.turnDirection(data.direction), true); + core.drawHero(); + return core.doAction(); + } + + var loc = this.__action_getHeroLoc(data.loc, prefix); + core.setHeroLoc('x', loc[0]); + core.setHeroLoc('y', loc[1]); + if (data.direction) core.setHeroLoc('direction', core.turnDirection(data.direction)); + core.drawHero(); + core.doAction(); +} + +events.prototype._action_showImage = function (data, x, y, prefix) { + if (core.isReplaying()) data.time = 0; + this.__action_doAsyncFunc(data.async || data.time == 0, core.showImage, + data.code, data.image + (data.reverse || ''), data.sloc, data.loc, data.opacity, data.time); +} + +events.prototype._precompile_showImage = function (data) { + data.sloc = this.__precompile_array(data.sloc); + data.loc = this.__precompile_array(data.loc); + return data; +} + +events.prototype._action_showTextImage = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, 0, 0, prefix); + if (core.isReplaying()) data.time = 0; + data.text = core.replaceText(data.text, prefix); + var __tmpName = (Math.random() + "_" + Math.random()).replace(/\./g, "") + ".png"; + core.material.images.images[__tmpName] = core.ui.textImage(data.text); + this.__action_doAsyncFunc(data.async || data.time == 0, core.showImage, + data.code, __tmpName + (data.reverse || ""), null, loc, data.opacity, data.time); + delete core.material.images.images[__tmpName]; +} + +events.prototype._action_hideImage = function (data, x, y, prefix) { + if (core.isReplaying()) data.time = 0; + this.__action_doAsyncFunc(data.async || data.time == 0, core.hideImage, data.code, data.time); +} + +events.prototype._action_showGif = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, 0, 0, prefix); + this.showGif(data.name, loc[0], loc[1]); + core.doAction(); +} + +events.prototype._action_moveImage = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, core.moveImage, data.code, data.to, data.opacity, data.moveMode, data.time); +} + +events.prototype._precompile_moveImage = function (data) { + data.to = this.__precompile_array(data.to); + return data; +} + +events.prototype._action_rotateImage = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, core.rotateImage, data.code, data.center, data.angle, data.moveMode, data.time); +} + +events.prototype._precompile_rotateImage = function (data) { + data.center = this.__precompile_array(data.center); + return data; +} + +events.prototype._action_scaleImage = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + this.__action_doAsyncFunc(data.async, core.scaleImage, data.code, data.center, data.scale, data.moveMode, data.time); +} + +events.prototype._precompile_scaleImage = function (data) { + data.center = this.__precompile_array(data.center); + return data; +} + +events.prototype._action_setCurtain = function (data, x, y, prefix) { + if (data.async) { + core.setCurtain(data.color || core.status.thisMap.color, data.time, data.moveMode); + if (data.color == null || data.keep) core.setFlag('__color__', data.color || null); + core.doAction(); + } + else { + core.setCurtain(data.color || core.status.thisMap.color, data.time, data.moveMode, function () { + if (data.color == null || data.keep) core.setFlag('__color__', data.color || null); + core.doAction(); + }); + } +} + +events.prototype._action_screenFlash = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.screenFlash, data.color, data.time, data.times, data.moveMode); +} + +events.prototype._action_setWeather = function (data, x, y, prefix) { + core.setWeather(data.name, data.level); + if (data.keep && ['rain', 'snow', 'sun', 'fog', 'cloud'].indexOf(data.name) >= 0) + core.setFlag('__weather__', [data.name, data.level]); + else core.removeFlag('__weather__'); + core.doAction(); +} + +events.prototype._action_openDoor = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + var floorId = data.floorId; + if (floorId == core.status.floorId) { + this.__action_doAsyncFunc(data.async, core.openDoor, loc[0], loc[1], data.needKey); + } + else { + core.removeBlock(loc[0], loc[1], floorId); + core.doAction(); + } +} + +events.prototype._action_closeDoor = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.__action_doAsyncFunc(data.async, core.closeDoor, loc[0], loc[1], data.id); +} + +events.prototype._action_useItem = function (data, x, y, prefix) { + // 考虑到可能覆盖楼传事件的问题,这里不对fly进行检查。 + if (data.id != 'book' && core.canUseItem(data.id)) { + core.useItem(data.id, true, core.doAction); + } + else { + core.playSound('操作失败'); + core.drawTip("当前无法使用" + ((core.material.items[data.id] || {}).name || "未知道具")); + core.doAction(); + } +} + +events.prototype._action_loadEquip = function (data, x, y, prefix) { + core.loadEquip(data.id); + core.doAction(); +} + +events.prototype._action_unloadEquip = function (data, x, y, prefix) { + core.unloadEquip(data.pos); + core.doAction(); +} + +events.prototype._action_openShop = function (data, x, y, prefix) { + core.setShopVisited(data.id, true); + if (data.open) core.openShop(data.id, true); + core.doAction(); +} + +events.prototype._action_disableShop = function (data, x, y, prefix) { + core.setShopVisited(data.id, false); + core.doAction(); +} + +events.prototype._action_battle = function (data, x, y, prefix) { + if (data.id) { + this.battle(data.id, null, null, true, core.doAction); + } + else { + if (data.floorId != core.status.floorId) { + core.doAction(); + return; + } + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this.battle(null, loc[0], loc[1], true, core.doAction); + } +} + +events.prototype._action_trigger = function (data, x, y, prefix) { + var loc = this.__action_getLoc(data.loc, x, y, prefix); + this._trigger_inAction(loc[0], loc[1]); +} + +events.prototype._action_insert = function (data, x, y, prefix) { + if (data.name) { // 公共事件 + core.insertCommonEvent(data.name, data.args); + } + else { + // 设置参数 + if (data.args instanceof Array) { + for (var i = 0; i < data.args.length; ++i) { + try { + if (data.args[i] != null) + core.setFlag('arg' + (i + 1), data.args[i]); + } catch (ee) { console.error(ee) } + } + } + var loc = this.__action_getLoc(data.loc, x, y, prefix); + core.setFlag('arg0', loc); + var floorId = data.floorId; + var which = data.which || "events"; + var event = (core.floors[floorId][which] || [])[loc[0] + "," + loc[1]]; + if (event) this.insertAction(event.data || event); + } + core.doAction(); +} + +events.prototype._action_playBgm = function (data, x, y, prefix) { + core.playBgm(data.name, data.startTime || 0); + core.setFlag("__bgm__", data.keep ? data.name : null); + core.doAction(); +} + +events.prototype._action_pauseBgm = function (data, x, y, prefix) { + core.pauseBgm(); + core.doAction(); +} + +events.prototype._action_resumeBgm = function (data, x, y, prefix) { + core.resumeBgm(data.resume); + core.doAction(); +} + +events.prototype._action_loadBgm = function (data, x, y, prefix) { + core.loadBgm(data.name); + core.doAction(); +} + +events.prototype._action_freeBgm = function (data, x, y, prefix) { + core.freeBgm(data.name); + core.doAction(); +} + +events.prototype._action_playSound = function (data, x, y, prefix) { + if (data.stop) core.stopSound(); + if (data.sync) { + core.playSound(data.name, data.pitch, core.doAction); + } else { + core.playSound(data.name, data.pitch); + core.doAction(); + } +} + +events.prototype._action_stopSound = function (data, x, y, prefix) { + core.stopSound(); + core.doAction(); +} + +events.prototype._action_setVolume = function (data, x, y, prefix) { + data.value = core.clamp(parseInt(data.value) / 100, 0, 1); + core.setFlag("__volume__", data.value); + this.__action_doAsyncFunc(data.async, core.setVolume, data.value, data.time || 0); +} + +events.prototype._action_setBgmSpeed = function (data, x, y, prefix) { + core.setBgmSpeed(data.value, data.pitch || false); + core.doAction(); +} + +events.prototype._action_setValue = function (data, x, y, prefix) { + this.setValue(data.name, data.operator, data.value, prefix); + if (!data.norefresh) { + if (core.status.hero.hp <= 0) { + core.status.hero.hp = 0; + core.updateStatusBar(); + core.events.lose(); + } else { + core.updateStatusBar(); + } + } + core.doAction(); +} + +events.prototype._action_addValue = function (data, x, y, prefix) { + data.operator = '+='; + this._action_setValue(data, x, y, prefix); +} + +events.prototype._action_setEnemy = function (data, x, y, prefix) { + this.setEnemy(data.id, data.name, data.value, data.operator, prefix, data.norefresh); + core.doAction(); +} + +events.prototype._action_setEnemyOnPoint = function (data, x, y, prefix) { + var loc = this.__action_getLoc2D(data.loc, x, y, prefix); + loc.forEach(function (one) { + core.setEnemyOnPoint(one[0], one[1], data.floorId, data.name, data.value, data.operator, prefix, data.norefresh); + }); + core.doAction(); +} + +events.prototype._action_resetEnemyOnPoint = function (data, x, y, prefix) { + var loc = this.__action_getLoc2D(data.loc, x, y, prefix); + loc.forEach(function (one) { + core.resetEnemyOnPoint(one[0], one[1], data.floorId, data.norefresh); + }); + core.doAction(); +} + +events.prototype._precompile_moveEnemyOnPoint = function (data) { + data.from = this.__precompile_array(data.from); + data.to = this.__precompile_array(data.to); + data.dxy = this.__precompile_array(data.dxy); + return data; +} + +events.prototype._action_moveEnemyOnPoint = function (data, x, y, prefix) { + var from = this.__action_getLoc(data.from, x, y, prefix), to; + if (data.dxy) { + to = [from[0] + (core.calValue(data.dxy[0], prefix) || 0), from[1] + (core.calValue(data.dxy[1], prefix) || 0)]; + } else { + to = this.__action_getLoc(data.to, x, y, prefix); + } + this.moveEnemyOnPoint(from[0], from[1], to[0], to[1], data.floorId, data.norefresh); + core.doAction(); +} + +events.prototype._action_setEquip = function (data, x, y, prefix) { + core.setEquip(data.id, data.valueType, data.name, data.value, data.operator, prefix); + core.doAction(); +} + +events.prototype._action_setFloor = function (data, x, y, prefix) { + this.setFloorInfo(data.name, data.value, data.floorId, prefix); + core.doAction(); +} + +events.prototype._action_setGlobalAttribute = function (data, x, y, prefix) { + this.setGlobalAttribute(data.name, data.value); + core.doAction(); +} + +events.prototype._action_setGlobalValue = function (data, x, y, prefix) { + core.values[data.name] = data.value; + core.doAction(); +} + +events.prototype._action_setGlobalFlag = function (data, x, y, prefix) { + this.setGlobalFlag(data.name, data.value); + core.doAction(); +} + +events.prototype._action_setNameMap = function (data, x, y, floorId) { + this.setNameMap(data.name, data.value); + core.doAction(); +} + +events.prototype._action_setHeroIcon = function (data, x, y, prefix) { + this.setHeroIcon(data.name, data.noDraw); + core.doAction(); +} + +events.prototype._action_input = function (data, x, y, prefix) { + this.__action_getInput(core.replaceText(data.text, prefix), false, function (value) { + value = parseInt(value) || 0; // 允许负整数 + core.status.route.push("input:" + value); + core.setFlag("input", value); + core.doAction(); + }); +} + +events.prototype._action_input2 = function (data, x, y, prefix) { + this.__action_getInput(core.replaceText(data.text, prefix), true, function (value) { + value = value || ""; + core.status.route.push("input2:" + core.encodeBase64(value)); + core.setFlag("input", value); + core.doAction(); + }); +} + +events.prototype.__action_getInput = function (hint, isText, callback) { + var value, prefix = isText ? "input2:" : "input:"; + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(); + try { + if (action.indexOf(prefix) != 0) { + console.warn("警告!当前需要一个 " + prefix + " 项,实际为 " + action); + core.status.replay.toReplay.unshift(action); + return callback(isText ? '' : 0); + } + if (isText) value = core.decodeBase64(action.substring(7)); + else value = parseInt(action.substring(6)); + callback(value); + } + catch (e) { + core.control._replay_error(action); + return; + } + } + else { + core.myprompt(core.replaceText(hint), null, callback); + } +} + +events.prototype._action_if = function (data, x, y, prefix) { + if (core.calValue(data.condition, prefix)) + core.events.insertAction(data["true"]) + else + core.events.insertAction(data["false"]) + core.doAction(); +} + +events.prototype._precompile_if = function (data) { + data.condition = core.replaceValue(data.condition); + data["true"] = this.precompile(data["true"]); + data["false"] = this.precompile(data["false"]); + return data; +} + +events.prototype._action_switch = function (data, x, y, prefix) { + var key = core.calValue(data.condition, prefix) + var list = []; + for (var i = 0; i < data.caseList.length; i++) { + if (data.caseList[i]._disabled) continue; + var condition = data.caseList[i]["case"]; + if (condition == "default" || core.calValue(condition, prefix) === key) { + core.push(list, data.caseList[i].action); + if (!data.caseList[i].nobreak) + break; + } + } + core.insertAction(list); + core.doAction(); +} + +events.prototype._precompile_switch = function (data) { + data.condition = core.replaceValue(data.condition); + for (var i = 0; i < data.caseList.length; i++) { + data.caseList[i]["case"] = core.replaceValue(data.caseList[i]["case"]); + data.caseList[i].action = this.precompile(data.caseList[i].action); + } + return data; +} + +events.prototype._action_choices = function (data, x, y, prefix) { + data.choices = data.choices.filter(function (x) { + if (x._disabled) return false; + if (x.condition == null || x.condition == '') return true; + try { return core.calValue(x.condition, prefix); } catch (e) { return true; } + }) + if (data.choices.length == 0) return this.doAction(); + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(); + if (action.indexOf('choices:') == 0 && !(action == 'choices:none' && !data.timeout)) { + var index = action.substring(8); + if (!this.__action_choices_replaying(data, index)) { + core.control._replay_error(action); + return; + } + } else { + // 容错录像 + if (main.replayChecking) { + // 录像验证系统中选最后一项 + if (action != 'choices:none') core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作 + core.events.__action_choices_replaying(data, -1); + } else { + // 正常游戏中弹窗选择 + core.myprompt('录像回放出错!当前需要执行选择项但录像中未记录。\n如需修复请输入您要选的项(从0起),点击取消将不会修复。', 0, function (value) { + if (value == null) { + core.control._replay_error(action); + return; + } + if (action != 'choices:none') core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作 + core.events.__action_choices_replaying(data, ((parseInt(value) || 0) + data.choices.length) % data.choices.length); + }); + } + } + } else { + if (data.timeout) { + core.status.event.interval = setTimeout(function () { + core.status.route.push("choices:none"); + core.setFlag('timeout', 0); + core.doAction(); + }, data.timeout); + } + core.status.event.timeout = new Date().getTime() + (data.timeout || 0); + } + 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, prefix); + } + core.ui.drawChoices(core.replaceText(data.text, prefix), data.choices, data.width); +} + +events.prototype.__action_choices_replaying = function (data, index) { + var selection = index; + if (index != 'none') { + selection = parseInt(index); + if (isNaN(selection)) return false; + if (selection < 0) selection += data.choices.length; + if (selection < 0) return false; + if (selection % 100 > 50) selection += data.choices.length; + if (selection % 100 > data.choices.length) return false; + var timeout = Math.floor(selection / 100) || 0; + core.setFlag('timeout', timeout); + selection %= 100; + } else core.setFlag('timeout', 0); + core.status.event.selection = selection; + setTimeout(function () { + core.status.route.push("choices:" + index); + if (selection != 'none') { + // 检查 + var choice = data.choices[selection]; + if (choice.need != null && choice.need != '' && !core.calValue(choice.need)) { + // 无法选择此项 + core.control._replay_error("无法选择项:" + index); + return; + } else { + core.insertAction(choice.action); + } + } + core.doAction(); + }, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed)); + return true; +} + +events.prototype._precompile_choices = function (data) { + if (!(data.choices instanceof Array)) return data; + for (var i = 0; i < data.choices.length; ++i) { + data.choices[i].condition = core.replaceValue(data.choices[i].condition); + data.choices[i].text = this.__precompile_text(data.choices[i].text); + data.choices[i].action = this.precompile(data.choices[i].action); + } + return data; +} + +events.prototype._action_confirm = function (data, x, y, prefix) { + data.text = core.replaceText(data.text, prefix); + core.status.event.ui = { "text": data.text, "yes": data.yes, "no": data.no }; + if (core.isReplaying()) { + var action = core.status.replay.toReplay.shift(); + if (action.indexOf('choices:') == 0 && !(action == 'choices:none' && !data.timeout)) { + var index = action.substring(8); + if (index == 'none' || ((index = parseInt(index)) >= 0) && index % 100 < 2) { + this.__action_confirm_replaying(data, index); + } else { + core.control._replay_error(action); + return; + } + } else { + // 录像中未记录选了哪个,则选默认值,而不是直接报错 + if (action != 'choices:none') core.status.replay.toReplay.unshift(action); + this.__action_confirm_replaying(data, data["default"] ? 0 : 1); + } + } + else { + core.status.event.selection = data["default"] ? 0 : 1; + if (data.timeout) { + core.status.event.interval = setTimeout(function () { + core.status.route.push("choices:none"); + core.setFlag('timeout', 0); + core.doAction(); + }, data.timeout); + } + core.status.event.timeout = new Date().getTime() + (data.timeout || 0); + } + core.ui.drawConfirmBox(data.text); +} + +events.prototype.__action_confirm_replaying = function (data, index) { + if (index != 'none') { + var timeout = Math.floor(index / 100) || 0; + core.setFlag('timeout', timeout); + index %= 100; + } else core.setFlag('timeout', 0); + core.status.event.selection = index; + setTimeout(function () { + core.status.route.push("choices:" + index); + if (index != 'none') { + if (index == 0) core.insertAction(data.yes); + else core.insertAction(data.no); + } + core.doAction(); + }, core.status.replay.speed == 24 ? 1 : 750 / Math.max(1, core.status.replay.speed)); +} + +events.prototype._precompile_confirm = function (data) { + data.yes = this.precompile(data.yes); + data.no = this.precompile(data.no); + return data; +} + +events.prototype._action_for = function (data, x, y, prefix) { + // Only support temp:A + if (!/^temp:[A-Z]$/.test(data.name)) { + core.insertAction('循环遍历事件只支持临时变量!'); + return core.doAction(); + } + var from = core.calValue(data.from); + var to = core.calValue(data.to); + var step = core.calValue(data.step); + if (typeof from != 'number' || typeof to != 'number' || typeof step != 'number') { + core.insertAction('循环遍历事件要求【起始点】【终止点】【每步】仅能是数字!'); + return core.doAction(); + } + // 首次判定 + if ((step > 0 && from > to) || (step < 0 && from < to)) { + core.doAction(); + return; + } + + var letter = data.name.substring(5); + core.setFlag('@temp@' + letter, from); + var toName = '@temp@for-to@' + letter; + var stepName = '@temp@for-step@' + letter; + core.setFlag(toName, data.to); + core.setFlag(stepName, data.step); + var condition = "(function () {" + + "var to = core.calValue(core.getFlag('" + toName + "'));" + + "var step = core.calValue(core.getFlag('" + stepName + "'));" + + "if (typeof step != 'number' || typeof to != 'number') return false;" + + "if (step == 0) return true;" + + "var currentValue = core.getFlag('@temp@" + letter + "');" + + "currentValue += step;" + + "core.setFlag('@temp@" + letter + "', currentValue);" + + "if (step > 0) { return currentValue <= to; }" + + "else { return currentValue >= to; }" + + "})()"; + return this._action_dowhile({ "condition": condition, "data": data.data }, x, y, prefix); +} + +events.prototype._precompile_for = function (data) { + data.from = core.replaceValue(data.from); + data.to = core.replaceValue(data.to); + data.step = core.replaceValue(data.step); + data.data = this.precompile(data.data); + return data; +} + +events.prototype._action_forEach = function (data, x, y, prefix) { + // Only support temp:A + if (!/^temp:[A-Z]$/.test(data.name)) { + core.insertAction(['循环遍历事件只支持临时变量!']); + return core.doAction(); + } + var listName = '@temp@forEach@' + data.name.substring(5); + core.setFlag(listName, core.clone(data.list)); + var condition = "(function () {" + + "var list = core.getFlag('" + listName + "', []);" + + "if (list.length == 0) return false;" + + "core.setFlag('@temp@'+'" + data.name.substring(5) + "', list.shift());" + + "return true;" + + "})()"; + return this._action_while({ "condition": condition, "data": data.data }, x, y, prefix); +} + +events.prototype._precompile_forEach = function (data) { + data.data = this.precompile(data.data); + return data; +} + +events.prototype._action_while = function (data, x, y, prefix) { + if (core.calValue(data.condition, prefix)) { + var list = core.clone(data.data); + if (!(list instanceof Array)) list = [list]; + list.push({ "type": "_label" }); + core.unshift(core.status.event.data.list, + { "todo": list, "total": core.clone(list), "condition": data.condition } + ); + } + core.doAction(); +} + +events.prototype._precompile_while = function (data) { + data.condition = core.replaceValue(data.condition); + data.data = this.precompile(data.data); + return data; +} + +events.prototype._action_dowhile = function (data, x, y, prefix) { + var list = core.clone(data.data); + if (!(list instanceof Array)) list = [list]; + list.push({ "type": "_label" }); + core.unshift(core.status.event.data.list, + { "todo": list, "total": core.clone(list), "condition": data.condition } + ); + core.doAction(); +} + +events.prototype._precompile_dowhile = function (data) { + data.condition = core.replaceValue(data.condition); + data.data = this.precompile(data.data); + return data; +} + +events.prototype._action_break = function (data, x, y, prefix) { + var n = data.n || 1; + while (n--) { + if (core.status.event.data.list.length > 1) + core.status.event.data.list.shift(); + } + core.doAction(); +} + +events.prototype._action_continue = function (data, x, y, prefix) { + var n = data.n || 1; + while (n-- > 1) { + if (core.status.event.data.list.length > 1) + core.status.event.data.list.shift(); + } + if (core.status.event.data.list.length > 1) { + if (core.calValue(core.status.event.data.list[0].condition, prefix)) { + core.status.event.data.list[0].todo = core.clone(core.status.event.data.list[0].total); + } + else { + core.status.event.data.list.shift(); + } + } + core.doAction(); +} + +events.prototype._action_win = function (data, x, y, prefix) { + this.win(core.replaceText(data.reason, prefix), data.norank, data.noexit); +} + +events.prototype._action_lose = function (data, x, y, prefix) { + this.lose(core.replaceText(data.reason, prefix)); +} + +events.prototype._action_restart = function (data, x, y, prefix) { + core.restart(); +} + +events.prototype._action_function = function (data, x, y, prefix) { + var func = data["function"]; + try { + if (typeof func == "string" && func.indexOf("function") == 0) { + eval('(' + func + ')()'); + } + } catch (e) { + console.error(e); + } + if (!data.async) + core.doAction(); +} + +events.prototype._action_update = function (data, x, y, prefix) { + core.updateStatusBar(data.doNotCheckAutoEvents, true); + core.doAction(); +} + +events.prototype._action_showStatusBar = function (data, x, y, prefix) { + core.showStatusBar(); + core.doAction(); +} + +events.prototype._action_hideStatusBar = function (data, x, y, prefix) { + core.hideStatusBar(data.toolbox); + core.doAction(); +} + +events.prototype._action_showHero = function (data, x, y, prefix) { + data.opacity = 1; + return this._action_setHeroOpacity(data, x, y, prefix); +} + +events.prototype._action_hideHero = function (data, x, y, prefix) { + data.opacity = 0; + return this._action_setHeroOpacity(data, x, y, prefix); +} + +events.prototype._action_setHeroOpacity = function (data, x, y, prefix) { + data.time = data.time || 0; + if (data.opacity == null) data.opacity = 1; + if (data.time > 0) { + this.__action_doAsyncFunc(data.async, core.setHeroOpacity, data.opacity, data.moveMode, data.time); + } else { + core.setFlag('__heroOpacity__', data.opacity); + core.drawHero(); + core.doAction(); + } +} + +events.prototype._action_vibrate = function (data, x, y, prefix) { + this.__action_doAsyncFunc(data.async, core.vibrate, data.direction, data.time, data.speed, data.power); +} + +events.prototype._action_sleep = function (data, x, y, prefix) { + core.timeout.sleepTimeout = setTimeout(function () { + core.timeout.sleepTimeout = null; + core.doAction(); + }, core.isReplaying() ? Math.min(data.time, 20) : data.time); +} + +events.prototype._action_wait = function (data, x, y, prefix) { + if (core.isReplaying()) { + var code = core.status.replay.toReplay.shift(); + if (code.indexOf("input:") == 0 && !(code == "input:none" && !data.timeout)) { + if (code == "input:none") { + core.status.route.push("input:none"); + core.setFlag("type", -1); + core.setFlag("timeout", 0); + this.__action_wait_afterGet(data); + return core.doAction(); + } else { + var value = parseInt(code.substring(6)); + core.status.route.push("input:" + value); + this.__action_wait_getValue(value); + if (this.__action_wait_afterGet(data) || !data.forceChild) return core.doAction(); + } + } + core.control._replay_error(code); + return; + } else if (data.timeout) { + core.status.event.interval = setTimeout(function () { + core.status.route.push("input:none"); + core.setFlag("type", -1); + core.setFlag("timeout", 0); + core.events.__action_wait_afterGet(data); + core.doAction(); + }, data.timeout); + } + core.status.event.timeout = new Date().getTime() + (data.timeout || 0); +} + +events.prototype.__action_wait_getValue = function (value) { + core.setFlag("timeout", Math.floor(value / 1e8) || 0); + value %= 1e8; + if (value >= 1000000) { + core.setFlag('type', 1); + var px = parseInt((value - 1000000) / 1000), py = value % 1000; + core.setFlag('px', px); + core.setFlag('py', py); + core.setFlag('x', parseInt(px / 32)); + core.setFlag('y', parseInt(py / 32)); + } + else if (value >= 10000) { + core.setFlag('type', 1); + var x = parseInt((value - 10000) / 100), y = value % 100; + core.setFlag('px', 32 * x + 16); + core.setFlag('py', 32 * y + 16); + core.setFlag('x', x); + core.setFlag('y', y); + } + else if (value > 0) { + core.setFlag('type', 0); + core.setFlag('keycode', value); + } +} + +events.prototype.__action_wait_afterGet = function (data) { + if (!data.data) return false; + var todo = []; + var stop = false; + var found = false; + data.data.forEach(function (one) { + if (one._disabled || stop) return; + if (one["case"] == "keyboard" && core.getFlag("type") == 0) { + (one.keycode + "").split(",").forEach(function (keycode) { + if (core.getFlag("keycode", 0) == keycode) { + found = true; + core.push(todo, one.action); + if (one["break"]) stop = true; + } + }); + } + if (one["case"] == "mouse" && one.px instanceof Array + && one.py instanceof Array && core.getFlag("type") == 1) { + var pxmin = core.calValue(one.px[0]); + var pxmax = core.calValue(one.px[1]); + var pymin = core.calValue(one.py[0]); + var pymax = core.calValue(one.py[1]); + var px = core.getFlag("px", 0), py = core.getFlag("py", 0); + if (px >= pxmin && px <= pxmax && py >= pymin && py <= pymax) { + found = true; + core.push(todo, one.action); + if (one["break"]) stop = true; + } + } + if (one["case"] == "condition") { + var condition = false; + try { condition = core.calValue(one.condition); } catch (e) { } + if (condition) { + found = true; + core.push(todo, one.action); + if (one["break"]) stop = true; + } + } + if (one["case"] == "timeout" && core.getFlag("type") == -1) { + found = true; + core.push(todo, one.action); + if (one["break"]) stop = true; + } + }) + if (found) { + core.insertAction(todo); + return true; + } + return false; +} + +events.prototype._precompile_wait = function (data) { + if (data.data) { + data.data.forEach(function (v) { + if (v.px != null) v.px = this.__precompile_array(v.px); + if (v.py != null) v.py = this.__precompile_array(v.py); + if (v.condition != null) v.condition = this.__precompile_array(v.condition); + v.action = this.precompile(v.action); + }, this); + } + return data; +} + +events.prototype._action_waitAsync = function (data, x, y, prefix) { + var test = window.setInterval(function () { + if (!core.hasAsync() + && (data.excludeAnimates || core.isReplaying() || core.getPlayingAnimates().length == 0) + && (!data.includeSounds || core.isReplaying() || core.getPlayingSounds().length == 0)) { + clearInterval(test); + core.doAction(); + } + }, 50 / core.status.replay.speed); +} + +events.prototype._action_stopAsync = function (data, x, y, prefix) { + core.stopAsync(); + core.doAction(); +} + +events.prototype._action_callBook = function (data, x, y, prefix) { + if (core.isReplaying() || !core.hasItem('book')) { + core.doAction(); + } + else { + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + core.openBook(); + core.status.event.interval = e; + } +} + +events.prototype._action_callSave = function (data, x, y, prefix) { + if (core.isReplaying() || core.hasFlag("__events__")) { + core.removeFlag("__events__"); + core.doAction(); + } + else { + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + var forbidSave = core.hasFlag('__forbidSave__'); + core.removeFlag('__forbidSave__'); + core.save(); + if (forbidSave) core.setFlag('__forbidSave__', true); + core.status.event.interval = e; + } +} + +events.prototype._action_autoSave = function (data, x, y, prefix) { + var forbidSave = core.hasFlag('__forbidSave__'); + core.removeFlag('__forbidSave__'); + core.autosave(data.removeLast); + if (forbidSave) core.setFlag('__forbidSave__', true); + core.doAction(); +} + +events.prototype._action_forbidSave = function (data, x, y, prefix) { + core.setFlag('__forbidSave__', data.forbid || null); + core.doAction(); +} + +events.prototype._action_callLoad = function (data, x, y, prefix) { + if (this.__action_checkReplaying()) return; + var e = core.clone(core.status.event.data); + core.ui.closePanel(); + core.load(); + core.status.event.interval = e; +} + +events.prototype._action_exit = function (data, x, y, prefix) { + this.setEvents([]); + core.doAction(); +} + +events.prototype._action_previewUI = function (data, x, y, prefix) { + this.insertAction(data.action); + core.doAction(); +} + +events.prototype._precompile_previewUI = function (data) { + data.action = this.precompile(data.action); + return data; +} + +events.prototype.__action_doUIEvent = function (data) { + this.__action_doUIEvent_doOne(data); + var current = core.status.event.data.list[0]; + while (current.todo.length > 0) { + data = current.todo[0]; + if (this.__action_doUIEvent_doOne(current.todo[0])) + current.todo.shift(); + else break; + } + core.doAction(); +} + +events.prototype.__action_doUIEvent_doOne = function (data) { + if (core.ui['_uievent_' + data.type]) { + core.ui['_uievent_' + data.type](data); + return true; + } + return false; +} + +events.prototype._action_clearMap = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillText = function (data, x, y, prefix) { + data.text = core.replaceText(data.text, prefix); + this.__action_doUIEvent(data); +} + +events.prototype._action_fillBoldText = function (data, x, y, prefix) { + data.text = core.replaceText(data.text, prefix); + this.__action_doUIEvent(data); +} + +events.prototype._action_fillRect = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillPolygon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._precompile_fillPolygon = function (data) { + data.nodes = this.__precompile_array(data.nodes); + return data; +} + +events.prototype._action_strokeRect = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_strokePolygon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._precompile_strokePolygon = function (data) { + data.nodes = this.__precompile_array(data.nodes); + return data; +} + +events.prototype._action_fillEllipse = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_strokeEllipse = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_fillArc = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_strokeArc = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawLine = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawArrow = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_setAttribute = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_setFilter = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawImage = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawIcon = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawSelector = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawBackground = function (data, x, y, prefix) { + this.__action_doUIEvent(data); +} + +events.prototype._action_drawTextContent = function (data, x, y, prefix) { + data.text = core.replaceText(data.text, prefix); + this.__action_doUIEvent(data); +} + +// ------ 点击状态栏图标所进行的一些操作 ------ // + +////// 判断当前能否进入某个事件 ////// +events.prototype._checkStatus = function (name, fromUserAction, checkItem) { + if (fromUserAction && core.status.event.id == name) { + core.ui.closePanel(); + return false; + } + if (fromUserAction && core.status.lockControl) return false; + if (checkItem && !core.hasItem(name)) { + core.playSound('操作失败'); + core.drawTip("你没有" + core.material.items[name].name, name); + return false; + } + if (core.isMoving()) { + core.playSound('操作失败'); + core.drawTip("请先停止勇士行动"); + return false; + } + core.lockControl(); + core.status.event.id = name; + return true; +} + +////// 点击怪物手册时的打开操作 ////// +events.prototype.openBook = function (fromUserAction) { + if (core.isReplaying()) return; + // 如果能恢复事件(从callBook事件触发) + if (core.status.event.id == 'book' && core.events.recoverEvents(core.status.event.interval)) + return; + // 当前是book,且从“浏览地图”打开 + if (core.status.event.id == 'book' && core.status.event.ui) { + core.status.boxAnimateObjs = []; + core.ui._drawViewMaps(core.status.event.ui); + return; + } + // 从“浏览地图”页面打开 + if (core.status.event.id == 'viewMaps') { + fromUserAction = false; + core.status.event.ui = core.status.event.data; + } + if (!this._checkStatus('book', fromUserAction, true)) return; + core.playSound('打开界面'); + core.useItem('book', true); +} + +////// 点击楼层传送器时的打开操作 ////// +events.prototype.useFly = function (fromUserAction) { + if (core.isReplaying()) return; + // 从“浏览地图”页面:尝试直接传送到该层 + if (core.status.event.id == 'viewMaps') { + if (!core.hasItem('fly')) { + core.playSound('操作失败'); + core.drawTip('你没有' + core.material.items['fly'].name, 'fly'); + } else if (!core.canUseItem('fly') || (core.flags.flyNearStair && !core.nearStair())) { + core.playSound('操作失败'); + core.drawTip('无法传送到当前层', 'fly'); + } else { + core.flyTo(core.status.event.data.floorId); + } + return; + } + + if (!this._checkStatus('fly', fromUserAction, true)) return; + if (core.flags.flyNearStair && !core.nearStair()) { + core.playSound('操作失败'); + core.drawTip("只有在楼梯边才能使用" + core.material.items['fly'].name, 'fly'); + core.unlockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + if (!core.canUseItem('fly')) { + core.playSound('操作失败'); + core.drawTip(core.material.items['fly'].name + "好像失效了", 'fly'); + core.unlockControl(); + core.status.event.data = null; + core.status.event.id = null; + return; + } + core.playSound('打开界面'); + core.useItem('fly', true); + return; +} + +events.prototype.flyTo = function (toId, callback) { + return this.eventdata.flyTo(toId, callback); +} + +////// 点击装备栏时的打开操作 ////// +events.prototype.openEquipbox = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('equipbox', fromUserAction)) return; + core.playSound('打开界面'); + core.ui._drawEquipbox(); +} + +////// 点击工具栏时的打开操作 ////// +events.prototype.openToolbox = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('toolbox', fromUserAction)) return; + core.playSound('打开界面'); + core.ui._drawToolbox(); +} + +////// 点击快捷商店按钮时的打开操作 ////// +events.prototype.openQuickShop = function (fromUserAction) { + if (core.isReplaying()) return; + + if (Object.keys(core.status.shops).length == 0) { + core.playSound('操作失败'); + core.drawTip("本游戏没有快捷商店!"); + return; + } + + // --- 如果只有一个商店,则直接打开之 + if (Object.keys(core.status.shops).length == 1) { + var shopId = Object.keys(core.status.shops)[0]; + if (core.status.event.id != null) return; + if (!core.canOpenShop(shopId)) { + core.playSound('操作失败'); + core.drawTip("当前无法打开快捷商店!"); + return; + } + var message = core.canUseQuickShop(shopId); + if (message != null) { + core.playSound('操作失败'); + core.drawTip(message); + return; + } + core.openShop(shopId, false); + return; + } + + if (!this._checkStatus('selectShop', fromUserAction)) return; + core.ui._drawQuickShop(); +} + +events.prototype.openKeyBoard = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('keyBoard', fromUserAction)) return; + core.ui._drawKeyBoard(); +} + +////// 点击保存按钮时的打开操作 ////// +events.prototype.save = function (fromUserAction) { + if (core.isReplaying()) return; + if (core.hasFlag('__forbidSave__')) { + core.playSound('操作失败'); + core.drawTip('当前禁止存档'); + return; + } + if (core.status.event.id == 'save' && core.events.recoverEvents(core.status.event.interval)) + return; + if (!this._checkStatus('save', fromUserAction)) return; + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + core.playSound('打开界面'); + core.ui._drawSLPanel(10 * page + offset); +} + +////// 点击读取按钮时的打开操作 ////// +events.prototype.load = function (fromUserAction) { + if (core.isReplaying()) return; + var saveIndex = core.saves.saveIndex; + var page = parseInt((saveIndex - 1) / 5), offset = saveIndex - 5 * page; + // 游戏开始前读档 + if (!core.isPlaying()) { + core.dom.startPanel.style.display = 'none'; + core.clearStatus(); + core.clearMap('all'); + core.status.event = { 'id': 'load', 'data': null }; + core.status.lockControl = true; + core.playSound('打开界面'); + core.ui._drawSLPanel(10 * page + offset); + return; + } + if (core.status.event.id == 'load' && core.events.recoverEvents(core.status.event.interval)) + return; + if (!this._checkStatus('load', fromUserAction)) return; + core.playSound('打开界面'); + core.ui._drawSLPanel(10 * page + offset); +} + +////// 点击设置按钮时的操作 ////// +events.prototype.openSettings = function (fromUserAction) { + if (core.isReplaying()) return; + if (!this._checkStatus('settings', fromUserAction)) + return; + core.playSound('打开界面'); + core.ui._drawSettings(); +} + +// ------ 一些事件的具体执行过程 ------ // + +events.prototype.hasAsync = function () { + return Object.keys(core.animateFrame.asyncId).length > 0; +} + +////// 立刻停止所有异步事件 ////// +events.prototype.stopAsync = function () { + var callbacks = []; + for (var id in core.animateFrame.asyncId) { + clearInterval(id); + callbacks.push(core.animateFrame.asyncId[id]); + } + core.animateFrame.asyncId = {}; + callbacks.forEach(function (cb) { + if (cb && cb instanceof Function) cb(); + }); +} + +events.prototype.hasAsyncAnimate = function () { + return (core.status.animateObjs || []).length > 0; +} + +////// 跟随 ////// +events.prototype.follow = function (name) { + name = core.getMappedName(name); + if (core.material.images.images[name]) { + core.status.hero.followers.push({ "name": name }); + core.gatherFollowers(); + core.clearMap('hero'); + core.drawHero(); + } + core.clearRouteFolding(); +} + +////// 取消跟随 ////// +events.prototype.unfollow = function (name) { + if (!name) { + core.status.hero.followers = []; + } + else { + name = core.getMappedName(name); + for (var i = 0; i < core.status.hero.followers.length; i++) { + if (core.status.hero.followers[i].name == name) { + core.status.hero.followers.splice(i, 1); + break; + } + } + } + core.gatherFollowers(); + core.clearMap('hero'); + core.drawHero(); + core.clearRouteFolding(); +} + +events.prototype._updateValueByOperator = function (value, originValue, operator) { + switch (operator) { + case '+=': value = originValue + value; break; + case '-=': value = originValue - value; break; + case '*=': value = originValue * value; break; + case '/=': value = originValue / value; break; + case '**=': value = Math.pow(originValue, value); break; + case '//=': value = Math.trunc(originValue / value); break; + case '%=': value = originValue % value; break; + case 'min=': value = Math.min(originValue, value); break; + case 'max=': value = Math.max(originValue, value); break; + default: break; + } + return value; +} + +////// 数值操作 ////// +events.prototype.setValue = function (name, operator, value, prefix) { + value = this._updateValueByOperator(core.calValue(value, prefix), core.calValue(name, prefix), operator); + this._setValue_setStatus(name, value); + this._setValue_setBuff(name, value); + this._setValue_setItem(name, value); + this._setValue_setFlag(name, value); + this._setValue_setSwitch(name, value, prefix); + this._setValue_setTemp(name, value, prefix); + this._setValue_setGlobal(name, value); +} + +events.prototype._setValue_setStatus = function (name, value) { + if (name.indexOf("status:") !== 0) return; + core.setStatus(name.substring(7), value); +} + +events.prototype._setValue_setBuff = function (name, value) { + if (name.indexOf('buff:') !== 0) return; + core.setBuff(name.substring(5), value); +} + +events.prototype._setValue_setItem = function (name, value) { + if (name.indexOf("item:") !== 0) return; + var itemId = name.substring(5), count = core.itemCount(itemId); + if (value > count) core.getItem(itemId, value - count); + else core.setItem(itemId, value); +} + +events.prototype._setValue_setFlag = function (name, value) { + if (name.indexOf("flag:") !== 0) return; + core.setFlag(name.substring(5), value); +} + +events.prototype._setValue_setSwitch = function (name, value, prefix) { + if (name.indexOf("switch:") !== 0) return; + core.setFlag((prefix || ":f@x@y") + "@" + name.substring(7), value); +} + +events.prototype._setValue_setTemp = function (name, value) { + if (name.indexOf("temp:") !== 0) return; + core.setFlag("@temp@" + name.substring(5), value); +} + +events.prototype._setValue_setGlobal = function (name, value) { + if (name.indexOf("global:") !== 0) return; + core.setGlobal(name.substring(7), value); +} + +////// 设置一个怪物属性 ////// +events.prototype.setEnemy = function (id, name, value, operator, prefix, norefresh) { + if (!core.hasFlag('enemyInfo')) { + core.setFlag('enemyInfo', {}); + } + var enemyInfo = core.getFlag('enemyInfo'); + if (!enemyInfo[id]) enemyInfo[id] = {}; + if (typeof value === 'string' && name == 'name') value = value.replace(/\r/g, '\\r'); + value = this._updateValueByOperator(core.calValue(value, prefix), (core.material.enemys[id] || {})[name], operator); + enemyInfo[id][name] = value; + (core.material.enemys[id] || {})[name] = core.clone(value); + if (!norefresh) core.updateStatusBar(); +} + +////// 设置某个点上的怪物属性 ////// +events.prototype.setEnemyOnPoint = function (x, y, floorId, name, value, operator, prefix, norefresh) { + floorId = floorId || core.status.floorId; + var block = core.getBlock(x, y, floorId); + if (block == null) return; + if (block.event.cls.indexOf('enemy') != 0) return; + var enemy = core.material.enemys[block.event.id]; + if (enemy == null) return; + if (typeof value === 'string' && name == 'name') value = value.replaceAll(/\r/g, '\\r'); + value = this._updateValueByOperator(core.calValue(value, prefix), core.getEnemyValue(enemy, name, x, y, floorId), operator); + flags.enemyOnPoint = flags.enemyOnPoint || {}; + flags.enemyOnPoint[floorId] = flags.enemyOnPoint[floorId] || {}; + flags.enemyOnPoint[floorId][x + "," + y] = flags.enemyOnPoint[floorId][x + "," + y] || {}; + flags.enemyOnPoint[floorId][x + "," + y][name] = value; + if (!norefresh) core.updateStatusBar(); +} + +////// 重置某个点上的怪物属性 ////// +events.prototype.resetEnemyOnPoint = function (x, y, floorId, norefresh) { + delete ((flags.enemyOnPoint || {})[floorId || core.status.floorId] || {})[x + "," + y]; + if (!norefresh) core.updateStatusBar(); +} + +////// 将某个点上已经设置的怪物属性移动到其他点 ////// +events.prototype.moveEnemyOnPoint = function (fromX, fromY, toX, toY, floorId, norefresh) { + floorId = floorId || core.status.floorId; + if (((flags.enemyOnPoint || {})[floorId] || {})[fromX + "," + fromY]) { + flags.enemyOnPoint[floorId][toX + "," + toY] = flags.enemyOnPoint[floorId][fromX + "," + fromY]; + delete flags.enemyOnPoint[floorId][fromX + "," + fromY]; + if (!norefresh) core.updateStatusBar(); + } +} + +////// 设置楼层属性 ////// +events.prototype.setFloorInfo = function (name, value, floorId, prefix) { + floorId = floorId || core.status.floorId; + core.status.maps[floorId][name] = value; + core.updateStatusBar(); +} + +////// 设置全塔属性 ////// +events.prototype.setGlobalAttribute = function (name, value) { + if (typeof value == 'string') { + if ((value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') + || (value.charAt(0) == "'" && value.charAt(value.length - 1) == "'")) + value = value.substring(1, value.length - 1); + // --- 检查 [] + if (value.charAt(0) == '[' && value.charAt(value.length - 1) == ']') + value = eval(value); + // --- 检查颜色 + if (/^[0-9 ]+,[0-9 ]+,[0-9 ]+(,[0-9. ]+)?$/.test(value)) { + value = 'rgba(' + value + ')'; + } + } + core.status.globalAttribute[name] = value; + core.setFlag('globalAttribute', core.status.globalAttribute); + core.resize(); +} + +////// 设置全局开关 ////// +events.prototype.setGlobalFlag = function (name, value) { + var flags = core.getFlag("globalFlags", {}); + if (name.startsWith('s:')) { + name = name.substring(2); + var statusBarItems = core.flags.statusBarItems.filter(function (v) { return v != name; }); + if (value) statusBarItems.push(name); + core.flags.statusBarItems = statusBarItems; + flags.statusBarItems = core.clone(statusBarItems); + } else { + flags[name] = core.flags[name] = value; + } + core.setFlag("globalFlags", flags); + core.resize(); + if (name == 'blurFg') + core.redrawMap(); +} + +////// 设置文件别名 ////// +events.prototype.setNameMap = function (name, value) { + if (!core.hasFlag('__nameMap__')) core.setFlag('__nameMap__', {}); + flags.__nameMap__[name] = value; +} + +////// 设置剧情文本的属性 ////// +events.prototype.setTextAttribute = function (data) { + if (!core.isPlaying()) return; + ["position", "offset", "align", "bold", "titlefont", "textfont", "lineHeight", "time", "letterSpacing", "animateTime"].forEach(function (t) { + if (data[t] != null) core.status.textAttribute[t] = data[t]; + }); + ["background", "title", "text"].forEach(function (t) { + if ((data[t] instanceof Array) && data[t].length >= 3) { + if (data[t].length == 3) data[t].push(1); + core.status.textAttribute[t] = data[t]; + } + if (t == 'background') { + var name = core.getMappedName(data[t]); + var img = core.material.images.images[name]; + if (img && img.width == 192 && img.height == 128) { + core.status.textAttribute[t] = name; + } + } + }); + if (main.mode == 'play') core.setFlag('textAttribute', core.status.textAttribute); +} + +events.prototype.moveTextBox = function (code, loc, relative, moveMode, time, callback) { + var ctx = core.getContextByName('__text__' + code); + if (!ctx) { + if (callback) callback(); + return; + } + var sx = parseInt(ctx.canvas.getAttribute('_text_left')) || 0; + var sy = parseInt(ctx.canvas.getAttribute('_text_top')) || 0; + var dx = relative ? loc[0] : (loc[0] - sx); + var dy = relative ? loc[1] : (loc[1] - sy); + var ox = parseInt(ctx.canvas.getAttribute('_left')) || 0; + var oy = parseInt(ctx.canvas.getAttribute('_top')) || 0; + + if (!time) { + core.relocateCanvas(ctx, ox + dx, oy + dy); + ctx.canvas.setAttribute('_text_left', loc[0]); + ctx.canvas.setAttribute('_text_top', loc[1]); + if (callback) callback(); + return; + } + + var moveInfo = { + sx: sx, sy: sy, dx: dx, dy: dy, ox: ox, oy: oy, + moveMode: moveMode, time: time / Math.max(core.status.replay.speed, 1) + }; + this._moveTextBox_moving(ctx, moveInfo, callback); +} + +events.prototype._moveTextBox_moving = function (ctx, moveInfo, callback) { + var step = 0, steps = moveInfo.time / 10; + if (steps <= 0) steps = 1; + var moveFunc = core.applyEasing(moveInfo.moveMode); + var animate = setInterval(function () { + step++; + var dx = moveInfo.dx * moveFunc(step / steps); + var dy = moveInfo.dy * moveFunc(step / steps); + core.relocateCanvas(ctx, parseInt(moveInfo.ox + dx), parseInt(moveInfo.oy + dy)); + ctx.canvas.setAttribute('_text_left', moveInfo.sx + dx); + ctx.canvas.setAttribute('_text_top', moveInfo.sy + dy); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, 10); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 清除对话框 ////// +events.prototype.clearTextBox = function (code, callback) { + if (code == null) { + code = Object.keys(core.dymCanvas).filter(function (one) { return one.startsWith('__text__') }) + .map(function (one) { return one.substring('__text__'.length); }) + } + + if (!(code instanceof Array)) code = [code]; + var index = 0; + var _work = function () { + if (index == code.length) { + if (callback) callback(); + return; + } + var ctx = '__text__' + code[index++]; + if (!core.getContextByName(ctx)) return _work(); + core.ui._animateUI('hide', ctx, function () { + core.deleteCanvas(ctx); + _work(); + }); + }; + _work(); +} + +////// 关门 ////// +events.prototype.closeDoor = function (x, y, id, callback) { + id = id || ""; + if ((core.material.icons.animates[id] == null && core.material.icons.npc48[id] == null) + || core.getBlock(x, y) != null) { + if (callback) callback(); + return; + } + var block = core.getBlockById(id); + var doorInfo = (block.event || {}).doorInfo; + if (!doorInfo) { + if (callback) callback(); + return; + } + + core.playSound(doorInfo.closeSound); + var blockInfo = core.getBlockInfo(block); + var speed = (doorInfo.time || 160) / 4; + blockInfo.posX = 3; + core.maps._drawBlockInfo(blockInfo, x, y); + + var cb = function () { + core.setBlock(id, x, y); + core.showBlock(x, y); + if (callback) callback(); + } + + var animate = window.setInterval(function () { + blockInfo.posX--; + if (blockInfo.posX < 0) { + clearInterval(animate); + delete core.animateFrame.asyncId[animate]; + cb(); + return; + } + core.maps._drawBlockInfo(blockInfo, x, y); + }, core.status.replay.speed == 24 ? 1 : speed / Math.max(core.status.replay.speed, 1)); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +////// 显示图片 ////// +events.prototype.showImage = function (code, image, sloc, loc, opacityVal, time, callback) { + var imageName = null; + if (typeof image == 'string') { + imageName = image; + if (image.endsWith(':x') || image.endsWith(':y') || image.endsWith(':o')) { + image = image.substring(0, image.length - 2); + } + image = core.getMappedName(image); + image = core.material.images.images[image]; + } + if (!image) { + if (callback) callback(); + return; + } + sloc = sloc || []; + var sx = core.calValue(sloc[0]) || 0, sy = core.calValue(sloc[1]) || 0; + var sw = core.calValue(sloc[2]), sh = core.calValue(sloc[3]); + if (sw == null) sw = image.width; + if (sh == null) sh = image.height; + loc = loc || []; + var x = core.calValue(loc[0]) || 0, y = core.calValue(loc[1]) || 0; + var w = core.calValue(loc[2]), h = core.calValue(loc[3]); + if (w == null) w = sw; + if (h == null) h = sh; + var zIndex = code + 100; + time = time || 0; + var name = "image" + zIndex; + var ctx = core.createCanvas(name, x, y, w, h, zIndex); + core.drawImage(ctx, imageName == null ? image : imageName, sx, sy, sw, sh, 0, 0, w, h); + if (time == 0) { + core.setOpacity(name, opacityVal); + if (callback) callback(); + return; + } + core.setOpacity(name, 0); + this.moveImage(code, null, opacityVal, null, time, callback); +} + +////// 隐藏图片 ////// +events.prototype.hideImage = function (code, time, callback) { + time = time || 0; + var name = "image" + (code + 100); + if (time == 0 || !core.dymCanvas[name]) { + core.deleteCanvas(name); + if (callback) callback(); + return; + } + this.moveImage(code, null, 0, null, time, function () { + core.deleteCanvas(name); + if (callback) callback(); + }); +} + +////// 移动图片 ////// +events.prototype.moveImage = function (code, to, opacityVal, moveMode, time, callback) { + to = to || []; + var name = "image" + (code + 100); + if (!core.dymCanvas[name]) { + if (callback) callback(); + return; + } + var getOrDefault = function (a, b) { + a = core.calValue(a); + return a != null ? a : b; + } + var canvas = core.dymCanvas[name].canvas; + var fromX = parseFloat(canvas.getAttribute("_left")), + fromY = parseFloat(canvas.getAttribute("_top")), + toX = getOrDefault(to[0], fromX), toY = getOrDefault(to[1], fromY); + + var opacity = parseFloat(canvas.style.opacity), toOpacity = getOrDefault(opacityVal, opacity); + + if (!time) { + core.relocateCanvas(name, toX, toY); + core.setOpacity(toOpacity); + if (callback) callback(); + return; + } + + this._moveImage_moving(name, { + fromX: fromX, fromY: fromY, toX: toX, toY: toY, opacity: opacity, toOpacity: toOpacity, + moveMode: moveMode, time: time / Math.max(core.status.replay.speed, 1) + }, callback) +} + +events.prototype._moveImage_moving = function (name, moveInfo, callback) { + var per_time = 10, step = 0, steps = parseInt(moveInfo.time / per_time); + if (steps <= 0) steps = 1; + var fromX = moveInfo.fromX, fromY = moveInfo.fromY, toX = moveInfo.toX, toY = moveInfo.toY, + opacity = moveInfo.opacity, toOpacity = moveInfo.toOpacity; + var currX = fromX, currY = fromY, currOpacity = opacity; + var moveFunc = core.applyEasing(moveInfo.moveMode); + var animate = setInterval(function () { + step++; + currOpacity = opacity + (toOpacity - opacity) * moveFunc(step / steps); + currX = parseInt(fromX + (toX - fromX) * moveFunc(step / steps)); + currY = parseInt(fromY + (toY - fromY) * moveFunc(step / steps)); + core.setOpacity(name, currOpacity); + core.relocateCanvas(name, currX, currY); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 旋转图片 ////// +events.prototype.rotateImage = function (code, center, angle, moveMode, time, callback) { + center = center || []; + var name = "image" + (code + 100); + if (!core.dymCanvas[name]) { + if (callback) callback(); + return; + } + var canvas = core.dymCanvas[name].canvas; + var centerX = core.calValue(center[0]), centerY = core.calValue(center[1]); + + var fromAngle = parseFloat(canvas.getAttribute('_angle')) || 0; + + if (!time) { + core.rotateCanvas(name, fromAngle + angle, centerX, centerY); + if (callback) callback(); + return; + } + + var rotateInfo = { + fromAngle: fromAngle, angle: angle, centerX: centerX, centerY: centerY, + moveMode: moveMode, time: time / Math.max(core.status.replay.speed, 1) + } + this._rotateImage_rotating(name, rotateInfo, callback); +} + +events.prototype._rotateImage_rotating = function (name, rotateInfo, callback) { + var per_time = 10, step = 0, steps = parseInt(rotateInfo.time / per_time); + if (steps <= 0) steps = 1; + var moveFunc = core.applyEasing(rotateInfo.moveMode); + var animate = setInterval(function () { + step++; + var currAngle = rotateInfo.fromAngle + rotateInfo.angle * moveFunc(step / steps); + core.rotateCanvas(name, currAngle, rotateInfo.centerX, rotateInfo.centerY); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 放缩一张图片 ////// +events.prototype.scaleImage = function (code, center, scale, moveMode, time, callback) { + center = center || []; + var name = "image" + (code + 100); + if (!core.dymCanvas[name]) { + if (callback) callback(); + return; + } + var ctx = core.dymCanvas[name]; + var currScale = 1.0; + if (ctx.canvas.hasAttribute('_scale')) { + currScale = parseFloat(ctx.canvas.getAttribute('_scale')); + } + var ratio = ctx.canvas.hasAttribute('isHD') ? core.domStyle.ratio : 1; + var width = ctx.canvas.width / ratio, height = ctx.canvas.height / ratio; + var currLeft = parseFloat(ctx.canvas.getAttribute("_left")); + var currTop = parseFloat(ctx.canvas.getAttribute("_top")); + var centerX = core.calValue(center[0]), centerY = core.calValue(center[1]); + if (centerX == null || centerY == null) { + centerX = currLeft + width * currScale / 2; + centerY = currTop + height * currScale / 2; + } + var scaleInfo = { + x: (currLeft - centerX) / currScale, y: (currTop - centerY) / currScale, centerX: centerX, centerY: centerY, + width: width, height: height, currScale: currScale, scale: scale, moveMode: moveMode, time: time + } + this._scaleImage_scale(ctx, scaleInfo, callback); +} + +events.prototype._scaleInfo_scale = function (ctx, scaleInfo, scale) { + core.resizeCanvas(ctx, scaleInfo.width * scale, scaleInfo.height * scale, true); + core.relocateCanvas(ctx, scaleInfo.centerX + scaleInfo.x * scale, scaleInfo.centerY + scaleInfo.y * scale); + ctx.canvas.setAttribute('_scale', scale); +} + +events.prototype._scaleImage_scale = function (ctx, scaleInfo, callback) { + if (!scaleInfo.time) { + this._scaleInfo_scale(ctx, scaleInfo, scaleInfo.scale); + if (callback) callback(); + return; + } + + var per_time = 10, step = 0, steps = parseInt(scaleInfo.time / per_time); + if (steps <= 0) steps = 1; + var moveFunc = core.applyEasing(scaleInfo.moveMode); + + var animate = setInterval(function () { + step++; + var scale = scaleInfo.currScale + (scaleInfo.scale - scaleInfo.currScale) * moveFunc(step / steps); + core.events._scaleInfo_scale(ctx, scaleInfo, scale); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 绘制或取消一张gif图片 ////// +events.prototype.showGif = function (name, x, y) { + name = core.getMappedName(name); + var image = core.material.images.images[name]; + if (image) { + var gif = new Image(); + gif.src = image.src; + gif.style.position = 'absolute'; + gif.style.left = x * core.domStyle.scale + "px"; + gif.style.top = y * core.domStyle.scale + "px"; + gif.style.width = image.width * core.domStyle.scale + "px"; + gif.style.height = image.height * core.domStyle.scale + "px"; + core.dom.gif2.appendChild(gif); + } + else { + core.dom.gif2.innerHTML = ""; + } +} + +////// 淡入淡出音乐 ////// +events.prototype.setVolume = function (value, time, callback) { + var set = function (value) { + core.musicStatus.designVolume = value; + if (core.musicStatus.playingBgm) + core.material.bgms[core.musicStatus.playingBgm].volume = core.musicStatus.userVolume * core.musicStatus.designVolume; + } + if (!time || time < 100) { + set(value); + if (callback) callback(); + return; + } + var currVolume = core.musicStatus.designVolume; + time /= Math.max(core.status.replay.speed, 1); + var per_time = 10, step = 0, steps = parseInt(time / per_time); + if (steps <= 0) steps = 1; + var animate = setInterval(function () { + step++; + set(currVolume + (value - currVolume) * step / steps); + if (step >= steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + if (callback) callback(); + } + }, per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = callback; +} + +////// 画面震动 ////// +events.prototype.vibrate = function (direction, time, speed, power, callback) { + if (core.isReplaying()) { + if (callback) callback(); + return; + } + if (!time) time = 1000; + speed = speed || 10; + power = power || 10; + var shakeInfo = { duration: parseInt(time / 10), speed: speed, power: power, direction: 1, shake: 0 }; + if (direction == 'random') { + direction = ['horizontal', 'vertical', 'diagonal1', 'diagonal2'][Math.floor(Math.random() * 4)]; + } + var cb = function () { + core.addGameCanvasTranslate(0, 0); + if (callback) callback(); + } + var animate = setInterval(function () { + core.events._vibrate_update(shakeInfo); + switch (direction) { + case 'vertical': core.addGameCanvasTranslate(0, shakeInfo.shake); break; + case 'diagonal1': core.addGameCanvasTranslate(shakeInfo.shake, shakeInfo.shake); break; + case 'diagonal2': core.addGameCanvasTranslate(-shakeInfo.shake, shakeInfo.shake); break; + default: core.addGameCanvasTranslate(shakeInfo.shake, 0); + } + if (shakeInfo.duration === 0 && shakeInfo.shake == 0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + }, 10); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +events.prototype._vibrate_update = function (shakeInfo) { + if (shakeInfo.duration >= 1 || shakeInfo.shake != 0) { + var delta = shakeInfo.speed * shakeInfo.direction / 6; + if (shakeInfo.duration <= 1 && shakeInfo.shake * (shakeInfo.shake + delta) < 0) { + shakeInfo.shake = 0; + } else { + shakeInfo.shake += delta; + } + if (shakeInfo.shake > shakeInfo.power) { + shakeInfo.direction = -1; + } + if (shakeInfo.shake < -shakeInfo.power) { + shakeInfo.direction = 1; + } + if (shakeInfo.duration >= 1) { + shakeInfo.duration -= 1 + } + } +} + +/////// 使用事件让勇士移动。这个函数将不会触发任何事件 ////// +events.prototype.eventMoveHero = function (steps, time, callback) { + time = time || core.values.moveSpeed; + var step = 0, moveSteps = (steps || []).map(function (t) { + return [t.split(':')[0], parseInt(t.split(':')[1] || "1")]; + }).filter(function (t) { + return ['up', 'down', 'left', 'right', 'forward', 'backward', 'leftup', 'leftdown', 'rightup', 'rightdown', 'speed'].indexOf(t[0]) >= 0 + && !(t[0] == 'speed' && t[1] < 16); + }); + core.status.heroMoving = -1; + var _run = function () { + var cb = function () { + core.status.heroMoving = 0; + core.drawHero(); + if (callback) callback(); + } + + var animate = window.setInterval(function () { + if (moveSteps.length == 0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + else { + if (step == 0 && moveSteps[0][0] == 'speed' && moveSteps[0][1] >= 16) { + time = moveSteps[0][1]; + moveSteps.shift(); + clearInterval(animate); + delete core.animateFrame.asyncId[animate]; + _run(); + } + else if (core.events._eventMoveHero_moving(++step, moveSteps)) + step = 0; + } + }, core.status.replay.speed == 24 ? 1 : time / 8 / core.status.replay.speed); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; + } + _run(); +} + +events.prototype._eventMoveHero_moving = function (step, moveSteps) { + var curr = moveSteps[0]; + var direction = curr[0], x = core.getHeroLoc('x'), y = core.getHeroLoc('y'); + // ------ 前进/后退 + var o = direction == 'backward' ? -1 : 1; + if (direction == 'forward' || direction == 'backward') direction = core.getHeroLoc('direction'); + var faceDirection = direction; + if (direction == 'leftup' || direction == 'leftdown') faceDirection = 'left'; + if (direction == 'rightup' || direction == 'rightdown') faceDirection = 'right'; + core.setHeroLoc('direction', direction); + if (curr[1] <= 0) { + core.setHeroLoc('direction', faceDirection); + moveSteps.shift(); + return true; + } + if (step <= 4) { + core.drawHero('leftFoot', 4 * o * step); + } + else if (step <= 8) { + core.drawHero('rightFoot', 4 * o * step); + } + if (step == 8) { + core.setHeroLoc('x', x + o * core.utils.scan2[direction].x, true); + core.setHeroLoc('y', y + o * core.utils.scan2[direction].y, true); + core.updateFollowers(); + curr[1]--; + if (curr[1] <= 0) moveSteps.shift(); + core.setHeroLoc('direction', faceDirection); + return true; + } + return false; +} + +////// 勇士跳跃事件 ////// +events.prototype.jumpHero = function (ex, ey, time, callback) { + var sx = core.getHeroLoc('x'), sy = core.getHeroLoc('y'); + if (ex == null) ex = sx; + if (ey == null) ey = sy; + var sx = core.status.hero.loc.x, sy = core.status.hero.loc.y; + if (!core.isset(ex)) ex = sx; + if (!core.isset(ey)) ey = sy; + var jumpInfo = core.maps.__generateJumpInfo(sx, sy, ex, ey, time || 500); + jumpInfo.icon = core.material.icons.hero[core.getHeroLoc('direction')]; + jumpInfo.width = core.material.icons.hero.width || 32; + jumpInfo.height = core.material.icons.hero.height; + + this._jumpHero_doJump(jumpInfo, callback); +} + +events.prototype._jumpHero_doJump = function (jumpInfo, callback) { + var cb = function () { + core.setHeroLoc('x', jumpInfo.ex); + core.setHeroLoc('y', jumpInfo.ey); + core.status.heroMoving = 0; + core.drawHero(); + if (callback) callback(); + } + + core.status.heroMoving = -1; + var animate = window.setInterval(function () { + if (jumpInfo.jump_count > 0) + core.events._jumpHero_jumping(jumpInfo) + else { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + }, jumpInfo.per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +events.prototype._jumpHero_jumping = function (jumpInfo) { + core.clearMap('hero'); + core.maps.__updateJumpInfo(jumpInfo); + var x = core.getHeroLoc('x'), + y = core.getHeroLoc('y'); + var nowx = jumpInfo.px, nowy = jumpInfo.py, width = jumpInfo.width || 32, height = jumpInfo.height; + core.drawHero('stop', { x: nowx - 32 * x, y: nowy - 32 * y }); +} + +////// 设置角色行走图 ////// +events.prototype.setHeroIcon = function (name, noDraw) { + name = core.getMappedName(name); + var img = core.material.images.images[name]; + if (!img) { + console.error("找不到图片: " + img); + return; + } + if (core.material.images.hero == img) return; + core.status.hero.image = name; + core.material.images.hero = img; + core.material.icons.hero.width = img.width / 4; + core.material.icons.hero.height = img.height / 4; + core.control.updateHeroIcon(name); + if (!noDraw) core.drawHero(); +} + +////// 检查升级事件 ////// +events.prototype.checkLvUp = function () { + var actions = []; + while (true) { + var next = this._checkLvUp_check(); + if (next == null) break; + actions = actions.concat(next); + } + if (actions.length > 0) core.insertAction(actions); +} + +events.prototype._checkLvUp_check = function () { + if (core.flags.statusBarItems.indexOf('enableLevelUp') < 0 || !core.firstData.levelUp + || core.status.hero.lv >= core.firstData.levelUp.length) return null; + // 计算下一个所需要的数值 + var next = (core.firstData.levelUp[core.status.hero.lv] || {}); + var need = core.calValue(next.need); + if (need == null) return null; + if (core.status.hero.exp >= need) { + // 升级 + core.status.hero.lv++; + if (next.clear) core.status.hero.exp -= need; + return next.action || []; + } + return null; +} + +////// 尝试使用道具 ////// +events.prototype.tryUseItem = function (itemId) { + if (itemId == 'book') { + core.ui.closePanel(); + return core.openBook(false); + } + if (itemId == 'fly') { + core.ui.closePanel(); + return core.useFly(false); + } + if (itemId == 'centerFly') { + core.ui.closePanel(); + return core.ui._drawCenterFly(); + } + if (core.canUseItem(itemId)) { + core.ui.closePanel(); + core.useItem(itemId); + } else { + core.playSound('操作失败'); + core.drawTip("当前无法使用" + core.material.items[itemId].name, itemId); + } +} diff --git a/libs/extensions.js b/libs/extensions.js new file mode 100644 index 0000000..873e70d --- /dev/null +++ b/libs/extensions.js @@ -0,0 +1,51 @@ + +/* +extensions.js:负责拓展插件 + */ + +"use strict"; + +function extensions () { + +} + +extensions.prototype._load = function (callback) { + if (main.replayChecking) return callback(); + if (!window.fs) { + this._loadJs('_server/fs.js', function () { + core.extensions._listExtensions(callback); + }, callback); + } else this._listExtensions(callback); +} + +extensions.prototype._loadJs = function (file, callback, onerror) { + var script = document.createElement('script'); + script.src = file + '?v=' + main.version; + script.onload = callback; + script.onerror = onerror; + main.dom.body.appendChild(script); +} + +extensions.prototype._listExtensions = function (callback) { + if (!window.fs) return callback(); + fs.readdir('extensions', function (error, data) { + if (error || !(data instanceof Array)) return callback(); + var list = []; + data.forEach(function (name) { + if (/^[\w.-]+\.js$/.test(name)) { + list.push(name); + } + }); + list.sort(); + core.extensions._loadExtensions(list, callback); + }); +} + +extensions.prototype._loadExtensions = function (list, callback) { + var i = 0; + var load = function () { + if (i == list.length) return callback(); + core.extensions._loadJs('extensions/' + list[i++], load, load); + } + load(); +} diff --git a/libs/icons.js b/libs/icons.js new file mode 100644 index 0000000..7efd6ef --- /dev/null +++ b/libs/icons.js @@ -0,0 +1,77 @@ + +"use strict"; + +function icons () { + this._init(); +} + +icons.prototype._init = function () { + this.icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1; + //delete(icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1); + + // tileset的起点 + this.tilesetStartOffset = 10000; +} + +icons.prototype.getIcons = function () { + var icons = core.clone(this.icons); + icons.hero.leftup = icons.hero.leftdown = icons.hero.left; + icons.hero.rightup = icons.hero.rightdown = icons.hero.right; + return icons; +} + +////// 根据道具ID获得其cls ////// +icons.prototype.getClsFromId = function (id) { + for (var cls in core.material.icons) { + if (cls != 'hero' && id in core.material.icons[cls]) + return cls; + } + return null; +} + +icons.prototype.getAllIconIds = function () { + if (this.allIconIds) return this.allIconIds; + this.allIconIds = []; + for (var type in this.icons) { + this.allIconIds = this.allIconIds.concat(Object.keys(this.icons[type])); + } + return this.allIconIds; +} + +icons.prototype._getAnimateFrames = function (cls) { + if (cls == 'enemys' || cls == 'npcs') { + return 2; + } + if (cls == 'animates' || cls == 'enemy48' || cls == 'npc48') { + return 4; + } + return 1; +} + +////// 根据图块数字或ID获得所在的tileset和坐标信息 ////// +icons.prototype.getTilesetOffset = function (id) { + + if (typeof id == 'string') { + id = core.getIdOfThis(id); + // Tileset的ID必须是 X+数字 的形式 + if (!/^X\d+$/.test(id)) return null; + id = parseInt(id.substring(1)); + } + else if (typeof id != 'number') { + return null; + } + + core.tilesets = core.tilesets || []; + var startOffset = this.tilesetStartOffset; + for (var i in core.tilesets) { + var imgName = core.tilesets[i]; + var img = core.material.images.tilesets[imgName]; + var width = Math.floor(parseInt(img.getAttribute('_width')) / 32), height = Math.floor(parseInt(img.getAttribute('_height')) / 32); + if (id >= startOffset && id < startOffset + width * height) { + var x = (id - startOffset) % width, y = parseInt((id - startOffset) / width); + return { "image": imgName, "x": x, "y": y }; + } + startOffset += this.tilesetStartOffset; + } + return null; +} diff --git a/libs/items.js b/libs/items.js new file mode 100644 index 0000000..593631a --- /dev/null +++ b/libs/items.js @@ -0,0 +1,434 @@ + +"use strict"; + +function items () { + this._init(); +} + +////// 初始化 ////// +items.prototype._init = function () { + this.items = items_296f5d02_12fd_4166_a7c1_b5e830c9ee3a; + for (var itemId in this.items) { + this.items[itemId].id = itemId; + } +} + +////// 获得所有道具 ////// +items.prototype.getItems = function () { + var items = core.clone(this.items); + var equipInfo = core.getFlag('equipInfo'); + if (equipInfo) { + for (var id in equipInfo) { + items[id].equip = core.clone(equipInfo[id]); + } + } + return items; +} + +////// “即捡即用类”道具的使用效果 ////// +items.prototype.getItemEffect = function (itemId, itemNum) { + var itemCls = core.material.items[itemId].cls; + // 消耗品 + if (itemCls === 'items') { + var curr_hp = core.status.hero.hp; + var itemEffect = core.material.items[itemId].itemEffect; + if (itemEffect) { + try { + for (var i = 0; i < itemNum; ++i) + eval(itemEffect); + } + catch (e) { + console.error(e); + } + } + core.status.hero.statistics.hp += core.status.hero.hp - curr_hp; + + var useItemEvent = core.material.items[itemId].useItemEvent; + if (useItemEvent) { + try { + core.insertAction(useItemEvent); + } + catch (e) { + console.error(e); + } + } + core.updateStatusBar(false, true); + } + else { + core.addItem(itemId, itemNum); + } +} + +////// “即捡即用类”道具的文字提示 ////// +items.prototype.getItemEffectTip = function (itemId) { + var itemCls = core.material.items[itemId].cls; + // 消耗品 + if (itemCls === 'items') { + var itemEffectTip = core.material.items[itemId].itemEffectTip; + if (itemEffectTip) { + try { + return core.replaceText(itemEffectTip) || ""; + } catch (e) { + console.error(e); + return ""; + } + } + } + return ""; +} + +////// 使用道具 ////// +items.prototype.useItem = function (itemId, noRoute, callback) { + if (!this.canUseItem(itemId)) { + if (callback) callback(); + return; + } + // 执行道具效果 + this._useItemEffect(itemId); + // 执行完毕 + this._afterUseItem(itemId); + // 记录路线 + if (!noRoute) core.status.route.push("item:" + itemId); + if (callback) callback(); +} + +items.prototype._useItemEffect = function (itemId) { + var useItemEffect = core.material.items[itemId].useItemEffect; + if (useItemEffect) { + try { + eval(useItemEffect); + } + catch (e) { + console.error(e); + } + } + var useItemEvent = core.material.items[itemId].useItemEvent; + if (useItemEvent) { + try { + core.insertAction(useItemEvent); + } + catch (e) { + console.error(e); + } + } +} + +items.prototype._afterUseItem = function (itemId) { + // 道具使用完毕:删除 + var itemCls = core.material.items[itemId].cls; + if (itemCls == 'tools') + core.status.hero.items[itemCls][itemId]--; + if (core.status.hero.items[itemCls][itemId] <= 0) + delete core.status.hero.items[itemCls][itemId]; + core.updateStatusBar(false, true); +} + +////// 当前能否使用道具 ////// +items.prototype.canUseItem = function (itemId) { + // 没有道具 + if (!core.hasItem(itemId)) return false; + + var canUseItemEffect = core.material.items[itemId].canUseItemEffect; + if (canUseItemEffect) { + try { + return eval(canUseItemEffect); + } + catch (e) { + console.error(e); + return false; + } + } +} + +////// 获得某个物品的个数 ////// +items.prototype.itemCount = function (itemId) { + if (!core.material.items[itemId] || !core.isPlaying()) return 0; + var itemCls = core.material.items[itemId].cls; + if (itemCls == "items") return 0; + return core.status.hero.items[itemCls][itemId] || 0; +} + +////// 是否存在某个物品 ////// +items.prototype.hasItem = function (itemId) { + return this.itemCount(itemId) > 0; +} + +////// 是否装备某件装备 ////// +items.prototype.hasEquip = function (itemId) { + if (!(core.material.items[itemId] || {}).equip || !core.isPlaying()) return null; + + for (var i in core.status.hero.equipment) + if (core.status.hero.equipment[i] == itemId) + return true; + return false +} + +////// 获得某个装备类型的当前装备 ////// +items.prototype.getEquip = function (equipType) { + return core.status.hero.equipment[equipType] || null; +} + +////// 设置某个物品的个数 ////// +items.prototype.setItem = function (itemId, itemNum) { + itemNum = itemNum || 0; + var itemCls = core.material.items[itemId].cls; + if (itemCls == 'items') return; + + core.status.hero.items[itemCls][itemId] = itemNum; + if (core.status.hero.items[itemCls][itemId] <= 0) { + delete core.status.hero.items[itemCls][itemId]; + } + core.updateStatusBar(); +} + +////// 增加某个物品的个数 ////// +items.prototype.addItem = function (itemId, itemNum) { + if (itemNum == null) itemNum = 1; + var itemData = core.material.items[itemId]; + var itemCls = itemData.cls; + if (itemCls == 'items') return; + if (core.status.hero.items[itemCls][itemId] == null) { + core.status.hero.items[itemCls][itemId] = 0; + } + core.status.hero.items[itemCls][itemId] += itemNum; + if (core.status.hero.items[itemCls][itemId] <= 0) { + delete core.status.hero.items[itemCls][itemId]; + } + // 永久道具只能有一个 + if (itemCls == 'constants' && core.status.hero.items[itemCls][itemId] > 1) + core.status.hero.items[itemCls][itemId] = 1; + core.updateStatusBar(); +} + +////// 删除某个物品 ////// +items.prototype.removeItem = function (itemId, itemNum) { + if (itemNum == null) itemNum = 1; + if (!core.hasItem(itemId)) return false; + var itemCls = core.material.items[itemId].cls; + core.status.hero.items[itemCls][itemId] -= itemNum; + if (core.status.hero.items[itemCls][itemId] <= 0) { + delete core.status.hero.items[itemCls][itemId]; + } + core.updateStatusBar(); + return true; +} + +// ---------- 装备相关 ------------ // + +items.prototype.getEquipTypeByName = function (name) { + var names = core.status.globalAttribute.equipName; + var types = []; + for (var i = 0; i < names.length; ++i) { + if (names[i] === name) { + types.push(i); + if (!core.status.hero.equipment[i]) return i; + } + } + return types.length == 1 ? types[0] : -1; +} + +items.prototype.getEquipTypeById = function (equipId) { + var type = core.material.items[equipId].equip.type; + if (typeof type == 'string') + type = this.getEquipTypeByName(type); + return type; +} + +// 当前能否撞上某装备 +items.prototype.canEquip = function (equipId, hint) { + // 装备是否合法 + var equip = core.material.items[equipId] || {}; + if (!equip.equip) { + if (hint) { + core.playSound('操作失败'); + core.drawTip("不合法的装备!"); + } + return false; + } + + // 是否拥有该装备 + if (!core.hasItem(equipId) && !core.hasEquip(equipId)) { + if (hint) { + core.playSound('操作失败'); + core.drawTip("你当前没有" + equip.name + ",无法换装"); + } + return false; + } + + // 可装备条件 + var canUseItemEffect = core.material.items[equipId].canUseItemEffect; + if (canUseItemEffect) { + try { + if (!eval(canUseItemEffect)) { + if (hint) { + core.playSound('操作失败'); + core.drawTip("当前不可换上" + equip.name); + } + return false; + } + } + catch (e) { + console.error(e); + return false; + } + } + return true; +} + +////// 换上 ////// +items.prototype.loadEquip = function (equipId, callback) { + if (!this.canEquip(equipId, true)) { + if (callback) callback(); + return; + } + + var loadEquip = core.material.items[equipId] || {}; + var type = this.getEquipTypeById(equipId); + if (type < 0) { + core.playSound('操作失败'); + core.drawTip("当前没有" + loadEquip.equip.type + "的空位!"); + if (callback) callback(); + return; + } + + this._realLoadEquip(type, equipId, core.status.hero.equipment[type], callback); +} + +////// 卸下 ////// +items.prototype.unloadEquip = function (equipType, callback) { + var unloadEquipId = core.status.hero.equipment[equipType]; + if (!unloadEquipId) { + if (callback) callback(); + return; + } + + this._realLoadEquip(equipType, null, unloadEquipId, callback); +} + +items.prototype.compareEquipment = function (compareEquipId, beComparedEquipId) { + var result = { "value": {}, "percentage": {} }; + var first = core.material.items[compareEquipId], second = core.material.items[beComparedEquipId]; + for (var one in result) { + for (var name in core.status.hero) { + if (typeof core.status.hero[name] == 'number') { + var ans = 0; + if (first) ans += ((first.equip || {})[one] || {})[name] || 0; + if (second) ans -= ((second.equip || {})[one] || {})[name] || 0; + if (ans != 0) result[one][name] = ans; + } + } + } + return result; +} + +////// 实际换装的效果 ////// +items.prototype._loadEquipEffect = function (equipId, unloadEquipId) { + // 比较能力值 + var result = core.compareEquipment(equipId, unloadEquipId); + + for (var name in result.percentage) + core.addBuff(name, result.percentage[name] / 100); + + for (var name in result.value) + core.status.hero[name] += result.value[name]; +} + +items.prototype._realLoadEquip = function (type, loadId, unloadId, callback) { + var loadEquip = core.material.items[loadId] || {}, unloadEquip = core.material.items[unloadId] || {}; + + // --- 音效 + this._realLoadEquip_playSound(); + + // --- 实际换装 + this._loadEquipEffect(loadId, unloadId); + + // --- 加减 + if (loadId) core.removeItem(loadId); + if (unloadId) core.addItem(unloadId); + core.status.hero.equipment[type] = loadId || null; + + // --- 提示 + if (loadId) core.drawTip("已装备上" + loadEquip.name, loadId); + else if (unloadId) core.drawTip("已卸下" + unloadEquip.name, unloadId); + + if (callback) callback(); +} + +items.prototype._realLoadEquip_playSound = function () { + if (core.hasFlag("__quickLoadEquip__")) return; + core.stopSound(); + core.playSound('穿脱装备'); +} + +////// 保存装备 ////// +items.prototype.quickSaveEquip = function (index) { + var saveEquips = core.getFlag("saveEquips", []); + saveEquips[index] = core.clone(core.status.hero.equipment); + core.setFlag("saveEquips", saveEquips); + core.status.route.push("saveEquip:" + index); + core.drawTip("已保存" + index + "号套装"); +} + +////// 读取装备 ////// +items.prototype.quickLoadEquip = function (index) { + var current = core.getFlag("saveEquips", [])[index]; + if (!current) { + core.playSound('操作失败'); + core.drawTip(index + "号套装不存在"); + return; + } + // 检查所有的装备 + var equipSize = core.status.globalAttribute.equipName.length; + for (var i = 0; i < equipSize; i++) { + var v = current[i]; + if (v && !this.canEquip(v, true)) + return; + } + core.status.route.push("loadEquip:" + index); + core.setFlag("__quickLoadEquip__", true); + // 快速换装 + var toEquip = []; + for (var i = 0; i < equipSize; i++) { + var now = core.status.hero.equipment[i]; + // --- 只考虑diff的装备 + var to = current[i]; + if (now != to) { + toEquip.push(to || null); + if (now) { + this.unloadEquip(i); + } + } + } + for (var i in toEquip) { + var to = toEquip[i]; + if (to) { + this.loadEquip(to); + } + } + core.removeFlag("__quickLoadEquip__"); + this._realLoadEquip_playSound(); + + core.drawTip("成功换上" + index + "号套装"); +} + +////// 设置装备属性 ////// +items.prototype.setEquip = function (equipId, valueType, name, value, operator, prefix) { + var equip = core.material.items[equipId]; + if (!equip || equip.cls != 'equips') return; + var equipInfo = equip.equip || {}; + if (!equipInfo[valueType]) equipInfo[valueType] = {}; + var toEquipInfo = core.clone(equipInfo); + toEquipInfo[valueType][name] = core.events._updateValueByOperator(core.calValue(value, prefix), equipInfo[valueType][name], operator); + // 如果是穿上状态,则还需要直接修改当前数值 + if (core.hasEquip(equipId)) { + // 设置一个临时装备,然后模拟换装操作 + var tempId = 'temp:' + equipId; + core.material.items[tempId] = { 'cls': 'equips', 'equip': core.clone(toEquipInfo) }; + this._loadEquipEffect(tempId, equipId); + delete core.material.items[tempId]; + core.updateStatusBar(); + } + equip.equip = core.clone(toEquipInfo); + flags.equipInfo = flags.equipInfo || {}; + flags.equipInfo[equipId] = core.clone(toEquipInfo); +} diff --git a/libs/loader.js b/libs/loader.js new file mode 100644 index 0000000..2b3b538 --- /dev/null +++ b/libs/loader.js @@ -0,0 +1,516 @@ + +/* +loader.js:负责对资源的加载 + + */ +"use strict"; + +function loader () { + this._init(); +} + +loader.prototype._init = function () { + +} + +////// 设置加载进度条进度 ////// +loader.prototype._setStartProgressVal = function (val) { + core.dom.startTopProgress.style.width = val + '%'; +} + +////// 设置加载进度条提示文字 ////// +loader.prototype._setStartLoadTipText = function (text) { + core.dom.startTopLoadTips.innerText = text; +} + +loader.prototype._load = function (callback) { + if (main.useCompress) { + this._load_async(callback); + } else { + this._load_sync(callback); + } +} + +loader.prototype._load_sync = function (callback) { + this._loadAnimates_sync(); + this._loadMusic_sync(); + core.loader._loadMaterials_sync(function () { + core.loader._loadExtraImages_sync(function () { + core.loader._loadAutotiles_sync(function () { + core.loader._loadTilesets_sync(callback); + }) + }) + }); +} + +loader.prototype._load_async = function (callback) { + core.loader._setStartLoadTipText('正在加载资源文件...'); + var all = {}; + + var _makeOnProgress = function (name) { + if (!all[name]) all[name] = { loaded: 0, total: 0, finished: false }; + return function (loaded, total) { + all[name].loaded = loaded; + all[name].total = total; + var allLoaded = 0, allTotal = 0; + for (var one in all) { + allLoaded += all[one].loaded; + allTotal += all[one].total; + } + if (allTotal > 0) { + if (allLoaded == allTotal) { + core.loader._setStartLoadTipText("正在处理资源文件... 请稍候..."); + } else { + core.loader._setStartLoadTipText('正在加载资源文件... ' + + core.formatSize(allLoaded) + " / " + core.formatSize(allTotal) + + " (" + (allLoaded / allTotal * 100).toFixed(2) + "%)"); + } + core.loader._setStartProgressVal(allLoaded / allTotal * 100); + } + }; + } + var _makeOnFinished = function (name) { + return function () { + setTimeout(function () { + all[name].finished = true; + for (var one in all) { + if (!all[one].finished) return; + } + callback(); + }); + } + } + + this._loadAnimates_async(_makeOnProgress('animates'), _makeOnFinished('animates')); + this._loadMusic_async(_makeOnProgress('sounds'), _makeOnFinished('sounds')); + this._loadMaterials_async(_makeOnProgress('materials'), _makeOnFinished('materials')); + this._loadExtraImages_async(_makeOnProgress('images'), _makeOnFinished('images')); + this._loadAutotiles_async(_makeOnProgress('autotiles'), _makeOnFinished('autotiles')); + this._loadTilesets_async(_makeOnProgress('tilesets'), _makeOnFinished('tilesets')); +} + +// ----- 加载资源文件 ------ // + +loader.prototype._loadMaterials_sync = function (callback) { + this._setStartLoadTipText("正在加载资源文件..."); + this.loadImages("materials", core.materials, core.material.images, function () { + core.loader._loadMaterials_afterLoad(); + callback(); + }); +} + +loader.prototype._loadMaterials_async = function (onprogress, onfinished) { + this.loadImagesFromZip('project/materials/materials.h5data', core.materials, core.material.images, onprogress, function () { + core.loader._loadMaterials_afterLoad(); + onfinished(); + }); +} + +loader.prototype._loadMaterials_afterLoad = function () { + var images = core.splitImage(core.material.images['icons']); + for (var key in core.statusBar.icons) { + if (typeof core.statusBar.icons[key] == 'number') { + core.statusBar.icons[key] = images[core.statusBar.icons[key]]; + if (core.statusBar.image[key] != null) + core.statusBar.image[key].src = core.statusBar.icons[key].src; + } + } +} + +// ------ 加载使用的图片 ------ // + +loader.prototype._loadExtraImages_sync = function (callback) { + core.material.images.images = {}; + this._setStartLoadTipText("正在加载图片文件..."); + core.loadImages("images", core.images, core.material.images.images, callback); +} + +loader.prototype._loadExtraImages_async = function (onprogress, onfinished) { + core.material.images.images = {}; + var images = core.images; + + // Check .gif + var gifs = images.filter(function (name) { + return name.toLowerCase().endsWith('.gif'); + }); + images = images.filter(function (name) { + return !name.toLowerCase().endsWith('.gif'); + }); + + this.loadImagesFromZip('project/images/images.h5data', images, core.material.images.images, onprogress, onfinished); + // gif没有被压缩在zip中,延迟加载... + gifs.forEach(function (gif) { + this.loadImage("images", gif, function (id, image) { + if (image != null) { + core.material.images.images[gif] = image; + } + }); + }, this); +} + +// ------ 加载自动元件 ------ // + +loader.prototype._loadAutotiles_sync = function (callback) { + core.material.images.autotile = {}; + var keys = Object.keys(core.material.icons.autotile); + var autotiles = {}; + + this._setStartLoadTipText("正在加载自动元件..."); + this.loadImages("autotiles", keys, autotiles, function () { + core.loader._loadAutotiles_afterLoad(keys, autotiles); + callback(); + }); +} + +loader.prototype._loadAutotiles_async = function (onprogress, onfinished) { + core.material.images.autotile = {}; + var keys = Object.keys(core.material.icons.autotile); + var autotiles = {}; + + this.loadImagesFromZip('project/autotiles/autotiles.h5data', keys, autotiles, onprogress, function () { + core.loader._loadAutotiles_afterLoad(keys, autotiles); + onfinished(); + }); +} + +loader.prototype._loadAutotiles_afterLoad = function (keys, autotiles) { + // autotile需要保证顺序 + keys.forEach(function (v) { + core.material.images.autotile[v] = autotiles[v]; + }); + + setTimeout(function () { + core.maps._makeAutotileEdges(); + }); + +} + +// ------ 加载额外素材 ------ // + +loader.prototype._loadTilesets_sync = function (callback) { + core.material.images.tilesets = {}; + this._setStartLoadTipText("正在加载额外素材..."); + this.loadImages("tilesets", core.tilesets, core.material.images.tilesets, function () { + core.loader._loadTilesets_afterLoad(); + callback(); + }); +} + +loader.prototype._loadTilesets_async = function (onprogress, onfinished) { + core.material.images.tilesets = {}; + this.loadImagesFromZip('project/tilesets/tilesets.h5data', core.tilesets, core.material.images.tilesets, onprogress, function () { + core.loader._loadTilesets_afterLoad(); + onfinished(); + }); +} + +loader.prototype._loadTilesets_afterLoad = function () { + // 检查宽高是32倍数,如果出错在控制台报错 + for (var imgName in core.material.images.tilesets) { + var img = core.material.images.tilesets[imgName]; + if (img.width % 32 != 0 || img.height % 32 != 0) { + console.warn("警告!" + imgName + "的宽或高不是32的倍数!"); + } + if (img.width * img.height > 32 * 32 * 3000) { + console.warn("警告!" + imgName + "上的图块素材个数大于3000!"); + } + } +} + +// ------ 实际加载一系列图片 ------ // + +loader.prototype.loadImages = function (dir, names, toSave, callback) { + if (!names || names.length == 0) { + if (callback) callback(); + return; + } + var items = 0; + for (var i = 0; i < names.length; i++) { + this.loadImage(dir, names[i], function (id, image) { + core.loader._setStartLoadTipText('正在加载图片 ' + id + "..."); + if (toSave[id] !== undefined) { + if (image != null) + toSave[id] = image; + return; + } + toSave[id] = image; + items++; + core.loader._setStartProgressVal(items * (100 / names.length)); + if (items == names.length) { + if (callback) callback(); + } + }) + } +} + +loader.prototype.loadImage = function (dir, imgName, callback) { + try { + var name = imgName; + if (name.indexOf(".") < 0) + name = name + ".png"; + var image = new Image(); + image.onload = function () { + image.setAttribute('_width', image.width); + image.setAttribute('_height', image.height); + callback(imgName, image); + } + image.onerror = function () { + callback(imgName, null); + } + image.src = 'project/' + dir + '/' + name + "?v=" + main.version; + if (name.endsWith('.gif')) + callback(imgName, null); + } + catch (e) { + console.error(e); + } +} + +// ------ 从zip中加载一系列图片 ------ // + +loader.prototype.loadImagesFromZip = function (url, names, toSave, onprogress, onfinished) { + if (!names || names.length == 0) { + if (onfinished) onfinished(); + return; + } + + core.unzip(url + "?v=" + main.version, function (data) { + var cnt = 1; + names.forEach(function (name) { + var imgName = name; + if (imgName.indexOf('.') < 0) imgName += '.png'; + if (imgName in data) { + var img = new Image(); + var url = URL.createObjectURL(data[imgName]); + cnt++; + img.onload = function () { + cnt--; + URL.revokeObjectURL(url); + img.setAttribute('_width', img.width); + img.setAttribute('_height', img.height); + if (cnt == 0 && onfinished) onfinished(); + } + img.src = url; + toSave[name] = img; + } + }); + cnt--; + if (cnt == 0 && onfinished) onfinished(); + }, null, false, onprogress); +} + +// ------ 加载动画文件 ------ // + +loader.prototype._loadAnimates_sync = function () { + this._setStartLoadTipText("正在加载动画文件..."); + + if (main.supportBunch) { + if (core.animates.length > 0) { + core.http('GET', '__all_animates__?v=' + main.version + '&id=' + core.animates.join(','), null, function (content) { + var u = content.split('@@@~~~###~~~@@@'); + for (var i = 0; i < core.animates.length; ++i) { + if (u[i] != '') { + core.material.animates[core.animates[i]] = core.loader._loadAnimate(u[i]); + } else { + console.error('无法找到动画文件' + core.animates[i] + '!'); + } + } + }, "text/plain; charset=x-user-defined"); + } + return; + } + + core.animates.forEach(function (t) { + core.http('GET', 'project/animates/' + t + ".animate?v=" + main.version, null, function (content) { + core.material.animates[t] = core.loader._loadAnimate(content); + }, function (e) { + console.error(e); + core.material.animates[t] = null; + }, "text/plain; charset=x-user-defined") + }); +} + +loader.prototype._loadAnimates_async = function (onprogress, onfinished) { + core.unzip('project/animates/animates.h5data?v=' + main.version, function (animates) { + for (var name in animates) { + if (name.endsWith(".animate")) { + var t = name.substring(0, name.length - 8); + if (core.animates.indexOf(t) >= 0) + core.material.animates[t] = core.loader._loadAnimate(animates[name]); + } + } + onfinished(); + }, null, true, onprogress); +} + +loader.prototype._loadAnimate = function (content) { + try { + content = JSON.parse(content); + var data = {}; + data.ratio = content.ratio; + data.se = content.se; + data.pitch = content.pitch; + data.images = []; + content.bitmaps.forEach(function (t2) { + if (!t2) { + data.images.push(null); + } + else { + try { + var image = new Image(); + image.src = t2; + data.images.push(image); + } catch (e) { + console.error(e); + data.images.push(null); + } + } + }) + data.frame = content.frame_max; + data.frames = []; + content.frames.forEach(function (t2) { + var info = []; + t2.forEach(function (t3) { + info.push({ + 'index': t3[0], + 'x': t3[1], + 'y': t3[2], + 'zoom': t3[3], + 'opacity': t3[4], + 'mirror': t3[5] || 0, + 'angle': t3[6] || 0, + }) + }) + data.frames.push(info); + }); + return data; + } + catch (e) { + console.error(e); + return null; + } +} + +// ------ 加载音乐和音效 ------ // + +loader.prototype._loadMusic_sync = function () { + this._setStartLoadTipText("正在加载音效文件..."); + core.bgms.forEach(function (t) { + core.loader.loadOneMusic(t); + }); + core.sounds.forEach(function (t) { + core.loader.loadOneSound(t); + }); + // 直接开始播放 + core.playBgm(main.startBgm); +} + +loader.prototype._loadMusic_async = function (onprogress, onfinished) { + core.bgms.forEach(function (t) { + core.loader.loadOneMusic(t); + }); + core.unzip('project/sounds/sounds.h5data?v=' + main.version, function (data) { + // 延迟解析 + setTimeout(function () { + for (var name in data) { + if (core.sounds.indexOf(name) >= 0) { + core.loader._loadOneSound_decodeData(name, data[name]); + } + } + }); + onfinished(); + }, null, false, onprogress); + + // 直接开始播放 + core.playBgm(main.startBgm); +} + +loader.prototype.loadOneMusic = function (name) { + var music = new Audio(); + music.preload = 'none'; + if (main.bgmRemote) music.src = main.bgmRemoteRoot + core.firstData.name + '/' + name; + else music.src = 'project/bgms/' + name; + music.loop = 'loop'; + core.material.bgms[name] = music; +} + +loader.prototype.loadOneSound = function (name) { + core.http('GET', 'project/sounds/' + name + "?v=" + main.version, null, function (data) { + core.loader._loadOneSound_decodeData(name, data); + }, function (e) { + console.error(e); + core.material.sounds[name] = null; + }, null, 'arraybuffer'); +} + +loader.prototype._loadOneSound_decodeData = function (name, data) { + if (data instanceof Blob) { + var blobReader = new zip.BlobReader(data); + blobReader.init(function () { + blobReader.readUint8Array(0, blobReader.size, function (uint8) { + core.loader._loadOneSound_decodeData(name, uint8.buffer); + }) + }); + return; + } + try { + core.musicStatus.audioContext.decodeAudioData(data, function (buffer) { + core.material.sounds[name] = buffer; + }, function (e) { + console.error(e); + core.material.sounds[name] = null; + }) + } + catch (e) { + console.error(e); + core.material.sounds[name] = null; + } +} + +loader.prototype.loadBgm = function (name) { + name = core.getMappedName(name); + if (!core.material.bgms[name]) return; + // 如果没开启音乐,则不预加载 + if (!core.musicStatus.bgmStatus) return; + // 是否已经预加载过 + var index = core.musicStatus.cachedBgms.indexOf(name); + if (index >= 0) { + core.musicStatus.cachedBgms.splice(index, 1); + } + else { + // 预加载BGM + this._preloadBgm(core.material.bgms[name]); + // core.material.bgms[name].load(); + // 清理尾巴 + if (core.musicStatus.cachedBgms.length == core.musicStatus.cachedBgmCount) { + this.freeBgm(core.musicStatus.cachedBgms.pop()); + } + } + // 移动到缓存最前方 + core.musicStatus.cachedBgms.unshift(name); +} + +loader.prototype._preloadBgm = function (bgm) { + bgm.volume = 0; + bgm.play(); +} + +loader.prototype.freeBgm = function (name) { + name = core.getMappedName(name); + if (!core.material.bgms[name]) return; + // 从cachedBgms中删除 + core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (t) { + return t != name; + }); + // 清掉缓存 + core.material.bgms[name].removeAttribute("src"); + core.material.bgms[name].load(); + core.material.bgms[name] = null; + if (name == core.musicStatus.playingBgm) { + core.musicStatus.playingBgm = null; + } + // 三秒后重新加载 + setTimeout(function () { + core.loader.loadOneMusic(name); + }, 3000); +} \ No newline at end of file diff --git a/libs/maps.js b/libs/maps.js new file mode 100644 index 0000000..4fbbb8b --- /dev/null +++ b/libs/maps.js @@ -0,0 +1,3112 @@ + +"use strict"; + +function maps () { + this._init(); +} + +maps.prototype._init = function () { + this.blocksInfo = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; + //delete(maps_90f36752_8815_4be8_b32b_d7fad1d0542e); +} + +maps.prototype._initFloors = function (floorId) { + if (!floorId) { + core.floorIds.forEach(function (floorId) { + core.maps._initFloors(floorId); + }); + return; + } + // 战前事件兼容性 + if (!core.floors[floorId].beforeBattle) core.floors[floorId].beforeBattle = {} + // cannotMoveIn兼容性 + if (!core.floors[floorId].cannotMoveIn) core.floors[floorId].cannotMoveIn = {} +} + +maps.prototype._resetFloorImages = function () { + for (var floorId in core.status.maps) { + (core.status.maps[floorId].images || []).forEach(function (one) { + var flag = "__floorImg__" + floorId + "_" + one.x + "_" + one.y; + if (core.getFlag(flag) == null) { + if (one.disabled) core.setFlag(flag, true); + } + }) + } +} + +maps.prototype._setHDCanvasSize = function (ctx, width, height) { + ctx.setTransform(1, 0, 0, 1, 0, 0); + var ratio = core.domStyle.scale; + ratio *= devicePixelRatio; + if (width != null) ctx.canvas.width = width * ratio; + if (height != null) ctx.canvas.height = height * ratio; + ctx.scale(ratio, ratio); + ctx.canvas.setAttribute('isHD', 1); +} + +// ------ 加载地图与地图的存档读档(压缩与解压缩) ------ // + +////// 加载某个楼层(从剧本或存档中) ////// +maps.prototype.loadFloor = function (floorId, map) { + var floor = core.floors[floorId]; + if (!map) map = core.cloneArray(floor.map); + if (map instanceof Array) { + map = { "map": map }; + } + if (!map.map) map.map = core.cloneArray(floor.map); + var content = {}; + var notCopy = this._loadFloor_doNotCopy(); + for (var name in floor) { + if (notCopy.indexOf(name) == -1 && floor[name] != null) + content[name] = core.clone(floor[name]); + } + for (var name in map) { + if (notCopy.indexOf(name) == -1 && map[name] != null) + content[name] = core.clone(map[name]); + } + content.map = map.map; + if (main.mode == 'editor') { + this.extractBlocks(content); + } + return content; +} + +maps.prototype._loadFloor_doNotCopy = function () { + return [ + "firstArrive", "eachArrive", "blocks", "parallelDo", "map", "bgmap", "fgmap", + "events", "changeFloor", "beforeBattle", "afterBattle", "afterGetItem", "afterOpenDoor", + "cannotMove", "cannotMoveIn" + ]; +} + +/// 根据需求解析出blocks +maps.prototype.extractBlocks = function (map) { + map = map || core.status.floorId; + if (typeof map == 'string') map = (core.status.maps || {})[map]; + if (!map) return; + if (map.blocks) return; + if (map.deleted) { + map.blocks = []; + return; + } + var floorId = map.floorId; + map.blocks = this._mapIntoBlocks(this.decompressMap(map.map, floorId), core.floors[floorId], floorId); +} + +maps.prototype._mapIntoBlocks = function (map, floor, floorId) { + var blocks = []; + var mw = core.floors[floorId].width; + var mh = core.floors[floorId].height; + for (var i = 0; i < mh; i++) { + for (var j = 0; j < mw; j++) { + var number = (map[i] || [])[j] || 0, block; + if (main.mode == 'editor') { + if (!number) continue; + block = { x: j, y: i, id: number, event: this.getBlockByNumber(number).event }; + } else { + block = this.initBlock(j, i, number, true, floor); + } + if (block.id != 0 || block.event.trigger) + blocks.push(block); + } + } + return blocks; +} + +maps.prototype.extractBlocksForUI = function (map, flags) { + if (!map || map.blocks) return; + if (map.deleted) return map.blocks = []; + var floorId = map.floorId; + var decompressed = this.decompressMap(map.map, floorId); + map.blocks = []; + var floor = core.floors[floorId]; + var mw = floor.width; + var mh = floor.height; + for (var i = 0; i < mh; i++) { + for (var j = 0; j < mw; j++) { + var number = (decompressed[i] || [])[j] || 0; + if (!number || number == 17) continue; + var isDisabled = this.isMapBlockDisabled(floorId, j, i, flags); + if (isDisabled) continue; + if (isDisabled == null) { + // 检查是否初始禁用 + var event = (floor.events || {})[j + "," + i]; + if (event != null && event.enable === false) continue; + } + var opacity = this._getBlockOpacityFromFlag(floorId, j, i, flags); + if (opacity == null) { + // 检查初始不透明度 + var event = (floor.events || {})[j + "," + i]; + if (event != null && event.opacity != null) opacity = event.opacity; + } + var filter = this._getBlockFilterFromFlag(floorId, j, i, flags); + if (filter == null) { + // 检查初始filter + var event = (floor.events || {})[j + "," + i]; + if (event != null && event.filter != null) filter = core.clone(event.filter); + } + map.blocks.push(Object.assign({}, this.getBlockByNumber(number), { x: j, y: i, opacity: opacity, filter: filter })); + } + } +} + +////// 从ID获得数字 ////// +maps.prototype.getNumberById = function (id) { + id = this.getIdOfThis(id); + core.status.id2number = core.status.id2number || {}; + if (core.status.id2number[id] != null) return core.status.id2number[id]; + return core.status.id2number[id] = this._getNumberById(id); +} + +maps.prototype._getNumberById = function (id) { + for (var number in this.blocksInfo) { + if ((this.blocksInfo[number] || {}).id == id) + return parseInt(number) || 0; + } + // tilesets + if (/^X\d+$/.test(id)) { + if (core.icons.getTilesetOffset(id)) return parseInt(id.substring(1)); + } + // 特殊ID + if (id == 'none') return 0; + if (id == 'airwall') return 17; + return 0; +} + +maps.prototype.getBlockByNumber = function (number) { + core.status.number2Block = core.status.number2Block || {}; + if (core.status.number2Block[number] != null) return core.status.number2Block[number]; + return core.status.number2Block[number] = this.initBlock(null, null, number, true); +} + +maps.prototype.getBlockById = function (id) { + return this.getBlockByNumber(this.getNumberById(id)); +} + +maps.prototype.getIdOfThis = function (id) { + if (id != 'this') return id; + if (core.status.event.id != 'action') return id; + if (!core.status.event.data || core.status.event.data.x == null || core.status.event.data.y == null) return id; + return core.getBlockId(core.status.event.data.x, core.status.event.data.y) || id; +} + +////// 数字和ID的对应关系 ////// +maps.prototype.initBlock = function (x, y, id, addInfo, eventFloor) { + var disable = null; + var opacity = null; + var filter = null; + if (eventFloor != null) { + disable = this.isMapBlockDisabled(eventFloor.floorId, x, y); + opacity = this._getBlockOpacityFromFlag(eventFloor.floorId, x, y); + filter = this._getBlockFilterFromFlag(eventFloor.floorId, x, y); + } + var block = { 'x': x, 'y': y, 'id': id }; + if (disable != null) block.disable = disable; + if (opacity != null) block.opacity = opacity; + if (filter != null) block.filter = filter; + + if (id == 17) block.event = { "cls": "terrains", "id": "airwall", "cannotIn": ["up", "down", "left", "right"] }; + else if (id in this.blocksInfo) block.event = JSON.parse(JSON.stringify(this.blocksInfo[id])); + else if (core.icons.getTilesetOffset(id)) block.event = { "cls": "tileset", "id": "X" + id }; + else block.event = { 'cls': 'terrains', 'id': 'none', 'noPass': false }; + + if (block.event.noPass == null) { + if (block.event.canPass == null) { + block.event.noPass = block.event.cls != 'items'; + } else { + block.event.noPass = !block.event.canPass; + } + } + delete block.event.canPass; + + // 增加怪物的faceIds + if (block.event.cls.indexOf("enemy") == 0) { + var enemy = core.material.enemys[block.event.id]; + if (enemy && enemy.faceIds) { + block.event.faceIds = enemy.faceIds; + } + } + + if (addInfo) this._addInfo(block); + if (eventFloor) { + this._addEvent(block, x, y, (eventFloor.events || {})[x + "," + y]); + var changeFloor = (eventFloor.changeFloor || {})[x + "," + y]; + if (changeFloor) this._addEvent(block, x, y, { "trigger": "changeFloor", "data": changeFloor }); + } + if (main.mode == 'editor') delete block.disable; + return block; +} + +////// 添加一些信息到block上 ////// +maps.prototype._addInfo = function (block) { + if (block.event.cls.indexOf("enemy") == 0 && !block.event.trigger) { + block.event.trigger = 'battle'; + } + if (block.event.cls == 'items' && !block.event.trigger) { + block.event.trigger = 'getItem'; + } + if (block.event.animate == null) { + block.event.animate = core.icons._getAnimateFrames(block.event.cls); + } + block.event.height = 32; + if (block.event.cls == 'enemy48' || block.event.cls == 'npc48') + block.event.height = 48; +} + +////// 向该楼层添加剧本的自定义事件 ////// +maps.prototype._addEvent = function (block, x, y, event) { + if (!event) return; + // event是字符串或数组? + if (typeof event == "string") { + event = { "data": [event] }; + } + else if (event instanceof Array) { + event = { "data": event }; + } + event.data = event.data || []; + + // 覆盖enable + if (block.disable == null && event.enable != null) { + block.disable = !event.enable; + } + // 覆盖opacity + if (block.opacity == null && event.opacity != null) { + block.opacity = event.opacity; + } + if (block.filter == null && event.filter != null) { + block.filter = core.clone(event.filter); + } + // 覆盖animate + if (event.animate === false) { + block.event.animate = 1; + } + // 覆盖所有属性 + for (var key in event) { + if (key != "enable" && key != "animate" && key != "opacity" && key != "filter" && event[key] != null) { + block.event[key] = core.clone(event[key]); + } + } + // 给无trigger的增加trigger:action + if (!block.event.trigger) { + block.event.trigger = 'action'; + } +} + +////// 初始化所有地图 ////// +maps.prototype._initMaps = function () { + var floorIds = core.floorIds; + var maps = {}; + for (var i = 0; i < floorIds.length; i++) { + var floorId = floorIds[i]; + maps[floorId] = this.loadFloor(floorId); + } + return maps; +} + +////// 压缩地图 +maps.prototype.compressMap = function (mapArr, floorId) { + var floorMap = core.floors[floorId].map; + if (core.utils.same(mapArr, floorMap)) return null; + + var mw = core.floors[floorId].width; + var mh = core.floors[floorId].height; + for (var x = 0; x < mh; x++) { + if (core.utils.same(mapArr[x], floorMap[x])) { + // 没有改变的行直接删掉记成0 + mapArr[x] = 0; + } + else { + for (var y = 0; y < mw; y++) { + if (mapArr[x][y] === floorMap[x][y]) { + // 没有改变的数据记成-1 + mapArr[x][y] = -1; + } + } + } + } + return mapArr; +} + +maps.prototype._processInvalidMap = function (mapArr, width, height) { + if (mapArr.length == height && mapArr[0].length == width) return mapArr; + var map = []; + for (var i = 0; i < height; ++i) { + map.push(Array(width).fill(0)); + } + for (var j = 0; j < height; ++j) { + for (var i = 0; i < width; ++i) { + if (j < mapArr.length && i < mapArr[j].length) + map[j][i] = mapArr[j][i]; + } + } + return map; +} + +maps.prototype._getBlockOpacityFromFlag = function (floorId, x, y, flags) { + if (flags == null) flags = (core.status.hero || {}).flags; + if (flags == null) return null; + var __opacity__ = flags.__opacity__ || {}; + floorId = floorId || core.status.floorId; + if (!floorId) return null; + if ((flags.__removed__ || []).indexOf(floorId) >= 0) return null; + var index = x + y * core.floors[floorId].width; + return (__opacity__[floorId] || {})[index]; +} + +maps.prototype._getBlockFilterFromFlag = function (floorId, x, y, flags) { + if (flags == null) flags = (core.status.hero || {}).flags; + if (flags == null) return null; + var __filter__ = flags.__filter__ || {}; + floorId = floorId || core.status.floorId; + if (!floorId) return null; + if ((flags.__removed__ || []).indexOf(floorId) >= 0) return null; + var index = x + y * core.floors[floorId].width; + return core.clone((__filter__[floorId] || {})[index]); +} + +////// 设置某个点的不透明度 ////// +maps.prototype.setBlockOpacity = function (opacity, x, y, floorId) { + if (window.flags == null) return; + floorId = floorId || core.status.floorId; + if (!floorId) return; + if (!window.flags.__opacity__) window.flags.__opacity__ = {}; + if ((window.flags.__removed__ || []).indexOf(floorId) >= 0) return; + var index = x + y * core.floors[floorId].width; + var __opacity__ = window.flags.__opacity__; + if (!__opacity__[floorId]) __opacity__[floorId] = {}; + if (opacity == null) delete __opacity__[floorId][index]; + else __opacity__[floorId][index] = opacity; + + ////// 重绘该点图块 + var block = core.getBlock(x, y, floorId, true); + if (block != null) { + block.opacity = opacity; + if (floorId == core.status.floorId && !block.disable) { + if (block.event.cls == 'autotile') { + core.redrawMap(); + } else { + core.drawBlock(block); + core.addGlobalAnimate(block); + } + } + } +} + +maps.prototype.setBlockFilter = function (filter, x, y, floorId) { + if (window.flags == null) return; + floorId = floorId || core.status.floorId; + if (!floorId) return; + if (!window.flags.__filter__) window.flags.__filter__ = {}; + if ((window.flags.__removed__ || []).indexOf(floorId) >= 0) return; + var index = x + y * core.floors[floorId].width; + var __filter__ = window.flags.__filter__; + if (!__filter__[floorId]) __filter__[floorId] = {}; + if (filter == null) delete __filter__[floorId][index]; + else { + if (!filter.blur && !filter.hue && !filter.shadow && !filter.grayscale && !filter.invert) + delete __filter__[floorId][index]; + else __filter__[floorId][index] = core.clone(filter); + } + + ////// 重绘该点图块 + var block = core.getBlock(x, y, floorId, true); + if (block != null) { + block.filter = core.clone(filter); + if (floorId == core.status.floorId && !block.disable) { + if (block.event.cls == 'autotile') { + core.redrawMap(); + } else { + core.drawBlock(block); + core.addGlobalAnimate(block); + } + } + } +} + +////// 某个点图块是否被强制启用或禁用 +maps.prototype.isMapBlockDisabled = function (floorId, x, y, flags) { + if (flags == null) flags = (core.status.hero || {}).flags; + if (flags == null) return null; + var __disabled__ = flags.__disabled__ || {}; + floorId = floorId || core.status.floorId; + if (!floorId) return null; + if ((flags.__removed__ || []).indexOf(floorId) >= 0) return null; + var index = x + y * core.floors[floorId].width; + if (!__disabled__[floorId]) return null; + if (__disabled__[floorId][0].indexOf(index) >= 0) return true; + if (__disabled__[floorId][1].indexOf(index) >= 0) return false; +} + +////// 设置某个点的图块强制启用/禁用状态 +maps.prototype.setMapBlockDisabled = function (floorId, x, y, disabled) { + if (window.flags == null) return; + floorId = floorId || core.status.floorId; + if (!floorId) return null; + if (!window.flags.__disabled__) window.flags.__disabled__ = {}; + if ((window.flags.__removed__ || []).indexOf(floorId) >= 0) return; + var __disabled__ = window.flags.__disabled__ || {}; + if (!__disabled__[floorId]) __disabled__[floorId] = [[], []]; + var index = x + y * core.floors[floorId].width; + __disabled__[floorId][0] = __disabled__[floorId][0].filter(function (x) { return x != index }); + __disabled__[floorId][1] = __disabled__[floorId][1].filter(function (x) { return x != index }); + if (disabled == null) return; + if (disabled) __disabled__[floorId][0].push(index); + else __disabled__[floorId][1].push(index); +} + +////// 解压缩地图 +maps.prototype.decompressMap = function (mapArr, floorId) { + var mw = core.floors[floorId].width; + var mh = core.floors[floorId].height; + var floorMap = this._processInvalidMap(core.floors[floorId].map, mw, mh); + + if (!mapArr) return core.cloneArray(floorMap); + + for (var x = 0; x < mh; x++) { + if (x >= mapArr.length) { + mapArr.push(0); + } + if (mapArr[x] === 0) { + mapArr[x] = core.cloneArray(floorMap[x]); + } + else { + for (var y = 0; y < mw; y++) { + if (y >= mapArr[x].length) mapArr[x].push(-1); + if (mapArr[x][y] === -1) { + mapArr[x][y] = floorMap[x][y]; + } + } + } + } + return mapArr; +} + +////// 将当前地图重新变成数字,以便于存档 ////// +maps.prototype.saveMap = function (floorId) { + var maps = core.status.maps; + if (!floorId) { + var map = {}; + for (var id in maps) { + var obj = this.saveMap(id); + if (Object.keys(obj).length > 0) map[id] = obj; + } + return map; + } + // 砍层状态:直接返回 + if ((flags.__removed__ || []).indexOf(floorId) >= 0) { + return {}; + } + + var map = maps[floorId]; + var thisFloor = this._compressFloorData(map, core.floors[floorId]); + var mapArr = this.compressMap(map.blocks ? this._getMapArrayFromBlocks(map.blocks, map.width, map.height, true) : map.map, floorId); + if (mapArr != null) thisFloor.map = mapArr; + return thisFloor; +} + +maps.prototype._compressFloorData = function (map, floor) { + var thisFloor = {}; + var notCopy = this._loadFloor_doNotCopy(); + for (var name in map) { + if (notCopy.indexOf(name) == -1) { + var floorData = floor[name]; + if (!core.utils.same(map[name], floorData)) { + thisFloor[name] = core.clone(map[name]); + } + } + } + return thisFloor; +} + +////// 将存档中的地图信息重新读取出来 ////// +maps.prototype.loadMap = function (data, floorId, flags) { + if (!floorId) { + var map = {}; + core.floorIds.forEach(function (id) { + if (core.inArray((flags || {}).__removed__, id)) { + data[id] = { deleted: true, canFlyTo: false, canFlyFrom: false, cannotViewMap: true }; + } + map[id] = core.maps.loadFloor(id, data[id]); + }) + return map; + } + return this.loadFloor(floorId, data[floorId]); +} + +////// 更改地图画布的尺寸 +maps.prototype.resizeMap = function (floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + core.bigmap.width = core.floors[floorId].width; + core.bigmap.height = core.floors[floorId].height; + core.bigmap.posX = core.bigmap.posY = 0; + + core.bigmap.v2 = core.bigmap.width * core.bigmap.height > core.bigmap.threshold; + var width = core.bigmap.v2 ? core._PX_ + 64 : core.bigmap.width * 32; + var height = core.bigmap.v2 ? core._PY_ + 64 : core.bigmap.height * 32; + + core.bigmap.canvas.forEach(function (cn) { + if (core.domStyle.hdCanvas.indexOf(cn) >= 0) + core.maps._setHDCanvasSize(core.canvas[cn], width, height); + else { + core.canvas[cn].canvas.width = width; + core.canvas[cn].canvas.height = height; + } + core.canvas[cn].canvas.style.width = width * core.domStyle.scale + "px"; + core.canvas[cn].canvas.style.height = height * core.domStyle.scale + "px"; + core.canvas[cn].translate(core.bigmap.v2 ? 32 : 0, core.bigmap.v2 ? 32 : 0); + if (main.mode === 'editor' && editor.isMobile) { + core.canvas[cn].canvas.style.width = width / core._PX_ * 96 + "vw"; + core.canvas[cn].canvas.style.height = height / core._PY_ * 96 + "vw"; + } + }); +} + +////// 将当前地图重新变成二维数组形式 ////// +maps.prototype.getMapArray = function (floorId, noCache) { + floorId = floorId || core.status.floorId; + var map = core.status.maps[floorId]; + if (!map.blocks || !noCache) return map.map; + return map.map = this._getMapArrayFromBlocks(map.blocks, map.width, map.height); +} + +////// 获得地图上某点的数字 +maps.prototype.getMapNumber = function (x, y, floorId, noCache) { + return this.getMapArray(floorId, noCache)[y][x]; +} + +maps.prototype._updateMapArray = function (floorId, x, y) { + floorId = floorId || core.status.floorId; + var map = core.status.maps[floorId]; + if (!map.blocks) return; + if (x == null || y == null) return this.getMapArray(floorId, true); + var block = this.getBlock(x, y, floorId, true); + if (block == null || block.disable) map.map[y][x] = 0; + else map.map[y][x] = block.id; +} + +maps.prototype._getMapArrayFromBlocks = function (blockArray, width, height, showDisable) { + var blocks = []; + for (var x = 0; x < height; x++) blocks.push(Array(width).fill(0)); + + blockArray.forEach(function (block) { + if (showDisable || !block.disable) + blocks[block.y][block.x] = block.id; + }); + return blocks; +} + +////// 以x,y的形式返回每个点的事件 ////// +maps.prototype.getMapBlocksObj = function (floorId, noCache) { + floorId = floorId || core.status.floorId; + if (core.status.mapBlockObjs[floorId] && !noCache) + return core.status.mapBlockObjs[floorId]; + + var obj = {}; + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + obj[block.x + "," + block.y] = block; + }); + return core.status.mapBlockObjs[floorId] = obj; +} + +////// 将背景前景层变成二维数组的形式 ////// +maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { + floorId = floorId || core.status.floorId; + if (!floorId) return []; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + if (!noCache && core.status[name + "maps"][floorId]) + return core.status[name + "maps"][floorId]; + + var arr = (main.mode == 'editor' && !(window.editor && editor.uievent && editor.uievent.isOpen)) + ? core.cloneArray(editor[name + 'map']) : null; + if (arr == null) + arr = core.cloneArray(core.floors[floorId][name + "map"] || []); + + for (var y = 0; y < height; ++y) { + if (arr[y] == null) arr[y] = Array(width).fill(0); + } + (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach(function (one) { + arr[one[1]][one[0]] = one[2] || 0; + }); + (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach(function (one) { + arr[one[1]][one[0]] = 0; + }); + if (main.mode == 'editor') { + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; + } + } + } + if (core.status[name + "maps"]) + core.status[name + "maps"][floorId] = arr; + return arr; +} + +maps.prototype.getBgMapArray = function (floorId) { + return this._getBgFgMapArray('bg', floorId); +} + +maps.prototype.getFgMapArray = function (floorId) { + return this._getBgFgMapArray('fg', floorId); +} + +maps.prototype._getBgFgNumber = function (name, x, y, floorId) { + if (x == null) x = core.getHeroLoc('x'); + if (y == null) y = core.getHeroLoc('y'); + return this._getBgFgMapArray(name, floorId)[y][x]; +} + +maps.prototype.getBgNumber = function (x, y, floorId) { + return this._getBgFgNumber('bg', x, y, floorId); +} + +maps.prototype.getFgNumber = function (x, y, floorId) { + return this._getBgFgNumber('fg', x, y, floorId); +} + +// ------ 当前能否朝某方向移动,能否瞬间移动 ------ // + +////// 生成全图的当前可移动信息 ////// +maps.prototype.generateMovableArray = function (floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return null; + var arrays = this._generateMovableArray_arrays(floorId); + + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var array = []; + for (var x = 0; x < width; ++x) { + array[x] = Array(height).fill([]); + } + var v2 = floorId == core.status.floorId && core.bigmap.v2; + var startX = v2 ? Math.max(0, core.bigmap.posX - core.bigmap.extend) : 0; + var endX = v2 ? Math.min(width, core.bigmap.posX + core._WIDTH_ + core.bigmap.extend + 1) : width; + var startY = v2 ? Math.max(0, core.bigmap.posY - core.bigmap.extend) : 0; + var endY = v2 ? Math.min(height, core.bigmap.posY + core._HEIGHT_ + core.bigmap.extend + 1) : height; + + for (var x = startX; x < endX; x++) { + for (var y = startY; y < endY; y++) { + array[x][y] = ["left", "down", "up", "right"].filter(function (direction) { + return core.maps._canMoveHero_checkPoint(x, y, direction, floorId, arrays); + }); + } + } + return array; +} + +maps.prototype._generateMovableArray_arrays = function (floorId) { + return { + bgArray: this.getBgMapArray(floorId), + fgArray: this.getFgMapArray(floorId), + eventArray: this.getMapArray(floorId) + }; +} + +////// 勇士能否前往某方向 ////// +maps.prototype.canMoveHero = function (x, y, direction, floorId) { + if (x == null) x = core.getHeroLoc('x'); + if (y == null) y = core.getHeroLoc('y'); + direction = direction || core.getHeroLoc('direction'); + return this._canMoveHero_checkPoint(x, y, direction, floorId); +} + +maps.prototype._canMoveHero_checkPoint = function (x, y, direction, floorId, arrays) { + floorId = floorId || core.status.floorId; + if (!floorId) return false; + arrays = arrays || this._generateMovableArray_arrays(floorId); + + // 1. 检查该点 cannotMove + if (core.inArray((core.floors[floorId].cannotMove || {})[x + "," + y], direction)) + return false; + + var nx = x + core.utils.scan[direction].x, ny = y + core.utils.scan[direction].y; + if (nx < 0 || ny < 0 || nx >= core.floors[floorId].width || ny >= core.floors[floorId].height) + return false; + + // 2. 检查下个点的 cannotMoveIn + if (core.inArray((core.floors[floorId].cannotMoveIn || {})[nx + "," + ny], core.turnDirection(":back", direction))) + return false; + + // 3. 检查该点素材的 cannotOut 和下一个点的 cannotIn + if (this._canMoveHero_checkCannotInOut(Object.keys(arrays).map(function (name) { return arrays[name][y][x]; }), "cannotOut", direction)) + return false; + if (this._canMoveHero_checkCannotInOut(Object.keys(arrays).map(function (name) { return arrays[name][ny][nx]; }), "cannotIn", direction)) + return false; + + // 4. 检查是否能进将死的领域 + if (floorId == core.status.floorId && !core.flags.canGoDeadZone && !core.status.lockControl && + Math.max(core.status.hero.hp, 1) <= ((core.status.checkBlock.damage || {})[nx + "," + ny] || 0) && arrays.eventArray[ny][nx] == 0) + return false; + + return true; +} + +maps.prototype._canMoveHero_checkCannotInOut = function (number, name, direction) { + if (number instanceof Array) { + for (var x in number) { + if (this._canMoveHero_checkCannotInOut(number[x], name, direction)) + return true; + } + return false; + } + if (name == 'cannotIn') direction = core.turnDirection(":back", direction); + return core.inArray((this.getBlockByNumber(number).event || {})[name], direction); +} + +////// 能否瞬间移动 ////// +maps.prototype.canMoveDirectly = function (destX, destY) { + return this.canMoveDirectlyArray([[destX, destY]])[0]; +} + +maps.prototype.canMoveDirectlyArray = function (locs, canMoveArray) { + var ans = [], number = locs.length; + + var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y'); + if (!this._canMoveDirectly_checkGlobal()) { + for (var i = 0; i < number; ++i) ans.push(-1); + return ans; + } + for (var i = 0; i < number; ++i) { + if (locs[i][0] == fromX && locs[i][1] == fromY) { + ans.push(0); + number--; + } + else if (locs[i][0] < 0 || locs[i][0] >= core.bigmap.width || locs[i][1] < 0 || locs[i][1] >= core.bigmap.height) { + ans.push(-1); + number--; + } + else ans.push(null); + } + if (number == 0) return ans; + + // 检查起点事件 + if (!this._canMoveDirectly_checkStartPoint(fromX, fromY)) { + for (var i in ans) { + if (ans[i] == null) ans[i] = -1; + } + return ans; + } + + return this._canMoveDirectly_bfs(fromX, fromY, locs, number, ans, canMoveArray); +} + +maps.prototype._canMoveDirectly_checkGlobal = function () { + // 检查全塔是否禁止瞬间移动 + if (!core.flags.enableMoveDirectly) return false; + // 检查该楼层是否不可瞬间移动 + if (core.status.thisMap.cannotMoveDirectly) return false; + // flag:cannotMoveDirectly为true:不能 + if (core.hasFlag('cannotMoveDirectly')) return false; + + return true; +} + +maps.prototype._canMoveDirectly_checkStartPoint = function (sx, sy) { + if (core.status.checkBlock.damage[sx + "," + sy]) return false; + var block = core.getBlock(sx, sy); + if (block != null) { + // 只有起点是传送点才是能无视 + return block.event.trigger == 'changeFloor'; + } + return true; +} + +maps.prototype._canMoveDirectly_bfs = function (sx, sy, locs, number, ans, canMoveArray) { + canMoveArray = canMoveArray || this.generateMovableArray(); + var blocksObj = this.getMapBlocksObj(); + // 滑冰 + var bgMap = this.getBgMapArray(); + + var visited = [], queue = []; + visited[sx + "," + sy] = 0; + queue.push(sx + "," + sy); + + while (queue.length > 0) { + var now = queue.shift().split(","), x = parseInt(now[0]), y = parseInt(now[1]); + for (var direction in core.utils.scan) { + if (!core.inArray(canMoveArray[x][y], direction)) continue; + var nx = x + core.utils.scan[direction].x, ny = y + core.utils.scan[direction].y, nindex = nx + "," + ny; + if (visited[nindex]) continue; + if (core.onSki(bgMap[ny][nx])) continue; + if (!this._canMoveDirectly_checkNextPoint(blocksObj, nx, ny)) continue; + visited[nindex] = visited[now] + 1; + // if (nx == ex && ny == ey) return visited[nindex]; + for (var i in ans) { + if (locs[i][0] == nx && locs[i][1] == ny && ans[i] == null) { + // 不可以绿点为终点 + var block = blocksObj[nx + "," + ny]; + if (block && !block.disable && block.event.trigger) { + ans[i] = -1; + } else { + ans[i] = visited[nindex]; + } + number--; + if (number == 0) return ans; + } + } + queue.push(nindex); + } + } + + for (var i in ans) { + if (ans[i] == null) ans[i] = -1; + } + return ans; +} + +maps.prototype._canMoveDirectly_checkNextPoint = function (blocksObj, x, y) { + var index = x + "," + y; + var block = blocksObj[index]; + // 该点是否不可通行或有脚本 + if (block && !block.disable && (block.event.noPass || block.event.script || block.event.event)) + return false; + // 该点是否是绿点可触发 + if (block && !block.disable && block.event.trigger) { + if (block.event.trigger != 'changeFloor') return false; + var ignore = core.flags.ignoreChangeFloor; + if (block.event.data && block.event.data.ignoreChangeFloor != null) + ignore = block.event.data.ignoreChangeFloor; + if (!ignore) return false; + } + // 是否存在阻激夹域伤害 + if (core.status.checkBlock.damage[index]) return false; + if (core.status.checkBlock.repulse[index]) return false; + // 是否存在捕捉 + if (core.status.checkBlock.ambush[index]) return false; + + return true; +} + +////// 自动寻路找寻最优路径 ////// +maps.prototype.automaticRoute = function (destX, destY) { + var startX = core.getHeroLoc('x'), startY = core.getHeroLoc('y'); + if (destX == startX && destY == startY) return []; + // BFS找寻最短路径 + var route = this._automaticRoute_bfs(startX, startY, destX, destY); + if (route[destX + "," + destY] == null) return []; + // 路径数组转换 + var ans = [], nowX = destX, nowY = destY; + while (nowX != startX || nowY != startY) { + var dir = route[nowX + "," + nowY]; + ans.push({ 'direction': dir, 'x': nowX, 'y': nowY }); + nowX -= core.utils.scan[dir].x; + nowY -= core.utils.scan[dir].y; + } + ans.reverse(); + return ans; +} + +maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) { + var route = {}, canMoveArray = this.generateMovableArray(); + // 使用优先队列 + var queue = new PriorityQueue({ comparator: function (a, b) { return a.depth - b.depth; } }); + route[startX + "," + startY] = ''; + queue.queue({ depth: 0, x: startX, y: startY }); + var blocks = core.getMapBlocksObj(); + while (queue.length != 0) { + var curr = queue.dequeue(), deep = curr.depth, nowX = curr.x, nowY = curr.y; + for (var direction in core.utils.scan) { + if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue; + var nx = nowX + core.utils.scan[direction].x; + var ny = nowY + core.utils.scan[direction].y; + if (nx < 0 || nx >= core.bigmap.width || ny < 0 || ny >= core.bigmap.height || route[nx + "," + ny] != null) continue; + // 重点 + if (nx == destX && ny == destY) { + route[nx + "," + ny] = direction; + break; + } + // 不可通行 + if (core.noPass(nx, ny)) continue; + route[nx + "," + ny] = direction; + queue.queue({ depth: deep + this._automaticRoute_deepAdd(nx, ny, blocks), x: nx, y: ny }); + } + if (route[destX + "," + destY] != null) break; + } + return route; +} + +maps.prototype._automaticRoute_deepAdd = function (x, y, blocks) { + // 判定每个可通行点的损耗值,越高越应该绕路 + var deepAdd = 1; + var block = blocks[x + "," + y]; + if (block && !block.disable) { + var id = block.event.id; + // 绕过亮灯 + if (id == "light") deepAdd += 100; + // 绕过路障 + if (id.endsWith("Net") && !core.hasFlag(id.substring(0, id.length - 3))) deepAdd += 100; + // 绕过血瓶和绿宝石 + if (core.hasFlag('__potionNoRouting__') && (id.endsWith("Potion") || id == 'greenGem')) deepAdd += 100; + // 绕过传送点 + // if (block.event.trigger == 'changeFloor') deepAdd+=10; + } + // 绕过存在伤害的地方 + deepAdd += (core.status.checkBlock.damage[x + "," + y] || 0) * 100; + // 绕过捕捉 + if (core.status.checkBlock.ambush[x + "," + y]) deepAdd += 1000; + return deepAdd; +} + +// -------- 绘制地图,各层图块,楼层贴图,Autotile -------- // + +maps.prototype._getBigImageInfo = function (bigImage, face, animate) { + face = face || "down"; + if (["up", "down", "left", "right"].indexOf(face) < 0) face = "down"; + var per_width = bigImage.width / 4; + var per_height = bigImage.height / 4; + var sx = animate * per_width, sy; + if (per_height <= per_width / 2) { // 强制视为 1*4 的怪物 + per_height = bigImage.height; + sy = 0; + } else { + sy = core.material.icons.hero[face].loc * per_height; + } + var dx, dy; + switch (face) { + case "down": case "up": case "left": case "right": dx = 16 - per_width / 2; dy = 32 - per_height; break; + // case "left": dx = 0; dy = 32 - per_height; break; + // case "right": dx = 32 - per_width; dy = 32 - per_height; break; + } + + return { sx: sx, sy: sy, per_width: per_width, per_height: per_height, face: face, dx: dx, dy: dy }; +} + +////// 绘制一个图块 ////// +maps.prototype.drawBlock = function (block, animate, ctx) { + if (block.event.id == 'none') return; + var redraw = animate != null; + if (!redraw) animate = 0; + var x = block.x, y = block.y; + // --- 在界面外的动画不绘制 + + // 判定是否绘制 + if (core.bigmap.v2) { + var posX = core.bigmap.posX, posY = core.bigmap.posY; + if (x < posX - 1 || y < posY - 1 || x > posX + core._WIDTH_ || y > posY + core._HEIGHT_ + 1) { // +1 for 48 height + return; + } + } else { + if (redraw && block.event.animate > 1 && + (32 * x < core.bigmap.offsetX - 64 || 32 * x > core.bigmap.offsetX + core._PX_ + 32 + || 32 * y < core.bigmap.offsetY - 64 || 32 * y > core.bigmap.offsetY + core._PY_ + 32 + 16)) { + return; + } + } + + var blockInfo = this.getBlockInfo(block); + if (blockInfo == null) return; + if (blockInfo.cls != 'tileset') blockInfo.posX = animate % blockInfo.animate; + blockInfo.opacity = block.opacity; + blockInfo.filter = block.filter; + if (!block.name) + this._drawBlockInfo(blockInfo, block.x, block.y, ctx); + else + this._drawBlockInfo_bgfg(blockInfo, block.name, block.x, block.y, ctx); +} + +maps.prototype._drawBlockInfo_bigImage = function (blockInfo, x, y, ctx) { + var bigImageInfo = this._getBigImageInfo(blockInfo.bigImage, blockInfo.face, blockInfo.posX); + var per_width = bigImageInfo.per_width, per_height = bigImageInfo.per_height, sx = bigImageInfo.sx, sy = bigImageInfo.sy; + var bigImage = blockInfo.bigImage; + + if (main.mode == 'editor') { + var px = 32 * x - 32 * core.bigmap.posX; + var py = 32 * y - 32 * core.bigmap.posY; + if (ctx == null) ctx = 'event'; + core.clearMap(ctx, px, py, 32, 32); + core.drawImage(ctx, bigImage, sx, sy, per_width, per_height, px, py, 32, 32); + return; + } + + var px = 32 * x - core.bigmap.offsetX; + var py = 32 * y - core.bigmap.offsetY; + + // 上半部分 - 会遮挡勇士;z值高于event2,为51 + var header = "_bigImage_header_" + x + "_" + y; + // 下半部分 - 会被勇士遮挡;z值高于event,为31 + var body = "_bigImage_body_" + x + "_" + y; + var dx = bigImageInfo.dx, dy = bigImageInfo.dy; + + switch (bigImageInfo.face) { + case "down": case "up": case "left": case "right": + core.createCanvas(header, px + dx, py + dy, per_width, -dy, 51); + this._drawBlockInfo_drawWithFilter(blockInfo, header, function () { + core.drawImage(header, bigImage, sx, sy, per_width, -dy, 0, 0, per_width, -dy); + }); + core.createCanvas(body, px + dx, py, per_width, 32, 31); + this._drawBlockInfo_drawWithFilter(blockInfo, body, function () { + core.drawImage(body, bigImage, sx, sy - dy, per_width, 32, 0, 0, per_width, 32); + }) + break; + /*case "left": + core.createCanvas(header, px + dx, py + dy, per_width, -dy, 51); + this._drawBlockInfo_drawWithFilter(blockInfo, header, function () { + core.drawImage(header, bigImage, sx, sy, per_width, -dy, 0, 0, per_width, -dy); + }); + core.createCanvas(body, px + dx, py, per_width, 32, 31); + this._drawBlockInfo_drawWithFilter(blockInfo, body, function () { + core.drawImage(body, bigImage, sx, sy - dy, per_width, 32, 0, 0, per_width, 32); + }); + break; + case "right": + core.createCanvas(header, px + dx, py + dy, per_width, -dy, 51); + this._drawBlockInfo_drawWithFilter(blockInfo, header, function () { + core.drawImage(header, bigImage, sx, sy, per_width, -dy, 0, 0, per_width, -dy); + }); + core.createCanvas(body, px + dx, py, per_width, per_height / 2 + 16, 31); + this._drawBlockInfo_drawWithFilter(blockInfo, body, function () { + core.drawImage(body, bigImage, sx, sy - dy, per_width, 32, 0, 0, per_width, 32); + }); + break;*/ + } + if (core.dymCanvas[header]) { + core.dymCanvas[header].canvas.setAttribute('_ox', 32 * x + dx); + core.dymCanvas[header].canvas.setAttribute('_oy', 32 * y + dy); + } + if (core.dymCanvas[body]) { + core.dymCanvas[body].canvas.setAttribute('_ox', 32 * x + dx); + core.dymCanvas[body].canvas.setAttribute('_oy', 32 * y); + } +} + +maps.prototype._drawBlockInfo_drawWithFilter = function (blockInfo, ctx, func) { + var alpha = null; + if (blockInfo.opacity != null) alpha = core.setAlpha(ctx, blockInfo.opacity); + core.setFilter(ctx, blockInfo.filter); + func(); + core.setFilter(ctx, null); + if (alpha != null) core.setAlpha(ctx, alpha); +} + +maps.prototype._drawBlockInfo = function (blockInfo, x, y, ctx) { + if (blockInfo.bigImage) return this._drawBlockInfo_bigImage(blockInfo, x, y, ctx); + + var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; + var px = 32 * x - 32 * core.bigmap.posX; + var py = 32 * y - 32 * core.bigmap.posY; + if (ctx == null) ctx = 'event'; + + this._drawBlockInfo_drawWithFilter(blockInfo, ctx, function () { + core.clearMap(ctx, px, py, 32, 32); + core.drawImage(ctx, image, posX * 32, posY * height + height - 32, 32, 32, px, py, 32, 32); + }); + if (height > 32) { + this._drawBlockInfo_drawWithFilter(blockInfo, 'event2', function () { + core.clearMap('event2', px, py + 32 - height, 32, height - 32); + core.drawImage('event2', image, posX * 32, posY * height, 32, height - 32, px, py + 32 - height, 32, height - 32); + }); + } +} + +maps.prototype._drawBlockInfo_bgfg = function (blockInfo, name, x, y, ctx) { + var image = blockInfo.image, posX = blockInfo.posX, posY = blockInfo.posY, height = blockInfo.height; + var px = 32 * x - 32 * core.bigmap.posX; + var py = 32 * y - 32 * core.bigmap.posY; + if (ctx == null) ctx = name; + + core.clearMap(ctx, px, py + 32 - height, 32, height); + if (name == 'bg') { + if (height > 32) { + core.clearMap(ctx, px, py - 32, 32, 32); + core.drawImage(ctx, core.material.groundCanvas.canvas, px, py - 32); + } + core.drawImage(ctx, core.material.groundCanvas.canvas, px, py); + } + var alpha = null; + if (blockInfo.opacity != null) alpha = core.setAlpha(ctx, blockInfo.opacity); + else if (name == 'fg' && this._drawBlockInfo_shouldBlurFg(x, y)) alpha = core.setAlpha(ctx, 0.6); + core.setFilter(ctx, blockInfo.filter); + core.drawImage(ctx, image, posX * 32, posY * height, 32, height, px, py + 32 - height, 32, height); + core.setFilter(ctx, null); + if (alpha != null) core.setAlpha(ctx, alpha); +} + +////// 是否应当存在事件时虚化前景层 ////// +maps.prototype._drawBlockInfo_shouldBlurFg = function (x, y) { + if (main.mode == 'play' && !core.flags.blurFg) return false; + var block = this.getBlock(x, y); + if (block == null || block.id == 0) return false; + if (block.event.cls == 'autotile' || block.event.cls == 'tileset') return block.event.script || block.event.event; + return true; +} + +////// 生成groundPattern ////// +maps.prototype.generateGroundPattern = function (floorId) { + // 生成floorId层的groundPattern(盒子内的怪物动画) + var groundId = ((core.status.maps || core.floors)[floorId || core.status.floorId] || {}).defaultGround || "ground"; + var groundInfo = core.getBlockInfo(groundId); + if (groundInfo == null) return; + core.material.groundCanvas.clearRect(0, 0, 32, 32); + core.material.groundCanvas.drawImage(groundInfo.image, 32 * groundInfo.posX, groundInfo.height * groundInfo.posY, 32, 32, 0, 0, 32, 32); + core.material.groundPattern = core.material.groundCanvas.createPattern(core.material.groundCanvas.canvas, 'repeat'); + // 如果需要用纯色可以直接将下面代码改成改成 + // core.material.groundPattern = '#000000'; +} + +////// 绘制某张地图 ////// +maps.prototype.drawMap = function (floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + core.clearMap('all'); + this.generateGroundPattern(floorId); + core.status.floorId = floorId; + core.extractBlocks(floorId); + core.status.thisMap = core.status.maps[floorId]; + + this._drawMap_drawAll(); + if (core.status.curtainColor) { + core.fillRect('curtain', 0, 0, core._PX_, core._PY_, core.arrayToRGBA(core.status.curtainColor)); + } + core.drawHero(); + core.updateStatusBar(); +} + +////// 重绘某张地图 ////// +maps.prototype.redrawMap = function () { + core.bigmap.canvas.forEach(function (one) { + core.clearMap(one); + }); + this._drawMap_drawAll(null, { redraw: true }); + core.drawDamage(); +} + +maps.prototype._drawMap_drawAll = function (floorId, config) { + floorId = floorId || core.status.floorId; + this.drawBg(floorId, config); + this.drawEvents(floorId); + this.drawFg(floorId, config); +} + +maps.prototype._drawMap_drawBlockInfo = function (ctx, block, blockInfo, arr, config) { + if (blockInfo == null) return; + var onMap = config.onMap; + if (onMap && core.bigmap.v2) { + // 判定是否绘制 + var posX = core.bigmap.posX, posY = core.bigmap.posY; + if (block.x < posX - 1 || block.y < posY - 1 || block.x > posX + core._WIDTH_ || block.y > posY + core._HEIGHT_ + 1) { // +1 for 48 height + return; + } + } + + if (blockInfo.cls == 'autotile') { // Autotile单独处理 + var alpha = null; + if (block.opacity != null) alpha = core.setAlpha(ctx, block.opacity); + core.setFilter(ctx, block.filter); + this._drawAutotile(ctx, arr, block, 32, 0, 0, 0, onMap); + core.setFilter(ctx, null); + if (alpha != null) core.setAlpha(ctx, alpha); + if (onMap) this.addGlobalAnimate(block); + return; + } + if (!onMap) { + var height = blockInfo.height; + if (blockInfo.bigImage) { + config.postDraw.push(function () { + var bigImageInfo = core.maps._getBigImageInfo(blockInfo.bigImage, blockInfo.face, 0); + var per_width = bigImageInfo.per_width, per_height = bigImageInfo.per_height; + core.maps._drawBlockInfo_drawWithFilter(block, ctx, function () { + core.drawImage(ctx, blockInfo.bigImage, bigImageInfo.sx, bigImageInfo.sy, per_width, per_height, + 32 * block.x + bigImageInfo.dx, 32 * block.y + bigImageInfo.dy, per_width, per_height); + }); + }); + return; + } + this._drawBlockInfo_drawWithFilter(block, ctx, function () { + core.drawImage(ctx, blockInfo.image, 32 * blockInfo.posX, height * blockInfo.posY, 32, height, 32 * block.x, 32 * block.y + 32 - height, 32, height); + }); + return; + } + this.drawBlock(block, null, ctx); + this.addGlobalAnimate(block); +} + +////// 绘制背景层 ////// +// config:绘制的参数,可包含如下项: +// redraw - 是否是重绘;ctx - 要绘制到的画布(仅限缩略图使用); +maps.prototype.drawBg = function (floorId, config) { + floorId = floorId || core.status.floorId; + if (config == null) config = {}; + if (typeof config == 'string' || config.canvas) config = { ctx: config }; + config = Object.assign({}, config); + if (config.ctx == null) { + config.onMap = true; + config.ctx = 'bg'; + core.clearMap('bg'); + core.status.floorAnimateObjs = this._getFloorImages(floorId); + } + var toDrawCtx = core.getContextByName(config.ctx); + if (!toDrawCtx) return; + + var cacheCtx = toDrawCtx; + if (config.onMap) { + cacheCtx = core.bigmap.cacheCanvas; + cacheCtx.canvas.width = toDrawCtx.canvas.width; + cacheCtx.canvas.height = toDrawCtx.canvas.height; + if (core.bigmap.v2) cacheCtx.translate(32, 32); + } + this._drawBg_draw(floorId, toDrawCtx, cacheCtx, config); + if (config.onMap) cacheCtx.translate(0, 0); +} + +maps.prototype._drawBg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + core.maps._drawBg_drawBackground(floorId, config); + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制背景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages(floorId, config.ctx, 'bg', null, null, config.onMap); + core.maps._drawBgFgMap(floorId, 'bg', config); + if (config.onMap) core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + config.ctx = toDrawCtx; +} + +maps.prototype._drawBg_drawBackground = function (floorId, config) { + var groundId = (core.status.maps || core.floors)[floorId].defaultGround || "ground"; + var groundInfo = core.getBlockInfo(groundId); + var onMap = config.onMap; + if (groundInfo != null) { + var start = onMap && core.bigmap.v2 ? -1 : 0; + var endX = onMap && core.bigmap.v2 ? core._WIDTH_ + 1 : core.floors[floorId].width; + var endY = onMap && core.bigmap.v2 ? core._HEIGHT_ + 1 : core.floors[floorId].height; + + var patternCanvas = document.createElement('canvas'); + patternCanvas.width = patternCanvas.height = 32; + var patternCtx = patternCanvas.getContext('2d'); + core.drawImage(patternCtx, groundInfo.image, 32 * groundInfo.posX, groundInfo.height * groundInfo.posY, 32, 32, 0, 0, 32, 32) + + core.fillRect(config.ctx, 32 * start, 32 * start, 32 * (endX - start), 32 * (endY - start), patternCtx.createPattern(patternCanvas, 'repeat')); + } +} + +////// 绘制事件层 ////// +maps.prototype.drawEvents = function (floorId, blocks, config) { + floorId = floorId || core.status.floorId; + if (config == null) config = {}; + if (typeof config == 'string' || config.canvas) config = { ctx: config }; + config = Object.assign({}, config); + if (config.ctx == null) { + config.onMap = true; + config.ctx = 'event'; + core.clearMap('event'); + core.clearMap('event2'); + } + var toDrawCtx = core.getContextByName(config.ctx); + if (!toDrawCtx) return; + + var cacheCtx = toDrawCtx; + if (config.onMap) { + cacheCtx = core.bigmap.cacheCanvas; + cacheCtx.canvas.width = toDrawCtx.canvas.width; + cacheCtx.canvas.height = toDrawCtx.canvas.height; + if (core.bigmap.v2) cacheCtx.translate(32, 32); + } + + var arr = null; + if (!blocks) { + core.extractBlocks(floorId); + blocks = core.status.maps[floorId].blocks; + arr = this.getMapArray(floorId, !config.redraw) + } else { + arr = this._getMapArrayFromBlocks(blocks, core.floors[floorId].width, core.floors[floorId].height); + } + config.postDraw = []; + + blocks.filter(function (block) { + if (config.onMap && core.bigmap.v2) { + // 判定是否绘制 + var posX = core.bigmap.posX, posY = core.bigmap.posY; + if (block.x < posX - 1 || block.y < posY - 1 || block.x > posX + core._WIDTH_ || block.y > posY + core._HEIGHT_ + 1) { // +1 for 48 height + return false; + } + } + return block.event && !block.disable; + }).forEach(function (block) { + core.maps._drawMap_drawBlockInfo(cacheCtx, block, core.maps.getBlockInfo(block), arr, config); + }); + config.postDraw.forEach(function (v) { v(); }); + delete config.postDraw; + + if (config.onMap) { + core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + cacheCtx.translate(0, 0); + } +} + +////// 绘制前景层 ////// +// config:绘制的参数,可包含如下项: +// redraw - 是否是重绘;ctx - 要绘制到的画布(仅限缩略图使用); +maps.prototype.drawFg = function (floorId, config) { + floorId = floorId || core.status.floorId; + if (config == null) config = {}; + if (typeof config == 'string' || config.canvas) config = { ctx: config }; + config = Object.assign({}, config); + if (config.ctx == null) { + config.onMap = true; + config.ctx = 'fg'; + core.clearMap('fg'); + } + var toDrawCtx = core.getContextByName(config.ctx); + if (!toDrawCtx) return; + + var cacheCtx = toDrawCtx; + if (config.onMap) { + cacheCtx = core.bigmap.cacheCanvas; + cacheCtx.canvas.width = toDrawCtx.canvas.width; + cacheCtx.canvas.height = toDrawCtx.canvas.height; + if (core.bigmap.v2) cacheCtx.translate(32, 32); + } + this._drawFg_draw(floorId, toDrawCtx, cacheCtx, config); + if (config.onMap) cacheCtx.translate(0, 0); +} + +maps.prototype._drawFg_draw = function (floorId, toDrawCtx, cacheCtx, config) { + config.ctx = cacheCtx; + // ------ 调整这两行的顺序来控制是先绘制贴图还是先绘制前景图块;后绘制的覆盖先绘制的。 + core.maps._drawFloorImages(floorId, config.ctx, 'fg', null, null, config.onMap); + core.maps._drawBgFgMap(floorId, 'fg', config); + if (config.onMap) core.drawImage(toDrawCtx, cacheCtx.canvas, core.bigmap.v2 ? -32 : 0, core.bigmap.v2 ? -32 : 0); + config.ctx = toDrawCtx; +} + +////// 实际的背景/前景图块的绘制 ////// +maps.prototype._drawBgFgMap = function (floorId, name, config) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + if (!core.status[name + "maps"]) + core.status[name + "maps"] = {}; + + var startX = config.onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posX - 1) : 0; + var endX = config.onMap && core.bigmap.v2 ? Math.min(width, core.bigmap.posX + core._WIDTH_ + 1) : width; + var startY = config.onMap && core.bigmap.v2 ? Math.max(0, core.bigmap.posY - 1) : 0; + var endY = config.onMap && core.bigmap.v2 ? Math.min(height, core.bigmap.posY + core._HEIGHT_ + 2) : height; + + var arr = this._getBgFgMapArray(name, floorId, !config.redraw); + config.postDraw = []; + for (var x = startX; x < endX; x++) { + for (var y = startY; y < endY; y++) { + if (arr[y][x] == 0) continue; + var block = this.initBlock(x, y, arr[y][x], true); + block.name = name; + var blockInfo = this.getBlockInfo(block); + if (!blockInfo) continue; + this._drawMap_drawBlockInfo(config.ctx, block, blockInfo, arr, config); + } + } + config.postDraw.forEach(function (v) { v(); }); + delete config.postDraw; +} + +////// 绘制楼层贴图 ////// +maps.prototype._drawFloorImages = function (floorId, ctx, name, images, currStatus, onMap) { + floorId = floorId || core.status.floorId; + if (!images) images = this._getFloorImages(floorId); + var redraw = currStatus != null; + images.forEach(function (one) { + var image = core.material.images.images[core.getMappedName(one.name)]; + var frame = one.frame || 1; + if (!image) return; + var flag = "__floorImg__" + floorId + "_" + one.x + "_" + one.y; + if (core.hasFlag(flag)) return; + if (redraw && frame == 1) return; // 不重绘 + + if (/.*\.gif/i.test(one.name)) { + if (redraw) return; + this._drawFloorImages_gif(image, one.x, one.y); + return; + } + this._drawFloorImage(ctx, name, one, image, currStatus, onMap); + }, this); +} + +maps.prototype._getFloorImages = function (floorId) { + return ((core.status.maps || core.floors)[floorId || core.status.floorId] || {}).images || []; +} + +maps.prototype._drawFloorImages_gif = function (image, dx, dy) { + core.dom.gif.innerHTML = ""; + var gif = new Image(); + gif.src = image.src; + gif.style.position = 'absolute'; + gif.style.left = (dx * core.domStyle.scale) + "px"; + gif.style.top = (dy * core.domStyle.scale) + "px"; + gif.style.width = image.width * core.domStyle.scale + "px"; + gif.style.height = image.height * core.domStyle.scale + "px"; + core.dom.gif.appendChild(gif); + return; +} + +maps.prototype._drawFloorImage = function (ctx, name, one, image, currStatus, onMap) { + var height = image.height; + var imageName = one.name + (one.reverse || ''); + var width = parseInt((one.w == null ? image.width : one.w) / (one.frame || 1)); + var height = one.h == null ? image.height : one.h; + var sx = (one.sx || 0) + (currStatus || 0) % (one.frame || 1) * width; + var sy = one.sy || 0; + var x = one.x || 0, y = one.y || 0; + if (onMap && core.bigmap.v2) { + if (x > 32 * core.bigmap.posX + core._PX_ + 32 || x + width < 32 * core.bigmap.posX - 32 + || y > 32 * core.bigmap.posX + core._PY_ + 32 || y + height < 32 * core.bigmap.posY - 32) { + return; + } + x -= 32 * core.bigmap.posX; + y -= 32 * core.bigmap.posY; + } + + if (one.canvas != 'auto' && one.canvas != name) return; + if (one.canvas != 'auto') { + if (currStatus != null) core.clearMap(ctx, x, y, width, height); + core.drawImage(ctx, imageName, sx, sy, width, height, x, y, width, height); + } else { + if (name == 'bg') { + if (currStatus != null) core.clearMap(ctx, x, y + height - 32, width, 32); + core.drawImage(ctx, imageName, sx, sy + height - 32, width, 32, x, y + height - 32, width, 32); + } else if (name == 'fg') { + if (currStatus != null) core.clearMap(ctx, x, y, width, height - 32); + core.drawImage(ctx, imageName, sx, sy, width, height - 32, x, y, width, height - 32); + } + } +} + +////// 绘制Autotile ////// + + +maps.prototype._drawAutotile = function (ctx, mapArr, block, size, left, top, status, onMap) { + var xx = block.x, yy = block.y; + var autotile = core.material.images['autotile'][block.event.id]; + status = status || 0; + status %= parseInt(autotile.width / 96); + var done = {}; + var isGrass = function (x, y) { + if (core.maps._drawAutotile_getAutotileAroundId(mapArr[yy][xx], x, y, mapArr)) { + return 1; + } else { + return 0; + } + } + var iG = []; + [-1, 0, 1].forEach(function (_x) { + iG[_x] = []; + [-1, 0, 1].forEach(function (_y) { + iG[_x][_y] = isGrass(xx + _x, yy + _y); + }) + }); + if (iG[-1][-1] + iG[0][-1] + iG[0][0] + iG[-1][0] == 3 && !iG[-1][-1]) { + this._drawAutotile_render(ctx, xx * size + left, yy * size + top, size, autotile, status, 16, null, onMap); + done[0] = true; + } + if (iG[0][-1] + iG[1][-1] + iG[1][0] + iG[0][0] == 3 && !iG[1][-1]) { + this._drawAutotile_render(ctx, xx * size + left + size / 2, yy * size + top, size, autotile, status, 17, null, onMap); + done[1] = true; + } + if (iG[0][0] + iG[1][0] + iG[1][1] + iG[0][1] == 3 && !iG[1][1]) { + this._drawAutotile_render(ctx, xx * size + left + size / 2, yy * size + top + size / 2, size, autotile, status, 18, null, onMap); + done[3] = true; + } + if (iG[0 - 1][0] + iG[0][0] + iG[0][1] + iG[-1][1] == 3 && !iG[-1][1]) { + this._drawAutotile_render(ctx, xx * size + left, yy * size + top + size / 2, size, autotile, status, 19, null, onMap); + done[2] = true; + } + var _id = iG[0][-1] + 2 * iG[-1][0] + 4 * iG[0][1] + 8 * iG[1][0]; + + this._drawAutotile_render(ctx, xx * size, yy * size, size, autotile, status, _id, done, onMap); +} + + +maps.prototype._drawAutotile_render = function (canvas, x, y, size, autotile, status, index, done, onMap) { + if (onMap) { + x -= 32 * core.bigmap.posX; + y -= 32 * core.bigmap.posY; + } + var indexData = [[[96 * status, 0, 32, 32, x, y, size, size],], + [[96 * status, 3 * 32, 16, 32, x, y, size / 2, size], [96 * status + 2 * 32 + 16, 3 * 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status + 2 * 32, 32, 32, 16, x, y, size, size / 2], [96 * status + 2 * 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status + 2 * 32, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 16, 32, x, y, size / 2, size], [96 * status + 2 * 32 + 16, 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status, 2 * 32, 16, 32, x, y, size / 2, size], [96 * status + 2 * 32 + 16, 2 * 32, 16, 32, x + size / 2, y, size / 2, size],], + [[96 * status + 2 * 32, 32, 32, 32, x, y, size, size],], + [[96 * status + 2 * 32, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 32, 16, x, y, size, size / 2], [96 * status, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 32, 32, 16, x, y, size, size / 2], [96 * status + 32, 3 * 32 + 16, 32, 16, x, y + size / 2, size, size / 2],], + [[96 * status + 32, 3 * 32, 32, 32, x, y, size, size],], + [[96 * status, 32, 32, 32, x, y, size, size],], + [[96 * status, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 32, 32, 32, x, y, size, size],], + [[96 * status + 32, 2 * 32, 32, 32, x, y, size, size],], + [[96 * status + 2 * 32, 0, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32 + 16, 0, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32 + 16, 16, 16, 16, x, y, size / 2, size / 2],], + [[96 * status + 2 * 32, 16, 16, 16, x, y, size / 2, size / 2],], + ]; + var data = indexData[index]; + if (index >= 16) { // 拐角直接绘制 + core.drawImage(canvas, autotile, data[0][0], data[0][1], data[0][2], data[0][3], data[0][4], data[0][5], size / 2, size / 2); + } else { // 非拐角要根据是否已经绘制进行切分后绘制 + this._drawAutotile_renderCut(canvas, autotile, x, y, size, data, done); + } +} + +maps.prototype._drawAutotile_renderCut = function (canvas, autotile, x, y, size, data, done) { + var drawData = []; + done = done || {}; + if (data.length == 2) { + var idx = 0; + var cut = 0; + for (var i in data) { + if (data[i][2] % 32) { // 是否纵切 + cut = 0; + } + else if (data[i][3] % 32) { // 是否横切 + cut = 1; + } + if (data[i][0] % 32 || data[i][1] % 32) { // right down + idx = 1; + } else { // left top + idx = 0; + } + if (cut) { + idx *= 2; + if (!done[idx]) drawData[idx] = [data[i][0], data[i][1]]; + if (!done[idx + 1]) drawData[idx + 1] = [parseInt(data[i][0]) + 16, data[i][1]]; + } else { + if (!done[idx]) drawData[idx] = [data[i][0], data[i][1]]; + if (!done[idx + 2]) drawData[idx + 2] = [data[i][0], parseInt(data[i][1]) + 16]; + } + } + } else { + if (!done[0]) drawData[0] = [data[0][0], data[0][1]]; + if (!done[1]) drawData[1] = [data[0][0] + 16, data[0][1]]; + if (!done[2]) drawData[2] = [data[0][0], data[0][1] + 16]; + if (!done[3]) drawData[3] = [data[0][0] + 16, data[0][1] + 16]; + } + for (var i = 0; i < 4; i++) { + var dt = drawData[i]; if (!dt) continue; + core.drawImage(canvas, autotile, dt[0], dt[1], 16, 16, x + (i % 2) * size / 2, y + parseInt(i / 2) * size / 2, size / 2, size / 2); + }; +} + + +maps.prototype._drawAutotile_drawBlockByIndex = function (ctx, dx, dy, autotileImg, index, size, status) { + //index为autotile的图块索引1-48 + var sx = 16 * ((index - 1) % 6), sy = 16 * (~~((index - 1) / 6)); + status = status || 0; + status %= parseInt(autotileImg.width / 96); + core.drawImage(ctx, autotileImg, sx + 96 * status, sy, 16, 16, dx, dy, size / 2, size / 2); +} + +maps.prototype._drawAutotile_getAutotileAroundId = function (currId, x, y, mapArr) { + if (x < 0 || y < 0 || x >= mapArr[0].length || y >= mapArr.length) return 1; + else return (core.material.autotileEdges[currId] || []).indexOf(mapArr[y][x]) >= 0; +} + +maps.prototype._drawAutotile_checkAround = function (x, y, mapArr) { + // 得到周围四个32*32块(周围每块都包含当前块的1/4,不清楚的话画下图你就明白)的数组索引 + var currId = mapArr[y][x]; + var pointBlock = []; + for (var i = 0; i < 4; i++) { + var bsum = 0; + var offsetx = i % 2, offsety = ~~(i / 2); + for (var j = 0; j < 4; j++) { + var mx = j % 2, my = ~~(j / 2); + var b = this._drawAutotile_getAutotileAroundId(currId, x + offsetx + mx - 1, y + offsety + my - 1, mapArr); + bsum += b * (Math.pow(2, 3 - j)); + } + pointBlock.push(bsum); + } + return pointBlock; +} + +maps.prototype._drawAutotile_getAutotileIndexs = function (x, y, mapArr, indexArrs) { + var indexArr = []; + var pointBlocks = this._drawAutotile_checkAround(x, y, mapArr); + for (var i = 0; i < 4; i++) { + var arr = indexArrs[pointBlocks[i]] + indexArr.push(arr[3 - i]); + } + return indexArr; +} + +maps.prototype._drawAutotileAnimate = function (block, animate) { + var x = block.x, y = block.y; + // ------ 界面外的动画不绘制 + if (core.bigmap.v2) { + var posX = core.bigmap.posX, posY = core.bigmap.posY; + if (x < posX - 1 || y < posY - 1 || x > posX + core._WIDTH_ || y > posY + core._HEIGHT_) { + return; + } + } else { + if (32 * x < core.bigmap.offsetX - 64 || 32 * x > core.bigmap.offsetX + core._PX_ + 32 + || 32 * y < core.bigmap.offsetY - 64 || 32 * y > core.bigmap.offsetY + core._PY_ + 32 + 16) { + return; + } + } + + var cv = block.name ? core.canvas[block.name] : core.canvas.event; + cv.clearRect(32 * x - 32 * core.bigmap.posX, 32 * y - 32 * core.bigmap.posY, 32, 32); + var alpha = null; + if (block.opacity != null) alpha = core.setAlpha(cv, block.opacity); + core.setFilter(cv, block.filter); + if (block.name) { + if (block.name == 'bg') + core.drawImage('bg', core.material.groundCanvas.canvas, 32 * x - 32 * core.bigmap.posX, 32 * y - 32 * core.bigmap.posY); + this._drawAutotile(cv, this._getBgFgMapArray(block.name), block, 32, 0, 0, animate, true); + } + else { + this._drawAutotile(cv, this.getMapArray(), block, 32, 0, 0, animate, true); + } + core.setFilter(cv, null); + if (alpha != null) core.setAlpha(cv, alpha); +} + +////// 为autotile判定边界 ////// +maps.prototype._makeAutotileEdges = function () { + var autotileIds = Object.keys(core.material.images.autotile); + core.material.autotileEdges = {}; + + var canvas = document.createElement("canvas"), ctx = canvas.getContext('2d'); + canvas.width = canvas.height = 32; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + ctx.msImageSmoothingEnabled = false; + ctx.imageSmoothingEnabled = false; + + var first = {}, second = {}; + autotileIds.forEach(function (t) { + var n = core.maps.getNumberById(t); + core.clearMap(ctx, 0, 0, 32, 32); + core.drawImage(ctx, core.material.images.autotile[t], 0, 0, 32, 32, 0, 0, 32, 32); + first[n] = canvas.toDataURL("image/png"); + core.clearMap(ctx, 0, 0, 32, 32); + core.drawImage(ctx, core.material.images.autotile[t], 32, 0, 32, 32, 0, 0, 32, 32); + second[n] = canvas.toDataURL("image/png"); + }); + + for (var n in first) { + n = parseInt(n); + core.material.autotileEdges[n] = [n]; + for (var n2 in second) { + n2 = parseInt(n2); + if (n == n2) continue; + if (first[n] == second[n2]) { + core.material.autotileEdges[n].push(n2); + } + } + } +} + +////// 绘制缩略图 ////// +// 此函数将绘制一个缩略图,floorId为目标floorId,blocks为地图的图块(可为null使用floorId对应默认的) +// options为绘制选项(可为null),包括: +// heroLoc: 勇士位置;heroIcon:勇士图标(默认当前勇士);damage:是否绘制显伤;flags:当前的flags(存读档时使用) +// ctx:要绘制到的画布(名);x,y:起点横纵坐标(默认0);size:大小(默认416/480); +// all:是否绘制全图(默认false);centerX,centerY:截取中心(默认为地图正中心) +// noHD:不使用高清绘制,避免存读档界面出问题 +maps.prototype.drawThumbnail = function (floorId, blocks, options) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + options = options || {}; + if (typeof options == 'string' || options.canvas) options = { ctx: options }; + var ctx = options.ctx; + // Step1:绘制到tempCanvas上 + this._drawThumbnail_drawTempCanvas(floorId, blocks, options); + options.ctx = ctx; + // Step2:从tempCanvas绘制到对应的画布上 + this._drawThumbnail_drawToTarget(floorId, options); +} + +maps.prototype._drawThumbnail_drawTempCanvas = function (floorId, blocks, options) { + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + // 绘制到tempCanvas上面 + var tempCanvas = core.bigmap.tempCanvas; + + // 如果是大地图模式? + if (options.all) { + // 计算比例 + if (options.noHD) { + tempCanvas.canvas.width = width * 32; + tempCanvas.canvas.height = height * 32; + tempCanvas.canvas.removeAttribute('isHD'); + } else { + core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32); + } + } else if (width * height > core.bigmap.threshold) { + options.v2 = true; + if (options.noHD) { + tempCanvas.canvas.width = core._PX_; + tempCanvas.canvas.height = core._PY_; + tempCanvas.canvas.removeAttribute('isHD'); + } else core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32); + var centerX = options.centerX, centerY = options.centerY; + if (centerX == null) centerX = Math.floor(width / 2); + if (centerY == null) centerY = Math.floor(height / 2); + var offsetX = core.clamp(centerX - core._HALF_WIDTH_, 0, width - core._WIDTH_), + offsetY = core.clamp(centerY - core._HALF_HEIGHT_, 0, height - core._HEIGHT_); + tempCanvas.translate(-32 * offsetX, -32 * offsetY, false, true); + } else { + options.v2 = false; + if (options.noHD) { + tempCanvas.canvas.width = width * 32; + tempCanvas.canvas.height = height * 32; + tempCanvas.canvas.removeAttribute('isHD'); + } else core.maps._setHDCanvasSize(tempCanvas, width * 32, height * 32); + } + options.ctx = tempCanvas; + + // 地图过大的缩略图不绘制显伤 + if (width * height > core.bigmap.threshold) + options.damage = false; + + // --- 暂存 flags + var hasHero = core.status.hero != null, flags = null; + if (options.flags) { + if (!hasHero) core.status.hero = {}; + flags = core.status.hero.flags; + core.status.hero.flags = options.flags; + } + + this._drawThumbnail_realDrawTempCanvas(floorId, blocks, options); + + // --- 恢复 flags + if (!hasHero) delete core.status.hero; + else if (flags != null) core.status.hero.flags = flags; + tempCanvas.setTransform(1, 0, 0, 1, 0, 0); +} + +maps.prototype._drawThumbnail_realDrawTempCanvas = function (floorId, blocks, options) { + // 缩略图:背景 + this.drawBg(floorId, options); + // 缩略图:事件 + this.drawEvents(floorId, blocks, options); + // 缩略图:勇士 + if (options.heroLoc) { + options.heroIcon = options.heroIcon || core.status.hero.image || 'hero.png'; + options.heroIcon = core.getMappedName(options.heroIcon); + var icon = core.material.icons.hero[options.heroLoc.direction]; + var height = core.material.images.images[options.heroIcon].height / 4; + var width = (core.material.images.images[options.heroIcon].width || 128) / 4; + core.drawImage(options.ctx, core.material.images.images[options.heroIcon], icon.stop * width, icon.loc * height, width, height, + 32 * options.heroLoc.x + 32 - width, 32 * options.heroLoc.y + 32 - height, width, height); + } + // 缩略图:前景 + this.drawFg(floorId, options); + // 缩略图:显伤 + if (options.damage && core.hasItem('book')) { + core.updateCheckBlock(floorId); + core.control.updateDamage(floorId, options.ctx); + } +} + +maps.prototype._drawThumbnail_drawToTarget = function (floorId, options) { + var ctx = core.getContextByName(options.ctx); + if (ctx == null) return; + var x = options.x || 0, y = options.y || 0, size = options.size || 1; + // size的含义改为(0,1]范围的系数以适配长方形,默认为1,楼传为3/4,SL界面为0.3 + var w = Math.ceil(size * core._PX_), h = Math.ceil(size * core._PY_); + // 特判是否为编辑器,编辑器中长宽均采用core.js的遗留正方形像素边长,以保证下面的绘制正常 + if (main.mode == 'editor') w = h = size * core.__PIXELS__; + var width = core.floors[floorId].width, height = core.floors[floorId].height; + var centerX = options.centerX, centerY = options.centerY; + if (centerX == null) centerX = Math.floor(width / 2); + if (centerY == null) centerY = Math.floor(height / 2); + var tempCanvas = core.bigmap.tempCanvas; + + const scale = core.domStyle.scale * devicePixelRatio; + if (options.all) { + var tempWidth = tempCanvas.canvas.width, tempHeight = tempCanvas.canvas.height; + // 绘制全景图 + if (tempWidth <= tempHeight) { + var realHeight = h, realWidth = realHeight * tempWidth / tempHeight; + var side = (w - realWidth) / 2; + core.fillRect(ctx, x, y, side, realHeight, '#000000'); + core.fillRect(ctx, x + w - side, y, side, realHeight); + core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x + side, y, realWidth, realHeight); + } + else { + var realWidth = w, realHeight = realWidth * tempHeight / tempWidth; + var side = (h - realHeight) / 2; + core.fillRect(ctx, x, y, realWidth, side, '#000000'); + core.fillRect(ctx, x, y + h - side, realWidth, side); + core.drawImage(ctx, tempCanvas.canvas, 0, 0, tempWidth, tempHeight, x, y + side, realWidth, realHeight); + } + } + else { + // 只绘制可见窗口 + var pw = core._PX_, + ph = core._PY_, + hw = core._HALF_WIDTH_, + hh = core._HALF_HEIGHT_, + W = core._WIDTH_, + H = core._HEIGHT_; + if (main.mode == 'editor') { + pw = ph = core.__PIXELS__; + hw = hh = core.__HALF_SIZE__; + W = H = core.__SIZE__; + } + if (options.v2) { + if (options.noHD) core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw, ph, x, y, w, h); + else core.drawImage(ctx, tempCanvas.canvas, 0, 0, pw * scale, ph * scale, x, y, w, h); + } else { + var offsetX = core.clamp(centerX - hw, 0, width - W), + offsetY = core.clamp(centerY - hh, 0, height - H); + if (options.noHD) { + core.drawImage(ctx, tempCanvas.canvas, offsetX * 32, offsetY * 32, pw, ph, x, y, w, h); + } else { + core.drawImage(ctx, tempCanvas.canvas, offsetX * 32 * scale, offsetY * 32 * scale, pw * scale, ph * scale, x, y, w, h); + } + } + } +} + +// -------- 获得某个点的图块信息 -------- // + +////// 某个点是否不可通行 ////// +maps.prototype.noPass = function (x, y, floorId) { + var block = core.getBlock(x, y, floorId); + if (block == null) return false; + return block.event.noPass; +} + +////// 某个点是否存在NPC ////// +maps.prototype.npcExists = function (x, y, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.event.cls.indexOf('npc') == 0; +} + +////// 某个点是否存在(指定的)地形 ////// +maps.prototype.terrainExists = function (x, y, id, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.event.cls == 'terrains' && (id ? block.event.id == id : true); +} + +////// 某个点是否存在楼梯 ////// +maps.prototype.stairExists = function (x, y, floorId) { + var blockId = this.getBlockId(x, y, floorId); + if (blockId == null) return false; + var ids = ['upFloor', 'downFloor']; + ids = ids.concat(['leftPortal', 'rightPortal', 'upPortal', 'downPortal', 'portal', 'starPortal']); + return ids.indexOf(blockId) >= 0; +} + +////// 当前位置是否在楼梯边 ////// +maps.prototype.nearStair = function () { + var x = core.getHeroLoc('x'), y = core.getHeroLoc('y'); + return this.stairExists(x, y) || this.stairExists(x - 1, y) || this.stairExists(x, y - 1) || this.stairExists(x + 1, y) || this.stairExists(x, y + 1); +} + +////// 某个点是否存在(指定的)怪物 ////// +maps.prototype.enemyExists = function (x, y, id, floorId) { + var block = this.getBlock(x, y, floorId); + if (block == null) return false; + return block.event.cls.indexOf('enemy') == 0 && (id ? block.event.id == id : true); +} + +////// 获得某个点的block ////// +maps.prototype.getBlock = function (x, y, floorId, showDisable) { + floorId = floorId || core.status.floorId; + if (!floorId) return null; + core.extractBlocks(floorId); + var blockObjs = this.getMapBlocksObj(floorId); + var block = blockObjs[x + "," + y]; + if (block && (showDisable || !block.disable)) return block; + return null; +} + +////// 获得某个点的blockId ////// +maps.prototype.getBlockId = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + return block == null ? null : block.event.id; +} + +////// 获得某个点的数字 ////// +maps.prototype.getBlockNumber = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + return block == null ? null : block.id; +} + +////// 获得某个点的blockCls ////// +maps.prototype.getBlockCls = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + return block == null ? null : block.event.cls; +} + +////// 获得某个点的不透明度 ////// +maps.prototype.getBlockOpacity = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + if (block == null) return null; + if (block.opacity == null) return 1.0; + return block.opacity == null ? 1.0 : block.opacity; +} + +////// 获得某个点的filter ////// +maps.prototype.getBlockFilter = function (x, y, floorId, showDisable) { + var block = core.getBlock(x, y, floorId, showDisable); + if (block == null) return null; + if (block.filter == null) return { blur: 0, hue: 0, grayscale: 0, invert: false, shadow: 0 }; + return core.clone(block.filter); +} + +////// 获得某个图块或素材的信息,包括 ID,cls,图片,坐标,faceIds 等等 ////// +maps.prototype.getBlockInfo = function (block) { + if (!block) return null; + if (typeof block == 'string') { // 参数是ID + block = this.getNumberById(block); + } + if (typeof block == 'number') { // 参数是数字 + if (block == 0) return null; + block = this.getBlockByNumber(block); + } + var number = block.id, id = block.event.id, cls = block.event.cls, name = block.event.name, + image = null, posX = 0, posY = 0, animate = block.event.animate, doorInfo = block.event.doorInfo, + height = block.event.height || 32, faceIds = {}, face = 'down', bigImage = null; + + if (id == 'none') return null; + else if (id == 'airwall') { + if (!core.material.images.airwall) return null; + image = core.material.images.airwall; + name = "空气墙"; + } + else if (cls == 'tileset') { + var offset = core.icons.getTilesetOffset(id); + if (offset == null) return null; + posX = offset.x; + posY = offset.y; + image = core.material.images.tilesets[offset.image]; + } + else if (cls == 'autotile') { + image = core.material.images.autotile[id]; + } + else { + image = core.material.images[cls]; + posY = core.material.icons[cls][id]; + faceIds = block.event.faceIds || {}; + for (var f in faceIds) { + if (faceIds[f] == id) { + face = f; + break; + } + } + if (block.event.bigImage) bigImage = core.material.images.images[block.event.bigImage]; + if (core.material.enemys[id]) { + name = core.material.enemys[id].name; + bigImage = core.material.images.images[core.material.enemys[id].bigImage]; + } else if (core.material.items[id]) { + name = core.material.items[id].name; + } + // 非门效果则强制变成四帧动画 + if (!doorInfo && bigImage != null) animate = 4; + } + + return { + number: number, id: id, cls: cls, name: name, image: image, posX: posX, doorInfo: doorInfo, + posY: posY, height: height, faceIds: faceIds, animate: animate, face: face, bigImage: bigImage + }; +} + +////// 搜索某个图块出现的所有位置 ////// +maps.prototype.searchBlock = function (id, floorId, showDisable) { + if (typeof id == 'number') id = this.getBlockByNumber(id).event.id; + floorId = floorId || core.status.floorId; + var result = []; + if (floorId instanceof Array) { + floorId.forEach(function (floorId) { + result = result.concat(core.searchBlock(id, floorId, showDisable)); + }); + return result; + } + core.extractBlocks(floorId); + for (var i = 0; i < core.status.maps[floorId].blocks.length; ++i) { + var block = core.status.maps[floorId].blocks[i]; + if ((showDisable || !block.disable) && (core.matchWildcard(id, block.event.id) || core.matchRegex(id, block.event.id))) { + result.push({ floorId: floorId, x: block.x, y: block.y, block: block }); + } + } + return result; +} + +////// 给定筛选函数,搜索某个图块出现的所有位置 ////// +maps.prototype.searchBlockWithFilter = function (blockFilter, floorId, showDisable) { + floorId = floorId || core.status.floorId; + var result = []; + if (floorId instanceof Array) { + floorId.forEach(function (floorId) { + result = result.concat(core.searchBlockWithFilter(blockFilter, floorId, showDisable)); + }); + return result; + } + core.extractBlocks(floorId); + for (var i = 0; i < core.status.maps[floorId].blocks.length; ++i) { + var block = core.status.maps[floorId].blocks[i]; + if ((showDisable || !block.disable) && blockFilter(block)) { + result.push({ floorId: floorId, x: block.x, y: block.y, block: block }); + } + } + return result; +} + +////// 获得某个图块,其行走图朝向朝下的图块ID ////// +maps.prototype.getFaceDownId = function (block) { + if (block == null) return null; + if (typeof block == 'string') { // 参数是ID + block = this.getNumberById(block); + } + if (typeof block == 'number') { // 参数是数字 + if (block == 0) return null; + block = this.getBlockByNumber(block); + } + if (!block.event) return null; + return (block.event.faceIds || {}).down || block.event.id; +} + +// -------- 启用/禁用图块,楼层贴图 -------- // + +////// 将某个块从禁用变成启用状态 ////// +maps.prototype.showBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + var block = core.getBlock(x, y, floorId, true); + if (block == null) return; // 不存在 + // 本身是禁用事件,启用之 + if (block.disable) { + block.disable = false; + core.setMapBlockDisabled(floorId, x, y, false); + this._updateMapArray(floorId, block.x, block.y); + // 在本层,添加动画 + if (floorId == core.status.floorId) { + if (block.event.cls == 'autotile') { + core.redrawMap(); + } else { + core.drawBlock(block); + core.addGlobalAnimate(block); + core.updateStatusBar(false, true); + } + } + } +} + +////// 只隐藏但不删除某块 ////// +maps.prototype.hideBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + + var block = core.getBlock(x, y, floorId, true); + if (block == null) return; // 不存在 + + block.disable = true; + core.setMapBlockDisabled(floorId, block.x, block.y, true); + this._updateMapArray(floorId, block.x, block.y); + + // 删除动画,清除地图 + this._removeBlockFromMap(floorId, block); +} + +////// 根据图块的索引来隐藏图块 ////// +maps.prototype.hideBlockByIndex = function (index, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + core.extractBlocks(floorId); + var blocks = core.status.maps[floorId].blocks, block = blocks[index]; + block.disable = true; + core.setMapBlockDisabled(floorId, block.x, block.y, true); + this._updateMapArray(floorId, block.x, block.y); +} + +////// 一次性隐藏多个block ////// +maps.prototype.hideBlockByIndexes = function (indexes, floorId) { + indexes.sort(function (a, b) { + return b - a; + }).forEach(function (index) { + core.hideBlockByIndex(index, floorId); + }); +} + +maps.prototype._removeBlockFromMap = function (floorId, block) { + if (floorId != core.status.floorId) return; + var filter = block.filter || {}; + if (block.event.cls == 'autotile' || filter.blur > 0 || filter.shadow > 0) { + core.redrawMap(); + } else { + var x = block.x, y = block.y; + var px = 32 * x - core.bigmap.posX * 32; + var py = 32 * y - core.bigmap.posY * 32; + core.removeGlobalAnimate(x, y); + core.clearMap('event', px, py, 32, 32); + var height = block.event.height || 32; + if (height > 32) core.clearMap('event2', px, py + 32 - height, 32, height - 32); + // 删除大怪物 + core.deleteCanvas("_bigImage_header_" + x + "_" + y); + core.deleteCanvas("_bigImage_body_" + x + "_" + y); + core.updateStatusBar(); + } +} + +////// 删除某个图块 ////// +maps.prototype.removeBlock = function (x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return false; + + core.extractBlocks(floorId); + for (var i in core.status.maps[floorId].blocks) { + var block = core.status.maps[floorId].blocks[i]; + if (block.x == x && block.y == y) { + this.removeBlockByIndex(i, floorId); + this._removeBlockFromMap(floorId, block); + return true; + } + } + return false; +} + +////// 根据block的索引(尽可能)删除该块 ////// +maps.prototype.removeBlockByIndex = function (index, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + core.extractBlocks(floorId); + var blocks = core.status.maps[floorId].blocks, block = blocks[index]; + blocks.splice(index, 1); + if (core.status.mapBlockObjs[floorId]) + delete core.status.mapBlockObjs[floorId][block.x + "," + block.y]; + core.setMapBlockDisabled(floorId, block.x, block.y, true); + this._updateMapArray(floorId, block.x, block.y); +} + +////// 一次性删除多个block ////// +maps.prototype.removeBlockByIndexes = function (indexes, floorId) { + indexes.sort(function (a, b) { + return b - a; + }).forEach(function (index) { + core.removeBlockByIndex(index, floorId); + }); +} + +////// 显示前景/背景地图 ////// +maps.prototype.showBgFgMap = function (name, loc, floorId, callback) { + this._triggerBgFgMap('show', name, loc, floorId, callback); +} + +////// 隐藏前景/背景地图 ////// +maps.prototype.hideBgFgMap = function (name, loc, floorId, callback) { + this._triggerBgFgMap('hide', name, loc, floorId, callback); +} + +////// 设置前景/背景地图的显示状态 ////// +maps.prototype._triggerBgFgMap = function (type, name, loc, floorId, callback) { + if (type != 'show') type = 'hide'; + if (!name || (!name.startsWith('bg') && !name.startsWith('fg'))) return; + if (typeof loc[0] == 'number' && typeof loc[1] == 'number') + loc = [loc]; + floorId = floorId || core.status.floorId; + if (!floorId) return; + + if (loc.length == 0) return; + var disabled = core.getFlag('__' + name + 'd__', {}); + disabled[floorId] = disabled[floorId] || []; + loc.forEach(function (t) { + if (type == 'hide') { + disabled[floorId].push([t[0], t[1]]); + } else { + disabled[floorId] = disabled[floorId].filter(function (one) { return one[0] != t[0] || one[1] != t[1] }); + } + }) + core.setFlag('__' + name + 'd__', disabled); + + core.status[name + "maps"][floorId] = null; + + if (floorId == core.status.floorId) { + core.redrawMap(); + } + if (callback) callback(); +} + +////// 显示一个楼层贴图 ////// +maps.prototype.showFloorImage = function (loc, floorId, callback) { + this._triggerFloorImage('show', loc, floorId, callback); +} + +////// 隐藏一个楼层贴图 ////// +maps.prototype.hideFloorImage = function (loc, floorId, callback) { + this._triggerFloorImage('hide', loc, floorId, callback); +} + +///// 设置贴图显示状态 ////// +maps.prototype._triggerFloorImage = function (type, loc, floorId, callback) { + if (type != 'show') type = 'hide'; + if (typeof loc[0] == 'number' && typeof loc[1] == 'number') + loc = [loc]; + floorId = floorId || core.status.floorId; + if (!floorId) return; + + if (loc.length == 0) return; + loc.forEach(function (t) { + var x = t[0], y = t[1]; + var flag = "__floorImg__" + floorId + "_" + x + "_" + y; + if (type == 'hide') core.setFlag(flag, true); + else core.removeFlag(flag); + }) + + if (floorId == core.status.floorId) { + core.redrawMap(); + } + if (callback) callback(); +} + +////// 改变图块 ////// +maps.prototype.setBlock = function (number, x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId || number == null || x == null || y == null) return; + if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return; + if (typeof number == 'string') { + if (/^\d+$/.test(number)) number = parseInt(number); + else number = core.getNumberById(number); + } + + var block = this.initBlock(x, y, number, true, core.floors[floorId]); + if (block.id == 0 && !block.event.trigger) { + // 转变图块为0且该点无事件,视为删除 + core.removeBlock(x, y, floorId); + return; + } + var originBlock = core.getBlock(x, y, floorId, true); + var originEvent = originBlock == null ? null : originBlock.event; + if (originBlock == null) { + core.status.maps[floorId].blocks.push(block); + if (core.status.mapBlockObjs[floorId]) + core.status.mapBlockObjs[floorId][block.x + "," + block.y] = block; + core.setMapBlockDisabled(floorId, block.x, block.y, false); + delete block.disable; + } + else { + originBlock.id = number; + originBlock.event = block.event; + block = originBlock; + } + this._updateMapArray(floorId, x, y); + if (floorId == core.status.floorId) { + // 有任何一个是autotile直接重绘地图 + if ((originEvent != null && originEvent.cls == 'autotile') || block.event.cls == 'autotile') { + core.redrawMap(); + } else { + if (originEvent != null) { + this._removeBlockFromMap(floorId, { x: x, y: y, event: originEvent }); + } + if (!block.disable) { + core.drawBlock(block); + core.addGlobalAnimate(block); + core.updateStatusBar(); + } + } + } +} + +maps.prototype.animateSetBlock = function (number, x, y, floorId, time, callback) { + floorId = floorId || core.status.floorId; + time = time || 0; + if (floorId != core.status.floorId || time == 0) { + // 不在当前楼层,直接忽略 + this.setBlock(number, x, y, floorId); + if (callback) callback(); + return; + } + if (typeof number == 'string') { + if (/^\d+$/.test(number)) number = parseInt(number); + else number = core.getNumberById(number); + } + var originBlock = core.getBlock(x, y, floorId, true); + var block = this.initBlock(x, y, number, true, core.floors[floorId]); + + // 如果原本是启用的 + if (originBlock != null && !originBlock.disable) { + return this._animateSetBlock_originEnabled(block, number, x, y, floorId, time, callback); + } + + // 如果原本不存在 + if (originBlock == null) { + return this._animateSetBlock_originNotExists(block, number, x, y, floorId, time, callback); + } + + // 如果原本存在且禁用;应当直接设置,没有动画 + if (originBlock != null && originBlock.disable) { + return this._animateSetBlock_originDisabled(number, x, y, floorId, callback); + } + if (callback) callback(); +} + +maps.prototype._animateSetBlock_originEnabled = function (block, number, x, y, floorId, time, callback) { + // 情况1:设置到0 + if (block.id == 0) { + // 如果该点红点没有事件 - 直接删除 + if (!block.event.trigger) { + return this.animateBlock([x, y], 'remove', time, callback); + } else { + // 如果该点红点有事件;则设置到0,但是需启用 + return this.animateBlock([x, y], 'hide', time, function () { + core.setBlock(0, x, y, floorId); + core.showBlock(x, y, floorId); + if (callback) callback(); + }); + } + } + // 情况2:设置到非0 + else { + return this.animateBlock([x, y], 'hide', time / 2, function () { + core.setBlock(number, x, y, floorId); + core.animateBlock([x, y], 'show', time / 2, callback); + }) + } +} + +maps.prototype._animateSetBlock_originNotExists = function (block, number, x, y, floorId, time, callback) { + // 情况1:设置到0;没有动画效果 + if (block.id == 0) { + core.setBlock(number, x, y, floorId); + if (callback) callback(); + } + else { + // 情况2:设置到非0,有淡入动画 + core.setBlock(number, x, y, floorId); + core.hideBlock(x, y, floorId); + core.animateBlock([x, y], 'show', time, callback); + return; + } +} + +maps.prototype._animateSetBlock_originDisabled = function (number, x, y, floorId, callback) { + core.setBlock(number, x, y, floorId); + if (callback) callback(); +} + +maps.prototype.animateSetBlocks = function (number, locs, floorId, time, callback) { + if (!(locs instanceof Array)) { + if (callback) callback(); + return; + } + if (typeof locs[0] == 'number' && typeof locs[1] == 'number') + locs = [locs]; + + var count = locs.length; + var _afterSet = function () { + count--; + if (count == 0) { + if (callback) callback(); + } + } + locs.forEach(function (loc) { + core.animateSetBlock(number, loc[0], loc[1], floorId, time, _afterSet); + }); +} + +////// 事件转向 ////// +maps.prototype.turnBlock = function (direction, x, y, floorId) { + var id = core.getBlockId(x, y, floorId, true); + var blockInfo = core.getBlockInfo(id); + if (blockInfo == null) return; + var faceIds = blockInfo.faceIds || {}; + var currDirection = null; + for (var dir in core.utils.scan) { + if (faceIds[dir] == id) { + currDirection = dir; + } + } + if (currDirection == null) return; + var nextDirection = core.turnDirection(direction, currDirection); + var nextId = faceIds[nextDirection]; + if (nextId != null && nextId != id) { + this.setBlock(nextId, x, y, floorId); + } +} + +////// 将地图中所有某个图块替换成另一个图块 ////// +maps.prototype.replaceBlock = function (fromNumber, toNumber, floorId) { + floorId = floorId || core.status.floorId; + if (floorId instanceof Array) { + floorId.forEach(function (floorId) { + core.replaceBlock(fromNumber, toNumber, floorId); + }); + return; + } + var toBlock = this.getBlockByNumber(toNumber, true); + core.extractBlocks(floorId); + core.status.maps[floorId].blocks.forEach(function (block) { + if (block.id == fromNumber) { + block.id = toNumber; + for (var one in toBlock.event) { + block.event[one] = core.clone(toBlock.event[one]); + } + this._updateMapArray(floorId, block.x, block.y); + } + }, this); + if (floorId == core.status.floorId) core.redrawMap(); +} + +////// 改变前景背景的图块 ////// +maps.prototype.setBgFgBlock = function (name, number, x, y, floorId) { + floorId = floorId || core.status.floorId; + if (!floorId || number == null || x == null || y == null) return; + if (x < 0 || x >= core.floors[floorId].width || y < 0 || y >= core.floors[floorId].height) return; + if (!name || (!name.startsWith('bg') && !name.startsWith('fg'))) return; + + if (typeof number == 'string') { + if (/^\d+$/.test(number)) number = parseInt(number); + else number = core.getNumberById(number); + } + + var values = core.getFlag('__' + name + 'v__', {}); + values[floorId] = (values[floorId] || []).filter(function (one) { return one[0] != x || one[1] != y }); + values[floorId].push([x, y, number]); + core.setFlag('__' + name + 'v__', values); + + core.status[name + "maps"][floorId] = null; + + this._getBgFgMapArray(name, floorId, true); + + if (floorId == core.status.floorId) { + core.clearMap(name); + if (name.startsWith('bg')) core.drawBg(floorId); + else core.drawFg(floorId); + } +} + +////// 重置地图 ////// +maps.prototype.resetMap = function (floorId) { + floorId = floorId || core.status.floorId; + if (!floorId) return; + if (typeof floorId == 'string') floorId = [floorId]; + var needRefresh = false; + floorId.forEach(function (t) { + core.status.maps[t] = core.maps.loadFloor(t); + // 重置本层的全部独立事件 + Object.keys(core.status.hero.flags).forEach(function (one) { + if (one.startsWith(floorId + '@')) delete core.status.hero.flags[one]; + }) + // 重置本层的图块删除信息 + delete (flags.__disabled__ || {})[t]; + delete (core.status.mapBlockObjs || {})[t]; + if (t == core.status.floorId) needRefresh = true; + }); + if (needRefresh) this.redrawMap(); + core.drawTip("地图重置成功"); +} + +// -------- 移动/跳跃图块,图块的淡入淡出 -------- // + +////// 初始化独立的block canvas ////// +maps.prototype._initDetachedBlock = function (blockInfo, x, y, displayDamage) { + var headCanvas = null, bodyCanvas = '__body_' + x + "_" + y, damageCanvas = null; + // head + if (!blockInfo.bigImage && blockInfo.height > 32) { + headCanvas = "__head_" + x + "_" + y; + core.createCanvas(headCanvas, 0, 0, 32, blockInfo.height - 32, 55); + } + // body + if (blockInfo.bigImage) { + var bigImageInfo = this._getBigImageInfo(blockInfo.bigImage, blockInfo.face, blockInfo.posX); + core.createCanvas(bodyCanvas, 0, 0, bigImageInfo.per_width, bigImageInfo.per_height, 35); + } else { + core.createCanvas(bodyCanvas, 0, 0, 32, 32, 35); + } + // damage + var damage = null, damageColor = null; + if (blockInfo.cls.indexOf('enemy') == 0 && core.hasItem('book') && displayDamage) { + var damageString = core.enemys.getDamageString(blockInfo.id, x, y); + damage = damageString.damage; + damageColor = damageString.color; + } + if (damage != null) { + damageCanvas = "__damage_" + x + "_" + y; + var ctx = core.createCanvas(damageCanvas, 0, 0, 32, 32, 65); + ctx.textAlign = 'left'; + ctx.font = "bold 11px Arial"; + core.fillBoldText(ctx, damage, 1, 31, damageColor); + if (core.flags.displayCritical) { + var critical = core.enemys.nextCriticals(blockInfo.id); + if (critical.length > 0) critical = critical[0]; + critical = core.formatBigNumber(critical[0], true); + if (critical == '???') critical = '?'; + core.fillBoldText(ctx, critical, 1, 21, '#FFFFFF'); + } + } + return { + "headCanvas": headCanvas, + "bodyCanvas": bodyCanvas, + "damageCanvas": damageCanvas + } +} + +////// 移动独立的block canvas ////// +maps.prototype._moveDetachedBlock = function (blockInfo, nowX, nowY, opacity, canvases) { + var height = blockInfo.height, posX = blockInfo.posX, posY = blockInfo.posY, image = blockInfo.image; + var headCanvas = canvases.headCanvas, bodyCanvas = canvases.bodyCanvas, damageCanvas = canvases.damageCanvas; + if (headCanvas) { + core.dymCanvas[headCanvas].clearRect(0, 0, 32, height); + core.dymCanvas[headCanvas].drawImage(image, posX * 32, posY * height, 32, height - 32, 0, 0, 32, height - 32); + core.relocateCanvas(headCanvas, nowX - core.bigmap.offsetX, nowY + 32 - height - core.bigmap.offsetY); + core.setOpacity(headCanvas, opacity); + } + if (bodyCanvas) { + if (blockInfo.bigImage) { + var face = blockInfo.face; + if (!blockInfo.faceIds) face = 'down'; + else if (!blockInfo.faceIds[face]) { + // 维持此时朝向 + face = 'down'; + for (var f in blockInfo.faceIds) { + if (blockInfo.faceIds[f] == blockInfo.id) { + face = f; + } + } + } + var bigImageInfo = this._getBigImageInfo(blockInfo.bigImage, face, blockInfo.posX); + var per_width = bigImageInfo.per_width, per_height = bigImageInfo.per_height; + core.dymCanvas[bodyCanvas].clearRect(0, 0, bigImageInfo.per_width, bigImageInfo.per_height); + core.dymCanvas[bodyCanvas].drawImage(blockInfo.bigImage, bigImageInfo.sx, bigImageInfo.sy, per_width, per_height, 0, 0, per_width, per_height); + core.relocateCanvas(bodyCanvas, nowX - core.bigmap.offsetX + bigImageInfo.dx, nowY - core.bigmap.offsetY + bigImageInfo.dy); + core.setOpacity(bodyCanvas, opacity); + } else { + core.dymCanvas[bodyCanvas].clearRect(0, 0, 32, 32); + core.dymCanvas[bodyCanvas].drawImage(image, posX * 32, posY * height + height - 32, 32, 32, 0, 0, 32, 32); + core.relocateCanvas(bodyCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); + core.setOpacity(bodyCanvas, opacity); + } + } + if (damageCanvas) { + core.relocateCanvas(damageCanvas, nowX - core.bigmap.offsetX, nowY - core.bigmap.offsetY); + core.setOpacity(damageCanvas, opacity); + } +} + +////// 删除独立的block canvas ////// +maps.prototype._deleteDetachedBlock = function (canvases) { + core.deleteCanvas(canvases.headCanvas); + core.deleteCanvas(canvases.bodyCanvas); + core.deleteCanvas(canvases.damageCanvas); +} + +maps.prototype._getAndRemoveBlock = function (x, y) { + var block = core.getBlock(x, y); + if (block == null) return null; + var blockInfo = this.getBlockInfo(block); + if (blockInfo == null) return; + core.removeBlock(x, y); + return [block, blockInfo]; +} + +////// 显示移动某块的动画,达到{“type”:”move”}的效果 ////// +maps.prototype.moveBlock = function (x, y, steps, time, keep, callback) { + if (core.status.replay.speed == 24) time = 1; + time = time || 500; + var blockArr = this._getAndRemoveBlock(x, y); + if (blockArr == null) { + if (callback) callback(); + return; + } + var block = blockArr[0], blockInfo = blockArr[1]; + var moveSteps = (steps || []).map(function (t) { + return [t.split(':')[0], parseInt(t.split(':')[1] || "1")]; + }).filter(function (t) { + return ['up', 'down', 'left', 'right', 'forward', 'backward', 'leftup', 'leftdown', 'rightup', 'rightdown', 'speed'].indexOf(t[0]) >= 0 + && !(t[0] == 'speed' && t[1] < 16) + }); + var canvases = this._initDetachedBlock(blockInfo, x, y, block.event.animate !== false); + this._moveDetachedBlock(blockInfo, 32 * x, 32 * y, 1, canvases); + + var moveInfo = { + sx: x, sy: y, x: x, y: y, px: 32 * x, py: 32 * y, opacity: 1, keep: keep, lastDirection: null, offset: 1, + moveSteps: moveSteps, step: 0, per_time: time / 16 / core.status.replay.speed + } + this._moveBlock_doMove(blockInfo, canvases, moveInfo, callback); +} + +maps.prototype._moveBlock_doMove = function (blockInfo, canvases, moveInfo, callback) { + var animateTotal = blockInfo.animate, animateTime = 0; + // 强制npc48行走时使用四帧动画 + if (!blockInfo.doorInfo && !blockInfo.bigImage && blockInfo.cls == 'npc48') animateTotal = 4; + var _run = function () { + var cb = function () { + core.maps._deleteDetachedBlock(canvases); + // 不消失 + if (moveInfo.keep) { + core.setBlock(blockInfo.number, moveInfo.x, moveInfo.y); + core.showBlock(moveInfo.x, moveInfo.y); + core.moveEnemyOnPoint(moveInfo.sx, moveInfo.sy, moveInfo.x, moveInfo.y); + } + if (callback) callback(); + } + + var animate = window.setInterval(function () { + if (blockInfo.cls != 'tileset') { + animateTime += moveInfo.per_time; + if (animateTime > core.values.animateSpeed) { + animateTime = 0; + blockInfo.posX = (blockInfo.posX + 1) % animateTotal; + } + } + if (moveInfo.moveSteps.length != 0) { + if (core.maps._moveBlock_updateSpeed(moveInfo)) { + clearInterval(animate); + delete core.animateFrame.asyncId[animate]; + _run(); + } + else core.maps._moveBlock_moving(blockInfo, canvases, moveInfo); + } + else + core.maps._moveJumpBlock_finished(blockInfo, canvases, moveInfo, animate, cb); + }, moveInfo.per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; + } + _run(); +} + +maps.prototype._moveBlock_updateSpeed = function (moveInfo) { + if (moveInfo.step == 0 && moveInfo.moveSteps[0][0] == 'speed' && moveInfo.moveSteps[0][1] >= 16) { + moveInfo.per_time = moveInfo.moveSteps[0][1] / 16 / core.status.replay.speed; + moveInfo.moveSteps.shift(); + return true; + } + return false; +} + +maps.prototype._moveBlock_updateDirection = function (blockInfo, moveInfo) { + moveInfo.offset = 1; + var curr = moveInfo.moveSteps[0]; + // 展开forward和backward + if ((curr[0] == 'backward' || curr[0] == 'forward') && curr[1] > 1) { + moveInfo.moveSteps.shift(); + for (var i = 0; i < curr[1]; ++i) { + moveInfo.moveSteps.unshift([curr[0], 1]); + } + return this._moveBlock_updateDirection(blockInfo, moveInfo); + } + if (moveInfo.lastDirection == null) { + for (var d in blockInfo.faceIds) { + if (blockInfo.faceIds[d] == blockInfo.id) { + moveInfo.lastDirection = d; + break; + } + } + } + if (curr[0] == 'forward' || curr[0] == 'backward') { + if (moveInfo.lastDirection == null) { + moveInfo.moveSteps.shift(); + return false; + } + if (curr[0] == 'backward') + moveInfo.offset = -1; + curr[0] = moveInfo.lastDirection; + } + moveInfo.lastDirection = curr[0]; + + // 根据faceIds修改朝向 + var faceDirection = curr[0]; + if (faceDirection == 'leftup' || faceDirection == 'leftdown') faceDirection = 'left'; + if (faceDirection == 'rightup' || faceDirection == 'rightdown') faceDirection = 'right'; + var currid = blockInfo.faceIds[faceDirection]; + blockInfo.face = faceDirection; + if (currid) { + var posY = core.material.icons[blockInfo.cls][currid]; + if (posY != null) { + blockInfo.number = core.getNumberById(currid) || blockInfo.number; + blockInfo.posY = posY; + } + } + // 处理 left:0 的情况,仅转向 + if (curr[1] <= 0) { + moveInfo.moveSteps.shift(); + return false; + } + moveInfo.x += core.utils.scan2[curr[0]].x * moveInfo.offset; + moveInfo.y += core.utils.scan2[curr[0]].y * moveInfo.offset; + return true; +} + +maps.prototype._moveBlock_moving = function (blockInfo, canvases, moveInfo) { + if (moveInfo.step == 0) { + if (!this._moveBlock_updateDirection(blockInfo, moveInfo)) return; + } + var curr = moveInfo.moveSteps[0]; + moveInfo.step++; + moveInfo.px += core.utils.scan2[curr[0]].x * 2 * moveInfo.offset; + moveInfo.py += core.utils.scan2[curr[0]].y * 2 * moveInfo.offset; + this._moveDetachedBlock(blockInfo, moveInfo.px, moveInfo.py, moveInfo.opacity, canvases); + if (moveInfo.step == 16) { + moveInfo.step = 0; + moveInfo.moveSteps[0][1]--; + if (moveInfo.moveSteps[0][1] <= 0) { + moveInfo.moveSteps.shift(); + } + } +} + +////// 显示跳跃某块的动画,达到{"type":"jump"}的效果 ////// +maps.prototype.jumpBlock = function (sx, sy, ex, ey, time, keep, callback) { + time = time || 500; + var blockArr = this._getAndRemoveBlock(sx, sy); + if (blockArr == null) { + if (callback) callback(); + return; + } + var block = blockArr[0], blockInfo = blockArr[1]; + var canvases = this._initDetachedBlock(blockInfo, sx, sy, block.event.animate !== false); + this._moveDetachedBlock(blockInfo, 32 * sx, 32 * sy, 1, canvases); + var jumpInfo = this.__generateJumpInfo(sx, sy, ex, ey, time); + jumpInfo.keep = keep; + + this._jumpBlock_doJump(blockInfo, canvases, jumpInfo, callback); +} + +maps.prototype.__generateJumpInfo = function (sx, sy, ex, ey, time) { + var dx = ex - sx, dy = ey - sy, distance = Math.round(Math.sqrt(dx * dx + dy * dy)); + var jump_peak = 6 + distance, jump_count = jump_peak * 2; + time /= Math.max(core.status.replay.speed, 1) + return { + sx: sx, sy: sy, x: sx, y: sy, ex: ex, ey: ey, px: 32 * sx, py: 32 * sy, opacity: 1, + jump_peak: jump_peak, jump_count: jump_count, + step: 0, per_time: time / jump_count + }; +} + +maps.prototype._jumpBlock_doJump = function (blockInfo, canvases, jumpInfo, callback) { + var cb = function () { + core.maps._deleteDetachedBlock(canvases); + // 不消失 + if (jumpInfo.keep) { + core.setBlock(blockInfo.number, jumpInfo.ex, jumpInfo.ey); + core.showBlock(jumpInfo.ex, jumpInfo.ey); + core.moveEnemyOnPoint(jumpInfo.sx, jumpInfo.sy, jumpInfo.ex, jumpInfo.ey); + } + if (callback) callback(); + } + + var animate = window.setInterval(function () { + if (jumpInfo.jump_count > 0) + core.maps._jumpBlock_jumping(blockInfo, canvases, jumpInfo) + else + core.maps._moveJumpBlock_finished(blockInfo, canvases, jumpInfo, animate, cb); + }, jumpInfo.per_time); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +maps.prototype.__updateJumpInfo = function (jumpInfo) { + jumpInfo.jump_count--; + jumpInfo.x = (jumpInfo.x * jumpInfo.jump_count + jumpInfo.ex) / (jumpInfo.jump_count + 1.0); + jumpInfo.y = (jumpInfo.y * jumpInfo.jump_count + jumpInfo.ey) / (jumpInfo.jump_count + 1.0); + jumpInfo.px = 32 * jumpInfo.x; + var delta = Math.abs(jumpInfo.jump_count - jumpInfo.jump_peak); + jumpInfo.py = 32 * jumpInfo.y - (jumpInfo.jump_peak * jumpInfo.jump_peak - delta * delta) / 2; +} + +maps.prototype._jumpBlock_jumping = function (blockInfo, canvases, jumpInfo) { + this.__updateJumpInfo(jumpInfo); + core.maps._moveDetachedBlock(blockInfo, jumpInfo.px, jumpInfo.py, jumpInfo.opacity, canvases); +} + +maps.prototype._moveJumpBlock_finished = function (blockInfo, canvases, info, animate, cb) { + if (info.keep) info.opacity = 0; + else info.opacity -= 0.06; + if (info.opacity <= 0) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + else { + this._moveDetachedBlock(blockInfo, info.px, info.py, info.opacity, canvases); + } +} + +////// 显示/隐藏某个块时的动画效果 ////// +maps.prototype.animateBlock = function (loc, type, time, callback) { + if (core.status.replay.speed == 24) time = 1; + if (typeof loc[0] == 'number' && typeof loc[1] == 'number') + loc = [loc]; + if (type != 'show' && type != 'hide' && type != 'remove' && typeof type != 'number') { + if (callback) callback(); + } + // --- 检测所有是0的点 + var list = this._animateBlock_getList(loc, type); + if (list.length == 0) { + if (callback) callback(); + return; + } + this._animateBlock_drawList(list, 0); + time /= Math.max(core.status.replay.speed, 1) + this._animateBlock_doAnimate(loc, list, type, time, callback); +} + +maps.prototype._animateBlock_doAnimate = function (loc, list, type, time, callback) { + var step = 0, steps = Math.max(parseInt(time / 10), 1); + var cb = function () { + list.forEach(function (t) { + if (t.blockInfo) + core.maps._deleteDetachedBlock(t.canvases); + }); + loc.forEach(function (t) { + if (type == 'show') core.showBlock(t[0], t[1]); + else if (type == 'hide') core.hideBlock(t[0], t[1]); + else if (type == 'remove') core.removeBlock(t[0], t[1]); + else { + core.setBlockOpacity(type, t[0], t[1]); + core.showBlock(t[0], t[1]); + } + }); + if (callback) callback(); + } + + var animate = setInterval(function () { + step++; + core.maps._animateBlock_drawList(list, step / steps); + if (step == steps) { + delete core.animateFrame.asyncId[animate]; + clearInterval(animate); + cb(); + } + }, 10); + + core.animateFrame.lastAsyncId = animate; + core.animateFrame.asyncId[animate] = cb; +} + +maps.prototype._animateBlock_getList = function (loc, type) { + var list = []; + loc.forEach(function (t) { + var block = core.getBlock(t[0], t[1], null, true); + if (block == null) return; + + var fromOpacity = block.opacity; + if (fromOpacity == null) fromOpacity = 1.0; + + var blockInfo = core.maps.getBlockInfo(block); + if (blockInfo == null) { + list.push({ 'x': t[0], 'y': t[1] }); + return; + } + if (typeof type == 'number' && block.disable) return; + // 该点是否已经被启用/删除 + if ((type == 'show' && !block.disable) || ((type == 'hide' || type == 'remove') && block.disable)) { + list.push({ 'x': t[0], 'y': t[1] }); + return; + } + + var toOpacity = type; + if (type == 'show') { + toOpacity = fromOpacity; + fromOpacity = 0.0; + } + else if (type == 'hide' || type == 'remove') { + core.hideBlock(t[0], t[1]); // 暂时先隐藏 + toOpacity = 0.0; + } + else { + core.hideBlock(t[0], t[1]); // 暂时先隐藏 + } + + var canvases = core.maps._initDetachedBlock(blockInfo, t[0], t[1], block.event.displayDamage !== false); + + list.push({ + 'x': t[0], 'y': t[1], 'blockInfo': blockInfo, 'canvases': canvases, + 'fromOpacity': fromOpacity, 'toOpacity': toOpacity, + }); + + }); + return list; +} + +maps.prototype._animateBlock_drawList = function (list, progress) { + list.forEach(function (t) { + if (t.blockInfo) + core.maps._moveDetachedBlock(t.blockInfo, t.x * 32, t.y * 32, t.fromOpacity + progress * (t.toOpacity - t.fromOpacity), t.canvases); + }); +} + +// ------ 全局动画控制,动画绘制 ------ // + +////// 添加一个全局动画 ////// +maps.prototype.addGlobalAnimate = function (block) { + if (!block || !block.event) return; + this.removeGlobalAnimate(block.x, block.y, block.name); + if (block.event.cls == 'autotile') { + var id = block.event.id, img = core.material.images.autotile[id]; + if (!img || img.width == 96) return; + core.status.autotileAnimateObjs.push(block); + } + else { + if (!block.event.bigImage && (!block.event.animate || block.event.animate == 1)) return; + core.status.globalAnimateObjs.push(block); + } +} + +////// 删除一个或所有全局动画 ////// +maps.prototype.removeGlobalAnimate = function (x, y, name) { + // 没有定义xy,则全部删除 + if (x == null || y == null) { + core.status.globalAnimateStatus = 0; + core.status.globalAnimateObjs = []; + core.status.autotileAnimateObjs = []; + core.status.floorAnimateObjs = []; + return; + } + + core.status.globalAnimateObjs = core.status.globalAnimateObjs.filter(function (block) { + return block.x != x || block.y != y || block.name != name; + }); + + // 检查Autotile + core.status.autotileAnimateObjs = core.status.autotileAnimateObjs.filter(function (block) { + return block.x != x || block.y != y || block.name != name; + }); +} + +////// 绘制UI层的box动画 ////// +maps.prototype.drawBoxAnimate = function () { + if (core.status.boxAnimateObjs.length == 0) return; + // check ui2 + if (main.mode == 'play' && core.status.boxAnimateObjs.filter(function (one) { return one.bigImage }).length > 0 && !core.dymCanvas.ui2) { + core.createCanvas('ui2', 0, 0, core._PX_, core._PY_, 142); + } + core.clearMap('ui2'); + + core.status.boxAnimateObjs.forEach(function (obj) { + if (obj.bigImage) { + var ctx = obj.ctx || 'ui2'; + var bigImageInfo = core.maps._getBigImageInfo(obj.bigImage, obj.face, core.status.globalAnimateStatus % 4); + var sx = bigImageInfo.sx, sy = bigImageInfo.sy, per_width = bigImageInfo.per_width, per_height = bigImageInfo.per_height; + var actual_width = Math.min(per_width, obj.max_width || per_width), actual_height = per_height * actual_width / per_width; + var x = obj.centerX - actual_width / 2, y = obj.centerY - actual_height / 2; + core.clearMap(ctx, x, y, actual_width, actual_height); + core.fillRect(ctx, x, y, actual_width, actual_height, core.material.groundPattern); + core.strokeRect(ctx, x, y, actual_width, actual_height, 'gold', 2); + core.drawImage(ctx, obj.bigImage, sx, sy, per_width, per_height, + obj.centerX - actual_width / 2, obj.centerY - actual_height / 2, actual_width, actual_height); + } else { + var ctx = obj.ctx || 'ui'; + core.clearMap(ctx, obj.bgx, obj.bgy, obj.bgWidth, obj.bgHeight); + core.fillRect(ctx, obj.bgx, obj.bgy, obj.bgWidth, obj.bgHeight, core.material.groundPattern); + core.drawImage(ctx, obj.image, core.status.globalAnimateStatus % obj.animate * 32, obj.pos, + 32, obj.height, obj.x, obj.y, obj.dw || 32, obj.dh || obj.height); + } + }); + if (main.mode != 'play') core.status.boxAnimateObjs = []; +} + +////// 绘制动画 ////// +maps.prototype.drawAnimate = function (name, x, y, alignWindow, callback) { + name = core.getMappedName(name); + + // 正在播放录像:不显示动画 + if (core.isReplaying() || !core.material.animates[name] || x == null || y == null) { + if (callback) callback(); + return -1; + } + + // 开始绘制 + var animate = core.material.animates[name], centerX = 32 * x + 16, centerY = 32 * y + 16; + if (alignWindow) { + centerX += core.bigmap.offsetX; + centerY += core.bigmap.offsetY; + } + animate.se = animate.se || {}; + if (typeof animate.se == 'string') animate.se = { 1: animate.se }; + + var id = setTimeout(null); + core.status.animateObjs.push({ + "name": name, + "id": id, + "animate": animate, + "centerX": centerX, + "centerY": centerY, + "index": 0, + "callback": callback + }); + + return id; +} + +////// 绘制一个跟随勇士的动画 ////// +maps.prototype.drawHeroAnimate = function (name, callback) { + name = core.getMappedName(name); + + // 正在播放录像或动画不存在:不显示动画 + if (core.isReplaying() || !core.material.animates[name]) { + if (callback) callback(); + return -1; + } + + // 开始绘制 + var animate = core.material.animates[name]; + animate.se = animate.se || {}; + if (typeof animate.se == 'string') animate.se = { 1: animate.se }; + + var id = setTimeout(null); + core.status.animateObjs.push({ + "name": name, + "id": id, + "animate": animate, + "hero": true, + "index": 0, + "callback": callback + }); + + return id; +} + +////// 获得当前正在播放的所有(指定)动画的id列表 ////// +maps.prototype.getPlayingAnimates = function (name) { + return (core.status.animateObjs || []).filter(function (one) { + return name == null || one.name == name; + }).map(function (one) { return one.id }); +} + +////// 绘制动画的某一帧 ////// +maps.prototype._drawAnimateFrame = function (name, animate, centerX, centerY, index) { + var ctx = core.getContextByName(name); + if (!ctx) return; + var frame = animate.frames[index % animate.frame]; + core.playSound((animate.se || {})[index % animate.frame + 1], (animate.pitch || {})[index % animate.frame + 1]); + var ratio = animate.ratio; + frame.forEach(function (t) { + var image = animate.images[t.index]; + if (!image) return; + + var realWidth = image.width * ratio * t.zoom / 100; + var realHeight = image.height * ratio * t.zoom / 100; + core.setAlpha(ctx, t.opacity / 255); + + var cx = centerX + t.x, cy = centerY + t.y; + + var ix = cx - realWidth / 2 - core.bigmap.offsetX, + iy = cy - realHeight / 2 - core.bigmap.offsetY; + + var mirror = t.mirror ? 'x' : null; + var angle = t.angle ? -t.angle * Math.PI / 180 : null; + core.drawImage(ctx, image, ix, iy, realWidth, realHeight, null, null, null, null, angle, mirror); + + core.setAlpha(ctx, 1); + }) +} + +////// 停止动画 ////// +maps.prototype.stopAnimate = function (id, doCallback) { + for (var i = 0; i < core.status.animateObjs.length; i++) { + var obj = core.status.animateObjs[i]; + if (id == null || obj.id == id) { + if (doCallback) { + (function (callback) { + setTimeout(function () { + if (callback) callback(); + }); + })(obj.callback); + } + } + } + core.status.animateObjs = core.status.animateObjs.filter(function (x) { return id != null && x.id != id }); + if (core.status.animateObjs.length == 0) + core.clearMap('animate'); +} diff --git a/libs/thirdparty/LICENSE.md b/libs/thirdparty/LICENSE.md new file mode 100644 index 0000000..2d1241c --- /dev/null +++ b/libs/thirdparty/LICENSE.md @@ -0,0 +1,254 @@ +lzstring +============ + +MIT License + +Copyright (c) 2013 pieroxy + +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. + +localforage +============ + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Mozilla + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +zip +============ + + Copyright (c) 2013 Gildas Lormeau. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/thirdparty/corejs.min.js b/libs/thirdparty/corejs.min.js new file mode 100644 index 0000000..7ee4914 --- /dev/null +++ b/libs/thirdparty/corejs.min.js @@ -0,0 +1,13 @@ +/** + * core-js 3.25.0 + * © 2014-2022 Denis Pushkarev (zloirock.ru) + * license: https://github.com/zloirock/core-js/blob/v3.25.0/LICENSE + * source: https://github.com/zloirock/core-js + */ + !function(t){"use strict";var r,e,n;r=[function(t,r,e){e(1),e(96),e(97),e(98),e(99),e(100),e(101),e(102),e(103),e(104),e(105),e(106),e(107),e(108),e(109),e(110),e(120),e(122),e(132),e(133),e(135),e(138),e(141),e(143),e(145),e(146),e(147),e(148),e(150),e(151),e(153),e(154),e(156),e(160),e(161),e(162),e(163),e(167),e(168),e(170),e(171),e(172),e(174),e(177),e(178),e(179),e(180),e(181),e(186),e(188),e(189),e(190),e(191),e(192),e(199),e(201),e(204),e(206),e(207),e(208),e(209),e(210),e(214),e(215),e(217),e(218),e(219),e(221),e(222),e(223),e(92),e(224),e(225),e(233),e(235),e(236),e(237),e(239),e(240),e(242),e(243),e(245),e(246),e(247),e(249),e(250),e(251),e(252),e(253),e(254),e(255),e(256),e(260),e(261),e(263),e(265),e(266),e(267),e(268),e(269),e(271),e(273),e(274),e(275),e(276),e(278),e(279),e(281),e(282),e(283),e(284),e(286),e(287),e(288),e(289),e(290),e(291),e(292),e(293),e(295),e(296),e(297),e(298),e(299),e(300),e(301),e(302),e(304),e(305),e(306),e(308),e(309),e(310),e(311),e(334),e(335),e(336),e(337),e(338),e(339),e(340),e(341),e(343),e(344),e(345),e(346),e(347),e(348),e(349),e(350),e(351),e(352),e(359),e(360),e(362),e(363),e(364),e(365),e(366),e(368),e(369),e(371),e(374),e(375),e(376),e(377),e(381),e(382),e(384),e(385),e(386),e(387),e(389),e(390),e(391),e(392),e(393),e(394),e(396),e(399),e(402),e(405),e(406),e(407),e(408),e(409),e(410),e(411),e(412),e(413),e(414),e(415),e(416),e(417),e(423),e(424),e(425),e(426),e(427),e(428),e(429),e(430),e(431),e(432),e(433),e(434),e(436),e(440),e(441),e(442),e(443),e(444),e(445),e(446),e(447),e(448),e(449),e(450),e(451),e(452),e(453),e(454),e(455),e(456),e(457),e(458),e(459),e(460),e(461),e(462),e(463),e(464),e(467),e(469),e(478),e(479),e(480),e(482),e(483),e(485),e(486),e(487),e(488),e(489),e(491),e(492),e(493),e(495),e(497),e(498),e(501),e(503),e(504),e(505),e(506),e(507),e(508),e(510),e(511),e(512),e(513),e(514),e(515),e(516),e(518),e(520),e(521),e(522),e(523),e(524),e(525),e(528),e(529),e(530),e(531),e(532),e(533),e(534),e(535),e(536),e(537),e(538),e(539),e(540),e(541),e(542),e(544),e(546),e(548),e(549),e(550),e(551),e(553),e(554),e(556),e(557),e(558),e(559),e(560),e(561),e(563),e(564),e(565),e(566),e(568),e(569),e(570),e(571),e(572),e(574),e(575),e(576),e(577),e(578),e(579),e(580),e(581),e(582),e(583),e(584),e(585),e(587),e(588),e(589),e(594),e(595),e(597),e(598),e(599),e(600),e(601),e(602),e(603),e(604),e(605),e(607),e(608),e(609),e(611),e(612),e(613),e(614),e(615),e(616),e(617),e(618),e(619),e(620),e(621),e(622),e(623),e(624),e(625),e(626),e(627),e(628),e(629),e(630),e(631),e(632),e(633),e(634),e(635),e(636),e(637),e(638),e(639),e(640),e(641),e(642),e(643),e(644),e(646),e(647),e(648),e(649),e(650),e(651),e(652),e(653),e(654),e(655),e(657),e(658),e(661),e(662),e(665),e(666),e(667),e(670),e(671),e(672),e(676),e(681),t.exports=e(682)},function(t,r,e){e(2),e(89),e(91),e(92),e(95)},function(r,e,n){var o=n(3),i=n(4),a=n(8),u=n(14),c=n(35),f=n(6),s=n(26),l=n(7),h=n(38),p=n(24),g=n(46),v=n(12),d=n(18),y=n(68),m=n(11),b=n(71),x=n(73),w=n(57),E=n(75),S=n(66),A=n(5),I=n(44),R=n(72),O=n(10),T=n(47),M=n(34),P=n(53),j=n(54),k=n(40),_=n(33),N=n(78),C=n(79),D=n(81),U=n(82),L=n(51),B=n(83).forEach,W=P("hidden"),z="Symbol",V=L.set,q=L.getterFor(z),G=Object.prototype,H=i.Symbol,K=H&&H.prototype,Y=i.TypeError,$=i.QObject,J=A.f,X=I.f,Q=E.f,Z=O.f,tt=u([].push),rt=M("symbols"),et=M("op-symbols"),nt=M("wks"),ot=!$||!$.prototype||!$.prototype.findChild,it=f&&l((function(){return 7!=b(X({},"a",{get:function(){return X(this,"a",{value:7}).a}})).a}))?function(t,r,e){var n=J(G,r);n&&delete G[r],X(t,r,e),n&&t!==G&&X(G,r,n)}:X,wrap=function(t,r){var e=rt[t]=b(K);return V(e,{type:z,tag:t,description:r}),f||(e.description=r),e},ut=function defineProperty(t,r,e){t===G&&ut(et,r,e),g(t);var n=d(r);return g(e),h(rt,n)?(e.enumerable?(h(t,W)&&t[W][n]&&(t[W][n]=!1),e=b(e,{enumerable:m(0,!1)})):(h(t,W)||X(t,W,m(1,{})),t[W][n]=!0),it(t,n,e)):X(t,n,e)},ct=function defineProperties(t,r){var e,n;return g(t),e=v(r),n=x(e).concat($getOwnPropertySymbols(e)),B(n,(function(r){f&&!a(ft,e,r)||ut(t,r,e[r])})),t},ft=function propertyIsEnumerable(t){var r=d(t),e=a(Z,this,r);return!(this===G&&h(rt,r)&&!h(et,r))&&(!(e||!h(this,r)||!h(rt,r)||h(this,W)&&this[W][r])||e)},st=function getOwnPropertyDescriptor(t,r){var e,n=v(t),o=d(r);if(n!==G||!h(rt,o)||h(et,o))return!(e=J(n,o))||!h(rt,o)||h(n,W)&&n[W][o]||(e.enumerable=!0),e},lt=function getOwnPropertyNames(t){var r=Q(v(t)),e=[];return B(r,(function(t){h(rt,t)||h(j,t)||tt(e,t)})),e},$getOwnPropertySymbols=function(t){var r=t===G,e=Q(r?et:v(t)),n=[];return B(e,(function(t){!h(rt,t)||r&&!h(G,t)||tt(n,rt[t])})),n};s||(H=function Symbol(){var r,e,n;if(p(K,this))throw Y("Symbol is not a constructor");return r=arguments.length&&arguments[0]!==t?y(arguments[0]):t,e=k(r),n=function(t){this===G&&a(n,et,t),h(this,W)&&h(this[W],e)&&(this[W][e]=!1),it(this,e,m(1,t))},f&&ot&&it(G,e,{configurable:!0,set:n}),wrap(e,r)},T(K=H.prototype,"toString",(function toString(){return q(this).tag})),T(H,"withoutSetter",(function(t){return wrap(k(t),t)})),O.f=ft,I.f=ut,R.f=ct,A.f=st,w.f=E.f=lt,S.f=$getOwnPropertySymbols,N.f=function(t){return wrap(_(t),t)},f&&(X(K,"description",{configurable:!0,get:function description(){return q(this).description}}),c||T(G,"propertyIsEnumerable",ft,{unsafe:!0}))),o({global:!0,constructor:!0,wrap:!0,forced:!s,sham:!s},{Symbol:H}),B(x(nt),(function(t){C(t)})),o({target:z,stat:!0,forced:!s},{useSetter:function(){ot=!0},useSimple:function(){ot=!1}}),o({target:"Object",stat:!0,forced:!s,sham:!f},{create:function create(r,e){return e===t?b(r):ct(b(r),e)},defineProperty:ut,defineProperties:ct,getOwnPropertyDescriptor:st}),o({target:"Object",stat:!0,forced:!s},{getOwnPropertyNames:lt}),D(),U(H,z),j[W]=!0},function(r,e,n){var o=n(4),i=n(5).f,a=n(43),u=n(47),c=n(37),f=n(55),s=n(67);r.exports=function(r,e){var n,l,h,p,g,v=r.target,d=r.global,y=r.stat;if(n=d?o:y?o[v]||c(v,{}):(o[v]||{}).prototype)for(l in e){if(p=e[l],h=r.dontCallGetSet?(g=i(n,l))&&g.value:n[l],!s(d?l:v+(y?".":"#")+l,r.forced)&&h!==t){if(typeof p==typeof h)continue;f(p,h)}(r.sham||h&&h.sham)&&a(p,"sham",!0),u(n,l,p,r)}}},function(t,r){var check=function(t){return t&&t.Math==Math&&t};t.exports=check("object"==typeof globalThis&&globalThis)||check("object"==typeof window&&window)||check("object"==typeof self&&self)||check("object"==typeof global&&global)||function(){return this}()||Function("return this")()},function(t,r,e){var n=e(6),o=e(8),i=e(10),a=e(11),u=e(12),c=e(18),f=e(38),s=e(41),l=Object.getOwnPropertyDescriptor;r.f=n?l:function getOwnPropertyDescriptor(t,r){if(t=u(t),r=c(r),s)try{return l(t,r)}catch(e){}if(f(t,r))return a(!o(i.f,t,r),t[r])}},function(t,r,e){var n=e(7);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,r){t.exports=function(t){try{return!!t()}catch(r){return!0}}},function(t,r,e){var n=e(9),o=function(){}.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},function(t,r,e){var n=e(7);t.exports=!n((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")}))},function(t,r,e){var n={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!n.call({1:2},1);r.f=i?function propertyIsEnumerable(t){var r=o(this,t);return!!r&&r.enumerable}:n},function(t,r){t.exports=function(t,r){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:r}}},function(t,r,e){var n=e(13),o=e(16);t.exports=function(t){return n(o(t))}},function(t,r,e){var n=e(14),o=e(7),i=e(15),a=Object,u=n("".split);t.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(t){return"String"==i(t)?u(t,""):a(t)}:a},function(t,r,e){var n=e(9),o=Function.prototype,i=o.call,a=n&&o.bind.bind(i,i);t.exports=n?function(t){return t&&a(t)}:function(t){return t&&function(){return i.apply(t,arguments)}}},function(t,r,e){var n=e(14),o=n({}.toString),i=n("".slice);t.exports=function(t){return i(o(t),8,-1)}},function(t,r,e){var n=e(17),o=TypeError;t.exports=function(t){if(n(t))throw o("Can't call method on "+t);return t}},function(r,e){r.exports=function(r){return null===r||r===t}},function(t,r,e){var n=e(19),o=e(22);t.exports=function(t){var r=n(t,"string");return o(r)?r:r+""}},function(r,e,n){var o=n(8),i=n(20),a=n(22),u=n(29),c=n(32),f=n(33),s=TypeError,l=f("toPrimitive");r.exports=function(r,e){var n,f;if(!i(r)||a(r))return r;if(n=u(r,l)){if(e===t&&(e="default"),f=o(n,r,e),!i(f)||a(f))return f;throw s("Can't convert object to primitive value")}return e===t&&(e="number"),c(r,e)}},function(r,e,n){var o=n(21),i="object"==typeof document&&document.all;r.exports=t===i&&i!==t?function(t){return"object"==typeof t?null!==t:o(t)||t===i}:function(t){return"object"==typeof t?null!==t:o(t)}},function(t,r){t.exports=function(t){return"function"==typeof t}},function(t,r,e){var n=e(23),o=e(21),i=e(24),a=e(25),u=Object;t.exports=a?function(t){return"symbol"==typeof t}:function(t){var r=n("Symbol");return o(r)&&i(r.prototype,u(t))}},function(r,e,n){var o=n(4),i=n(21),aFunction=function(r){return i(r)?r:t};r.exports=function(t,r){return arguments.length<2?aFunction(o[t]):o[t]&&o[t][r]}},function(t,r,e){var n=e(14);t.exports=n({}.isPrototypeOf)},function(t,r,e){var n=e(26);t.exports=n&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},function(t,r,e){var n=e(27),o=e(7);t.exports=!!Object.getOwnPropertySymbols&&!o((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},function(t,r,e){var n,o,i=e(4),a=e(28),u=i.process,c=i.Deno,f=u&&u.versions||c&&c.version,s=f&&f.v8;s&&(o=(n=s.split("."))[0]>0&&n[0]<4?1:+(n[0]+n[1])),!o&&a&&(!(n=a.match(/Edge\/(\d+)/))||n[1]>=74)&&(n=a.match(/Chrome\/(\d+)/))&&(o=+n[1]),t.exports=o},function(t,r,e){var n=e(23);t.exports=n("navigator","userAgent")||""},function(r,e,n){var o=n(30),i=n(17);r.exports=function(r,e){var n=r[e];return i(n)?t:o(n)}},function(t,r,e){var n=e(21),o=e(31),i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not a function")}},function(t,r){var e=String;t.exports=function(t){try{return e(t)}catch(r){return"Object"}}},function(t,r,e){var n=e(8),o=e(21),i=e(20),a=TypeError;t.exports=function(t,r){var e,u;if("string"===r&&o(e=t.toString)&&!i(u=n(e,t)))return u;if(o(e=t.valueOf)&&!i(u=n(e,t)))return u;if("string"!==r&&o(e=t.toString)&&!i(u=n(e,t)))return u;throw a("Can't convert object to primitive value")}},function(t,r,e){var n=e(4),o=e(34),i=e(38),a=e(40),u=e(26),c=e(25),f=o("wks"),s=n.Symbol,l=s&&s["for"],h=c?s:s&&s.withoutSetter||a;t.exports=function(t){if(!i(f,t)||!u&&"string"!=typeof f[t]){var r="Symbol."+t;f[t]=u&&i(s,t)?s[t]:c&&l?l(r):h(r)}return f[t]}},function(r,e,n){var o=n(35),i=n(36);(r.exports=function(r,e){return i[r]||(i[r]=e!==t?e:{})})("versions",[]).push({version:"3.25.0",mode:o?"pure":"global",copyright:"© 2014-2022 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.25.0/LICENSE",source:"https://github.com/zloirock/core-js"})},function(t,r){t.exports=!1},function(t,r,e){var n=e(4),o=e(37),i="__core-js_shared__",a=n[i]||o(i,{});t.exports=a},function(t,r,e){var n=e(4),o=Object.defineProperty;t.exports=function(t,r){try{o(n,t,{value:r,configurable:!0,writable:!0})}catch(e){n[t]=r}return r}},function(t,r,e){var n=e(14),o=e(39),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function hasOwn(t,r){return i(o(t),r)}},function(t,r,e){var n=e(16),o=Object;t.exports=function(t){return o(n(t))}},function(r,e,n){var o=n(14),i=0,a=Math.random(),u=o(1..toString);r.exports=function(r){return"Symbol("+(r===t?"":r)+")_"+u(++i+a,36)}},function(t,r,e){var n=e(6),o=e(7),i=e(42);t.exports=!n&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},function(t,r,e){var n=e(4),o=e(20),i=n.document,a=o(i)&&o(i.createElement);t.exports=function(t){return a?i.createElement(t):{}}},function(t,r,e){var n=e(6),o=e(44),i=e(11);t.exports=n?function(t,r,e){return o.f(t,r,i(1,e))}:function(t,r,e){return t[r]=e,t}},function(t,r,e){var n=e(6),o=e(41),i=e(45),a=e(46),u=e(18),c=TypeError,f=Object.defineProperty,s=Object.getOwnPropertyDescriptor;r.f=n?i?function defineProperty(t,r,e){if(a(t),r=u(r),a(e),"function"==typeof t&&"prototype"===r&&"value"in e&&"writable"in e&&!e.writable){var n=s(t,r);n&&n.writable&&(t[r]=e.value,e={configurable:"configurable"in e?e.configurable:n.configurable,enumerable:"enumerable"in e?e.enumerable:n.enumerable,writable:!1})}return f(t,r,e)}:f:function defineProperty(t,r,e){if(a(t),r=u(r),a(e),o)try{return f(t,r,e)}catch(n){}if("get"in e||"set"in e)throw c("Accessors not supported");return"value"in e&&(t[r]=e.value),t}},function(t,r,e){var n=e(6),o=e(7);t.exports=n&&o((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},function(t,r,e){var n=e(20),o=String,i=TypeError;t.exports=function(t){if(n(t))return t;throw i(o(t)+" is not an object")}},function(r,e,n){var o=n(21),i=n(44),a=n(48),u=n(37);r.exports=function(r,e,n,c){var f,s;if(c||(c={}),f=c.enumerable,s=c.name!==t?c.name:e,o(n)&&a(n,s,c),c.global)f?r[e]=n:u(e,n);else{try{c.unsafe?r[e]&&(f=!0):delete r[e]}catch(l){}f?r[e]=n:i.f(r,e,{value:n,enumerable:!1,configurable:!c.nonConfigurable,writable:!c.nonWritable})}return r}},function(r,e,n){var o=n(7),i=n(21),a=n(38),u=n(6),c=n(49).CONFIGURABLE,f=n(50),s=n(51),l=s.enforce,h=s.get,p=Object.defineProperty,g=u&&!o((function(){return 8!==p((function(){}),"length",{value:8}).length})),v=String(String).split("String"),d=r.exports=function(r,e,n){"Symbol("===String(e).slice(0,7)&&(e="["+String(e).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),n&&n.getter&&(e="get "+e),n&&n.setter&&(e="set "+e),(!a(r,"name")||c&&r.name!==e)&&(u?p(r,"name",{value:e,configurable:!0}):r.name=e),g&&n&&a(n,"arity")&&r.length!==n.arity&&p(r,"length",{value:n.arity});try{n&&a(n,"constructor")&&n.constructor?u&&p(r,"prototype",{writable:!1}):r.prototype&&(r.prototype=t)}catch(i){}var o=l(r);return a(o,"source")||(o.source=v.join("string"==typeof e?e:"")),r};Function.prototype.toString=d((function toString(){return i(this)&&h(this).source||f(this)}),"toString")},function(t,r,e){var n=e(6),o=e(38),i=Function.prototype,a=n&&Object.getOwnPropertyDescriptor,u=o(i,"name"),c=u&&"something"===function something(){}.name,f=u&&(!n||n&&a(i,"name").configurable);t.exports={EXISTS:u,PROPER:c,CONFIGURABLE:f}},function(t,r,e){var n=e(14),o=e(21),i=e(36),a=n(Function.toString);o(i.inspectSource)||(i.inspectSource=function(t){return a(t)}),t.exports=i.inspectSource},function(t,r,e){var n,o,i,a,u,c,f,s,l=e(52),h=e(4),p=e(14),g=e(20),v=e(43),d=e(38),y=e(36),m=e(53),b=e(54),x="Object already initialized",w=h.TypeError;l||y.state?(a=y.state||(y.state=new(0,h.WeakMap)),u=p(a.get),c=p(a.has),f=p(a.set),n=function(t,r){if(c(a,t))throw w(x);return r.facade=t,f(a,t,r),r},o=function(t){return u(a,t)||{}},i=function(t){return c(a,t)}):(b[s=m("state")]=!0,n=function(t,r){if(d(t,s))throw w(x);return r.facade=t,v(t,s,r),r},o=function(t){return d(t,s)?t[s]:{}},i=function(t){return d(t,s)}),t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!g(r)||(e=o(r)).type!==t)throw w("Incompatible receiver, "+t+" required");return e}}}},function(t,r,e){var n=e(4),o=e(21),i=n.WeakMap;t.exports=o(i)&&/native code/.test(String(i))},function(t,r,e){var n=e(34),o=e(40),i=n("keys");t.exports=function(t){return i[t]||(i[t]=o(t))}},function(t,r){t.exports={}},function(t,r,e){var n=e(38),o=e(56),i=e(5),a=e(44);t.exports=function(t,r,e){var u,c,f=o(r),s=a.f,l=i.f;for(u=0;uf;)o(n,e=r[f++])&&(~a(s,e)||c(s,e));return s}},function(t,r,e){var n=e(12),o=e(60),i=e(63),createMethod=function(t){return function(r,e,a){var u,c=n(r),f=i(c),s=o(a,f);if(t&&e!=e){for(;f>s;)if((u=c[s++])!=u)return!0}else for(;f>s;s++)if((t||s in c)&&c[s]===e)return t||s||0;return!t&&-1}};t.exports={includes:createMethod(!0),indexOf:createMethod(!1)}},function(t,r,e){var n=e(61),o=Math.max,i=Math.min;t.exports=function(t,r){var e=n(t);return e<0?o(e+r,0):i(e,r)}},function(t,r,e){var n=e(62);t.exports=function(t){var r=+t;return r!=r||0===r?0:n(r)}},function(t,r){var e=Math.ceil,n=Math.floor;t.exports=Math.trunc||function trunc(t){var r=+t;return(r>0?n:e)(r)}},function(t,r,e){var n=e(64);t.exports=function(t){return n(t.length)}},function(t,r,e){var n=e(61),o=Math.min;t.exports=function(t){return t>0?o(n(t),9007199254740991):0}},function(t,r){t.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},function(t,r){r.f=Object.getOwnPropertySymbols},function(t,r,e){var n=e(7),o=e(21),i=/#|\.prototype\./,isForced=function(t,r){var e=u[a(t)];return e==f||e!=c&&(o(r)?n(r):!!r)},a=isForced.normalize=function(t){return String(t).replace(i,".").toLowerCase()},u=isForced.data={},c=isForced.NATIVE="N",f=isForced.POLYFILL="P";t.exports=isForced},function(t,r,e){var n=e(69),o=String;t.exports=function(t){if("Symbol"===n(t))throw TypeError("Cannot convert a Symbol value to a string");return o(t)}},function(r,e,n){var o=n(70),i=n(21),a=n(15),u=n(33)("toStringTag"),c=Object,f="Arguments"==a(function(){return arguments}());r.exports=o?a:function(r){var e,n,o;return r===t?"Undefined":null===r?"Null":"string"==typeof(n=function(t,r){try{return t[r]}catch(e){}}(e=c(r),u))?n:f?a(e):"Object"==(o=a(e))&&i(e.callee)?"Arguments":o}},function(t,r,e){var n={};n[e(33)("toStringTag")]="z",t.exports="[object z]"===String(n)},function(r,e,n){var o,i=n(46),a=n(72),u=n(65),c=n(54),f=n(74),s=n(42),l=n(53)("IE_PROTO"),EmptyConstructor=function(){},scriptTag=function(t){return"