merge from origin:master

This commit is contained in:
echo 2018-01-20 13:39:43 +08:00
commit efe8138a2d
30 changed files with 1508 additions and 386 deletions

29
LICENSE.md Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2018, Zhang Chen
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.

View File

@ -45,6 +45,12 @@ HTML5 canvas制作的魔塔样板支持全平台游戏
## 更新说明
### 2018.1.12 V1.3.1
* [x] 增加虚拟键盘
* [x] 增加自动存档回退A键可快速读档
* [x] 修复几处较为严重的Bug
### 2018.1.1 V1.3
* [x] 支持全键盘操作。

View File

@ -93,6 +93,7 @@ core.setAutoHeroMove // 设置勇士的自动行走路线
core.autoHeroMove // 让勇士开始自动行走
core.setHeroMoveInterval // 设置行走的效果动画
core.setHeroMoveTriggerInterval // 设置勇士行走过程中对事件的触发检测
core.moveAction // 实际每一步的行走过程
* core.turnHero(direction) // 设置勇士的方向(转向)
core.canMoveHero // 勇士能否前往某方向
core.moveHero // 让勇士开始移动
@ -178,16 +179,21 @@ core.clone // 深拷贝一个对象
core.formatDate // 格式化时间为字符串
core.setTwoDigits // 两位数显示
core.debug // 进入Debug模式攻防血和钥匙都调成很高的数值
core.replay // 开始回放
core.checkStatus // 判断当前能否进入某个事件
core.openBook // 点击怪物手册时的打开操作
core.useFly // 点击楼层传送器时的打开操作
core.openToolbox // 点击工具栏时的打开操作
core.openQuickShop // 点击快捷商店时的打开操作
core.save // 点击保存按钮时的打开操作
core.load // 点击读取按钮时的打开操作
core.openSettings // 点击设置按钮时的打开操作
core.doSL // 实际进行存读档事件
core.syncSave // 存档同步操作
core.saveData // 存档到本地
core.loadData // 从本地读档
core.encodeRoute // 将路线压缩
core.decodeRoute // 将路线解压缩
* core.setStatus // 设置勇士属性
* core.getStatus // 获得勇士属性
core.getLvName // 获得某个等级的名称
@ -260,6 +266,7 @@ core.events.changeLight // 改变亮灯(感叹号)的事件
* core.events.afterLoadData // 读档事件后,载入事件前,可以执行的操作
// ------ 点击事件和键盘事件的处理 ------
core.events.longClick // 长按
core.events.keyDownCtrl // 按下Ctrl键时快捷跳过对话
core.events.clickConfirmBox // 确认框界面时的点击操作
core.events.keyUpConfirmBox // 确认框界面时,放开某个键的操作
@ -295,6 +302,7 @@ core.events.keyUpSettings // 系统菜单栏界面时,放开某个键的操作
core.events.clickSyncSave // 同步存档界面时的点击操作
core.events.keyDownSyncSave // 同步存档界面时,按下某个键的操作
core.events.keyUpSyncSave // 同步存档界面时,放开某个键的操作
core.events.clickKeyBoard // 虚拟键盘界面时的点击操作
core.events.clickAbout // “关于”界面时的点击操作
```

View File

@ -142,10 +142,6 @@ floorId指定的是目标楼层的唯一标识符ID
可以指定time指定后切换动画时长为指定的数值。
楼梯和传送门默认可`"穿透"`。所谓穿透,就是当寻路穿过一个楼梯/传送门后不会触发楼层传送事件而是继续前进。通过系统Flag可以指定是否穿透你也可以对每个传送点单独设置该项。
![楼层转换穿透](./img/floorset.png)
## 背景音乐
本塔支持BGM和SE的播放。
@ -190,12 +186,14 @@ this.sounds = [ // 在此存放所有的SE和文件名一致
- **点任意块并拖动:** 指定寻路路线
- **单击勇士:** 转向
- **双击勇士:** 轻按(仅在轻按开关打开时有效)
- **长按任意位置:** 打开虚拟键盘
键盘操作快捷键如下:
- **[CTRL]** 跳过对话
- **[X]** 打开/关闭怪物手册
- **[G]** 打开/关闭楼层传送器
- **[A]** 读取自动存档
- **[S/D]** 打开/关闭存/读档页面
- **[K]** 打开/关闭快捷商店选择列表
- **[T]** 打开/关闭工具栏

View File

@ -465,6 +465,8 @@ direction为可选的指定的话将使勇士的朝向变成该方向
time为可选的指定的话将作为楼层切换动画的时间。
**time也可以置为0如果为0则没有楼层切换动画。**
!> **changeFloor到达一个新的楼层将不会执行firstArrive事件如有需求请在到达点设置自定义事件然后使用type: trigger立刻调用之。**
### changePos: 当前位置切换/勇士转向
@ -844,8 +846,9 @@ events.prototype.addPoint = function (enemy) {
全局商店定义在`data.js`中找到shops一项。
``` js
"shops": { // 定义全局商店(即快捷商店)
"moneyShop1": { // 商店唯一ID
"shops": [ // 定义全局商店(即快捷商店)
{
"id": "moneyShop1", // 商店唯一ID
"name": "贪婪之神", // 商店名称(标题)
"icon": "blueShop", // 商店图标blueShop为蓝色商店pinkShop为粉色商店
"textInList": "1F金币商店", // 在快捷商店栏中显示的名称
@ -870,7 +873,8 @@ events.prototype.addPoint = function (enemy) {
// "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍
]
},
"expShop1": { // 商店唯一ID
{
"id": "expShop1", // 商店唯一ID
"name": "经验之神",
"icon": "pinkShop",
"textInList": "1F经验商店",
@ -885,15 +889,15 @@ events.prototype.addPoint = function (enemy) {
{"text": "攻击+5", "need": "30", "effect": "status:atk+=5"},
{"text": "防御+5", "need": "30", "effect": "status:def+=5"},
]
},
},
}
],
```
全局商店全部定义在`data.js`中的shops一项里。
每个全局商店有一个唯一标识符ID然后是一系列对该商店的定义。
全局商店全部定义在`data.js`中的shops一项里。商店以数组形式存放每一个商店都是其中的一个对象。
- id 为商店的唯一标识符ID请确保任何两个商店的id都不相同
- name 为商店的名称(打开商店后的标题)
- icon 为商店的图标,blueShop为蓝色商店pinkShop为粉色商店
- icon 为商店的图标,在icons.js的npcs中定义。如woman可代表一个商人。
- textInList 为其在快捷商店栏中显示的名称,如"3楼金币商店"等
- use 为消耗的类型是金币money还是经验experience
- need 是一个表达式,计算商店所需要用到的数值。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -32,7 +32,7 @@
<div id='startButtons'>
<span class='startButton' id='playGame'>开始游戏</span>
<span class='startButton' id='loadGame'>载入游戏</span>
<span class='startButton' id='aboutGame'>关于本塔</span>
<span class='startButton' id='replayGame'>录像回放</span>
</div>
<div id='levelChooseButtons'>
<span class='startButton' id='easyLevel'>简单</span>

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ data.prototype.init = function() {
this.firstData = {
"title": "魔塔样板", // 游戏名,将显示在标题页面以及切换楼层的界面中
"name": "template", // 游戏的唯一英文标识符。由英文、数字、下划线组成不能超过20个字符。
"version": "Ver 1.0.0 (Beta)", // 当前游戏版本;版本不一致的存档不能通用。
"version": "Ver 1.3.2", // 当前游戏版本;版本不一致的存档不能通用。
"floorId": "sample0", // 初始楼层ID
"hero": { // 勇士初始数据
"name": "阳光", // 勇士名;可以改成喜欢的
@ -32,17 +32,19 @@ data.prototype.init = function() {
"poison": false, // 毒
"weak": false, // 衰
"curse": false, // 咒
}
},
"steps": 0, // 行走步数统计
},
"startText": [ // 游戏开始前剧情。如果无剧情直接留一个空数组即可。
"Hi欢迎来到 HTML5 魔塔样板!\n\n本样板由艾之葵制作可以让你在不会写任何代码\n的情况下也能做出属于自己的H5魔塔",
"这里游戏开始时的剧情。\n定义在data.js的startText处。\n\n你可以在这里写上自己的内容。",
"赶快来试一试吧!"
],
"shops": { // 定义全局商店(即快捷商店)
"moneyShop1": { // 商店唯一ID
"shops": [ // 定义全局商店(即快捷商店)
{
"id": "moneyShop1", // 商店唯一ID
"name": "贪婪之神", // 商店名称(标题)
"icon": "blueShop", // 商店图标,blueShop为蓝色商店pinkShop为粉色商店
"icon": "blueShop", // 商店图标,在icons.js中的npc一项定义
"textInList": "1F金币商店", // 在快捷商店栏中显示的名称
"use": "money", // 商店所要使用的。只能是"money"或"experience"。
"need": "20+10*times*(times+1)", // 商店需要的金币/经验数值可以是一个表达式以times作为参数计算。
@ -65,7 +67,8 @@ data.prototype.init = function() {
// "status:hp+=2*(status:atk+status:def)" 将生命提升攻防和的数值的两倍
]
},
"expShop1": { // 商店唯一ID
{
"id": "expShop1", // 商店唯一ID
"name": "经验之神",
"icon": "pinkShop",
"textInList": "1F经验商店",
@ -80,8 +83,8 @@ data.prototype.init = function() {
{"text": "攻击+5", "need": "30", "effect": "status:atk+=5"},
{"text": "防御+5", "need": "30", "effect": "status:def+=5"},
]
},
},
}
],
"levelUp": [ // 经验升级所需要的数值,是一个数组
{}, // 第一项为初始等级可以简单留空也可以写name
@ -94,7 +97,7 @@ data.prototype.init = function() {
// effect也允许写一个function代表本次升级将会执行的操作
{"need": 40, "effect": function () {
core.drawText("恭喜升级!");
core.insertAction("恭喜升级!");
core.status.hero.hp *= 2;
core.status.hero.atk += 100;
core.status.hero.def += 100;
@ -155,7 +158,7 @@ data.prototype.init = function() {
"bigKeyIsBox": false, // 如果此项为true则视为钥匙盒红黄蓝钥匙+1若为false则视为大黄门钥匙
/****** 怪物相关 ******/
"enableNegativeDamage": true, // 是否支持负伤害(回血)
"zoneSquare": false, // 领域类型。如果此项为true则为九宫格伤害为false则为十字伤害
"betweenAttackCeil": false, // 夹击方式是向上取整还是向下取整。如果此项为true则为向上取整为false则为向下取整
/****** 系统相关 ******/
"startDirectly": false, // 点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面
"canOpenBattleAnimate": true, // 是否允许用户开启战斗过程如果此项为false则下面两项均强制视为false
@ -164,8 +167,8 @@ data.prototype.init = function() {
"displayEnemyDamage": true, // 是否地图怪物显伤;用户可以手动在菜单栏中开关
"displayExtraDamage": false, // 是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关
"enableGentleClick": true, // 是否允许轻触(获得面前物品)
"portalWithoutTrigger": true, // 经过楼梯、传送门时是否能“穿透”。穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件
"potionWhileRouting": false, // 寻路算法是否经过血瓶如果该项为false则寻路算法会自动尽量绕过血瓶
"enableViewMaps": true, // 是否支持在菜单栏中查看所有楼层的地图
}
}

View File

@ -24,8 +24,8 @@ enemys.prototype.init = function () {
'slimeMan': {'name': '影子战士', 'hp': 100, 'atk': 0, 'def': 0, 'money': 11, 'experience': 0, 'special': 10}, // 模仿怪的攻防设为0就好
'bluePriest': {'name': '初级法师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 3, 'experience': 0, 'special': 2, 'point': 1}, // 'point'可以在打败怪物后进行加点,详见文档说明。
'redPriest': {'name': '高级法师', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100, 'zoneSquare': true}, // 领域怪需要加value表示领域伤害的数值zoneSquare代表是否九宫格伤害
'redWizard': {'name': '高级巫师', 'hp': 1000, 'atk': 1200, 'def': 0, 'money': 160, 'experience': 0, 'special': 15, 'value': 200, 'range': 2}, // range可选代表领域伤害的范围不加默认为1
'brownWizard': {'name': '初级巫师', 'hp': 100, 'atk': 120, 'def': 0, 'money': 16, 'experience': 0, 'special': 15, 'value': 100, 'range': 2}, // 领域怪需要加value表示领域伤害的数值range可选代表领域伤害的范围不加默认为1
'redWizard': {'name': '高级巫师', 'hp': 1000, 'atk': 1200, 'def': 0, 'money': 160, 'experience': 0, 'special': 15, 'value': 200, 'zoneSquare': true}, // zoneSquare可选代表是否九宫格伤害true为是九宫格伤害false或不设置为十字伤害
'yellowGuard': {'name': '初级卫兵', 'hp': 100, 'atk': 120, 'def': 0, 'money': 10, 'experience': 0, 'special': 0},
'blueGuard': {'name': '中级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
'redGuard': {'name': '高级卫兵', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
@ -248,11 +248,11 @@ enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mo
if (this.hasSpecial(mon_special, 8)) counterDamage += parseInt(core.values.counterAttack * hero_atk);
// 先攻
var damage = mon_special == 1 ? per_damage : 0;
var damage = this.hasSpecial(mon_special, 1) ? per_damage : 0;
// 破甲
if (this.hasSpecial(mon_special, 7)) damage += parseInt(core.values.breakArmor * hero_def);
// 净化
if (this.hasSpecial(mon_special, 9)) damage = core.values.purify * hero_mdef;
if (this.hasSpecial(mon_special, 9)) damage += core.values.purify * hero_mdef;
var turn = parseInt((mon_hp - 1) / (hero_atk - mon_def));
var ans = damage + turn * per_damage + (turn + 1) * counterDamage;

View File

@ -6,6 +6,7 @@ function events() {
events.prototype.init = function () {
this.events = {
'battle': function (data, core, callback) {
core.autosave();
core.battle(data.event.id, data.x, data.y);
if (core.isset(callback))
callback();
@ -16,9 +17,11 @@ events.prototype.init = function () {
callback();
},
'openDoor': function (data, core, callback) {
core.openDoor(data.event.id, data.x, data.y, true);
if (core.isset(callback))
callback();
core.autosave();
core.openDoor(data.event.id, data.x, data.y, true, function () {
if (core.isset(callback)) callback();
core.replay();
});
},
'changeFloor': function (data, core, callback) {
var heroLoc = {};
@ -27,7 +30,10 @@ events.prototype.init = function () {
if (core.isset(data.event.data.direction))
heroLoc.direction = data.event.data.direction;
core.changeFloor(data.event.data.floorId, data.event.data.stair,
heroLoc, data.event.data.time, callback);
heroLoc, data.event.data.time, function () {
if (core.isset(callback)) callback();
core.replay();
});
},
'passNet': function (data, core, callback) {
core.events.passNet(data);
@ -107,44 +113,136 @@ events.prototype.setInitData = function (hard) {
////// 游戏获胜事件 //////
events.prototype.win = function(reason) {
core.ui.closePanel();
var replaying = core.status.replay.replaying;
core.status.replay.replaying=false;
core.waitHeroToStop(function() {
core.removeGlobalAnimate(0,0,true);
core.clearMap('all'); // 清空全地图
core.drawText([
"\t[结局2]恭喜通关!你的分数是${status:hp}。"
"\t[恭喜通关]你的分数是${status:hp}。"
], function () {
core.restart();
core.events.gameOver(true, replaying);
})
});
}
////// 游戏失败事件 //////
events.prototype.lose = function(reason) {
core.ui.closePanel();
var replaying = core.status.replay.replaying;
core.status.replay.replaying=false;
core.waitHeroToStop(function() {
core.status.replay.replaying=false;
core.drawText([
"\t[结局1]你死了。\n如题。"
], function () {
core.restart();
core.events.gameOver(false, replaying);
});
})
}
////// 游戏结束 //////
events.prototype.gameOver = function (success, fromReplay) {
// 上传成绩
var confirmUpload = function () {
if (!success) {
core.restart();
return;
}
var doUpload = function(username) {
if (username==null) username="";
// 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.isPC?"PC":core.platform.isAndroid?"Android":core.platform.isIOS?"iOS":"");
formData.append('hard', core.status.hard);
formData.append('username', username);
formData.append('lv', core.status.hero.lv);
formData.append('hp', core.status.hero.hp);
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.experience);
formData.append('steps', core.status.hero.steps);
formData.append('route', core.encodeRoute(core.status.route));
var xhr = new XMLHttpRequest();
xhr.open("POST", "/games/upload.php");
xhr.send(formData);
core.restart();
}
core.ui.drawConfirmBox("你想记录你的ID和成绩吗", function () {
doUpload(prompt("请输入你的ID"));
}, function () {
doUpload("");
})
return;
}
// 下载录像
var confirmDownload = function () {
core.ui.closePanel();
core.ui.drawConfirmBox("你想下载录像吗?", function () {
var obj = {
'name': core.firstData.name,
'version': core.firstData.version,
'hard': core.status.hard,
'route': core.encodeRoute(core.status.route)
}
core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify(obj));
confirmUpload();
}, function () {
confirmUpload();
})
}
if (fromReplay) {
core.drawText("录像回放完毕!", function () {
core.restart();
});
}
else {
confirmDownload();
}
}
////// 转换楼层结束的事件 //////
events.prototype.afterChangeFloor = function (floorId) {
if (!core.isset(core.status.event.id) && !core.hasFlag("visited_"+floorId)) {
this.doEvents(core.floors[floorId].firstArrive);
if (core.isset(core.status.event.id)) return; // 当前存在事件
if (!core.hasFlag("visited_"+floorId)) {
this.doEvents(core.floors[floorId].firstArrive, null, null, function () {
core.autosave();
});
core.setFlag("visited_"+floorId, true);
return;
}
// 自动存档
core.autosave();
}
////// 开始执行一系列自定义事件 //////
events.prototype.doEvents = function (list, x, y, callback) {
if (!core.isset(list)) return;
if (!(list instanceof Array)) {
list = [list];
}
// 停止勇士
core.waitHeroToStop(function() {
if (!core.isset(list)) return;
if (!(list instanceof Array)) {
list = [list];
}
core.lockControl();
core.status.event = {'id': 'action', 'data': {
'list': core.clone(list), 'x': x, 'y': y, 'callback': callback
@ -165,6 +263,7 @@ events.prototype.doAction = function() {
if (core.isset(core.status.event.data.callback))
core.status.event.data.callback();
core.ui.closePanel();
core.replay();
return;
}
@ -178,13 +277,20 @@ events.prototype.doAction = function() {
// 如果是文字:显示
if (typeof data == "string") {
core.status.event.data.type='text';
core.ui.drawTextBox(data);
// 如果是正在回放中,不显示
if (core.status.replay.replaying)
core.events.doAction();
else
core.ui.drawTextBox(data);
return;
}
core.status.event.data.type=data.type;
switch (data.type) {
case "text": // 文字/对话
core.ui.drawTextBox(data.data);
if (core.status.replay.isreplaying)
core.events.doAction();
else
core.ui.drawTextBox(data.data);
break;
case "tip":
core.drawTip(core.replaceText(data.text));
@ -270,7 +376,12 @@ events.prototype.doAction = function() {
this.doAction();
break;
case "openShop": // 打开一个全局商店
core.events.openShop(data.id);
if (core.status.replay.replaying) { // 正在播放录像简单将visited置为true
core.status.shops[data.id].visited=true;
this.doAction();
}
else
core.events.openShop(data.id);
break;
case "disableShop": // 禁用一个全局商店
core.events.disableQuickShop(data.id);
@ -351,13 +462,41 @@ events.prototype.doAction = function() {
this.doAction();
break;
case "choices": // 提供选项
if (core.status.replay.replaying) {
if (core.status.replay.toReplay.length==0) { // 回放完毕
core.status.replay.replaying=false;
core.drawTip("录像回放完毕");
}
else {
var action = core.status.replay.toReplay.shift(), index;
if (action.indexOf("choices:")==0 && ((index=parseInt(action.substring(8)))>=0) && index<data.choices.length) {
//core.status.route.push("choices:"+index);
//this.insertAction(data.choices[index].action);
//this.doAction();
core.status.event.selection=index;
setTimeout(function () {
core.status.route.push("choices:"+index);
core.events.insertAction(data.choices[index].action);
core.events.doAction();
}, 500)
}
else {
core.status.replay.replaying=false;
core.drawTip("录像文件出错");
}
}
}
core.ui.drawChoices(data.text, data.choices);
break;
case "win":
core.events.win(data.reason);
core.events.win(data.reason, function () {
core.events.doAction();
});
break;
case "lose":
core.events.lose(data.reason);
core.events.lose(data.reason, function () {
core.events.doAction();
});
break;
case "function":
var func = data["function"];
@ -401,12 +540,15 @@ events.prototype.doAction = function() {
}
////// 往当前事件列表之前添加一个或多个事件 //////
events.prototype.insertAction = function (action) {
events.prototype.insertAction = function (action, x, y, callback) {
if (core.status.event.id == null) {
this.doEvents(action);
this.doEvents(action, x, y, callback);
}
else {
core.unshift(core.status.event.data.list, action)
if (core.isset(x)) core.status.event.data.x=x;
if (core.isset(y)) core.status.event.data.y=y;
if (core.isset(callback)) core.status.event.data.callback=callback;
}
}
@ -423,11 +565,15 @@ events.prototype.openShop = function(shopId, needVisited) {
shop.visited = true;
var selection = core.status.event.selection;
var actions = [];
if (core.isset(core.status.event.data) && core.isset(core.status.event.data.actions))
actions=core.status.event.data.actions;
core.ui.closePanel();
core.lockControl();
// core.status.event = {'id': 'shop', 'data': {'id': shopId, 'shop': shop}};
core.status.event.id = 'shop';
core.status.event.data = {'id': shopId, 'shop': shop};
core.status.event.data = {'id': shopId, 'shop': shop, 'actions': actions};
core.status.event.selection = selection;
// 拼词
@ -458,7 +604,7 @@ events.prototype.disableQuickShop = function (shopId) {
}
////// 能否使用快捷商店 //////
events.prototype.canUseQuickShop = function(shopIndex) {
events.prototype.canUseQuickShop = function(shopId) {
if (core.isset(core.floors[core.status.floorId].canUseQuickShop) && !core.isset(core.floors[core.status.floorId].canUseQuickShop))
return '当前不能使用快捷商店。';
@ -696,6 +842,14 @@ events.prototype.afterLoadData = function(data) {
/********** 点击事件、键盘事件 ************/
/****************************************/
////// 长按 //////
events.prototype.longClick = function () {
core.waitHeroToStop(function () {
// 绘制快捷键
core.ui.drawKeyBoard();
});
}
////// 按下Ctrl键时快捷跳过对话 //////
events.prototype.keyDownCtrl = function () {
if (core.status.event.id=='text') {
@ -756,6 +910,8 @@ events.prototype.clickAction = function (x,y) {
if (x >= 5 && x <= 7) {
var topIndex = 6 - parseInt((choices.length - 1) / 2);
if (y>=topIndex && y<topIndex+choices.length) {
// 选择
core.status.route.push("choices:"+(y-topIndex));
this.insertAction(choices[y-topIndex].action);
this.doAction();
}
@ -771,12 +927,10 @@ events.prototype.keyDownAction = function (keycode) {
if (choices.length>0) {
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -794,6 +948,7 @@ events.prototype.keyUpAction = function (keycode) {
var choices = data.choices;
if (choices.length>0) {
if (keycode==13 || keycode==32 || keycode==67) {
core.status.route.push("choices:"+core.status.event.selection);
this.insertAction(choices[core.status.event.selection].action);
this.doAction();
}
@ -870,6 +1025,7 @@ events.prototype.clickFly = function(x,y) {
var index=core.status.hero.flyRange.indexOf(core.status.floorId);
var stair=core.status.event.data<index?"upFloor":"downFloor";
var floorId=core.status.event.data;
core.status.route.push("fly:"+core.status.hero.flyRange[floorId]);
core.changeFloor(core.status.hero.flyRange[floorId], stair);
core.ui.closePanel();
}
@ -892,6 +1048,38 @@ events.prototype.keyUpFly = function (keycode) {
return;
}
////// 查看地图界面时的点击操作 //////
events.prototype.clickViewMaps = function (x,y) {
if(y<=4) {
core.ui.drawMaps(core.status.event.data+1);
}
else if (y>=8) {
core.ui.drawMaps(core.status.event.data-1);
}
else {
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 1);
core.ui.closePanel();
}
}
////// 查看地图界面时,按下某个键的操作 //////
events.prototype.keyDownViewMaps = function (keycode) {
if (keycode==37 || keycode==38) core.ui.drawMaps(core.status.event.data+1);
else if (keycode==39 || keycode==40) core.ui.drawMaps(core.status.event.data-1);
return;
}
////// 查看地图界面时,放开某个键的操作 //////
events.prototype.keyUpViewMaps = function (keycode) {
if (keycode==27 || keycode==88 || keycode==13 || keycode==32 || keycode==67) {
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 1);
core.ui.closePanel();
}
return;
}
////// 商店界面时的点击操作 //////
events.prototype.clickShop = function(x,y) {
var shop = core.status.event.data.shop;
@ -899,6 +1087,9 @@ events.prototype.clickShop = function(x,y) {
if (x >= 5 && x <= 7) {
var topIndex = 6 - parseInt(choices.length / 2);
if (y>=topIndex && y<topIndex+choices.length) {
core.status.event.selection=y-topIndex;
//this.insertAction(choices[y-topIndex].action);
//this.doAction();
var money = core.getStatus('money'), experience = core.getStatus('experience');
@ -912,11 +1103,12 @@ events.prototype.clickShop = function(x,y) {
if (need > eval(use)) {
core.drawTip("你的"+use_text+"不足");
return;
return false;
}
eval(use+'-='+need);
core.status.event.data.actions.push(y-topIndex);
eval(use+'-='+need);
core.setStatus('money', money);
core.setStatus('experience', experience);
@ -930,27 +1122,29 @@ events.prototype.clickShop = function(x,y) {
}
// 离开
else if (y==topIndex+choices.length) {
if (core.status.event.data.actions.length>0) {
core.status.route.push("shop:"+core.status.event.data.id+":"+core.status.event.data.actions.join(""));
}
core.status.boxAnimateObjs = [];
core.setBoxAnimate();
if (core.status.event.data.fromList)
core.ui.drawQuickShop();
else core.ui.closePanel();
}
else return false;
}
return true;
}
////// 商店界面时,按下某个键的操作 //////
events.prototype.keyDownShop = function (keycode) {
var shop = core.status.event.data.shop;
var choices = shop.choices;
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>choices.length) core.status.event.selection=choices.length;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -982,7 +1176,7 @@ events.prototype.clickQuickShop = function(x, y) {
if (x >= 5 && x <= 7) {
var topIndex = 6 - parseInt(keys.length / 2);
if (y>=topIndex && y<topIndex+keys.length) {
var reason = core.events.canUseQuickShop(y-topIndex);
var reason = core.events.canUseQuickShop(keys[y - topIndex]);
if (core.isset(reason)) {
core.drawText(reason);
return;
@ -999,15 +1193,12 @@ events.prototype.clickQuickShop = function(x, y) {
////// 快捷商店界面时,按下某个键的操作 //////
events.prototype.keyDownQuickShop = function (keycode) {
var shopList = core.status.shops, keys = Object.keys(shopList);
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>keys.length) core.status.event.selection=keys.length;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -1141,13 +1332,17 @@ events.prototype.keyUpToolbox = function (keycode) {
////// 存读档界面时的点击操作 //////
events.prototype.clickSL = function(x,y) {
var index=core.status.event.data;
var page = parseInt(index/10), offset=index%10;
// 上一页
if ((x == 3 || x == 4) && y == 12) {
core.ui.drawSLPanel(core.status.event.data - 6);
core.ui.drawSLPanel(10*(page-1)+offset);
}
// 下一页
if ((x == 8 || x == 9) && y == 12) {
core.ui.drawSLPanel(core.status.event.data + 6);
core.ui.drawSLPanel(10*(page+1)+offset);
}
// 返回
if (x>=10 && x<=12 && y==12) {
@ -1158,50 +1353,77 @@ events.prototype.clickSL = function(x,y) {
return;
}
var page=parseInt((core.status.event.data-1)/6);
var index=6*page+1;
if (y>=1 && y<=4) {
if (x>=1 && x<=3) core.doSL(index, core.status.event.id);
if (x>=5 && x<=7) core.doSL(index+1, core.status.event.id);
if (x>=9 && x<=11) core.doSL(index+2, core.status.event.id);
if (x>=1 && x<=3) core.doSL("autoSave", core.status.event.id);
if (x>=5 && x<=7) core.doSL(5*page+1, core.status.event.id);
if (x>=9 && x<=11) core.doSL(5*page+2, core.status.event.id);
}
if (y>=7 && y<=10) {
if (x>=1 && x<=3) core.doSL(index+3, core.status.event.id);
if (x>=5 && x<=7) core.doSL(index+4, core.status.event.id);
if (x>=9 && x<=11) core.doSL(index+5, core.status.event.id);
if (x>=1 && x<=3) core.doSL(5*page+3, core.status.event.id);
if (x>=5 && x<=7) core.doSL(5*page+4, core.status.event.id);
if (x>=9 && x<=11) core.doSL(5*page+5, core.status.event.id);
}
}
////// 存读档界面时,按下某个键的操作 //////
events.prototype.keyDownSL = function(keycode) {
var index=core.status.event.data;
var page = parseInt(index/10), offset=index%10;
if (keycode==37) { // left
core.ui.drawSLPanel(core.status.event.data - 1);
if (offset==0) {
core.ui.drawSLPanel(10*(page-1) + 5);
}
else {
core.ui.drawSLPanel(index - 1);
}
return;
}
if (keycode==38) { // up
core.ui.drawSLPanel(core.status.event.data - 3);
if (offset<3) {
core.ui.drawSLPanel(10*(page-1) + offset + 3);
}
else {
core.ui.drawSLPanel(index - 3);
}
return;
}
if (keycode==39) { // right
core.ui.drawSLPanel(core.status.event.data + 1);
if (offset==5) {
core.ui.drawSLPanel(10*(page+1)+1);
}
else {
core.ui.drawSLPanel(index + 1);
}
return;
}
if (keycode==40) { // down
core.ui.drawSLPanel(core.status.event.data + 3);
if (offset>=3) {
core.ui.drawSLPanel(10*(page+1) + offset - 3);
}
else {
core.ui.drawSLPanel(index + 3);
}
return;
}
if (keycode==33) { // PAGEUP
core.ui.drawSLPanel(core.status.event.data - 6);
core.ui.drawSLPanel(10*(page-1) + offset);
return;
}
if (keycode==34) { // PAGEDOWN
core.ui.drawSLPanel(core.status.event.data + 6);
core.ui.drawSLPanel(10*(page+1) + offset);
return;
}
}
////// 存读档界面时,放开某个键的操作 //////
events.prototype.keyUpSL = function (keycode) {
var index=core.status.event.data;
var page = parseInt(index/10), offset=index%10;
if (keycode==27 || keycode==88 || (core.status.event.id == 'save' && keycode==83) || (core.status.event.id == 'load' && keycode==68)) {
core.ui.closePanel();
if (!core.isPlaying()) {
@ -1210,7 +1432,12 @@ events.prototype.keyUpSL = function (keycode) {
return;
}
if (keycode==13 || keycode==32 || keycode==67) {
core.doSL(core.status.event.data, core.status.event.id);
if (offset==0) {
core.doSL("autoSave", core.status.event.id);
}
else {
core.doSL(5*page+offset, core.status.event.id);
}
return;
}
}
@ -1219,7 +1446,7 @@ events.prototype.keyUpSL = function (keycode) {
events.prototype.clickSwitchs = function (x,y) {
if (x<5 || x>7) return;
var choices = [
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单"
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "下载离线版本", "返回主菜单"
];
var topIndex = 6 - parseInt((choices.length - 1) / 2);
if (y>=topIndex && y<topIndex+choices.length) {
@ -1262,8 +1489,11 @@ events.prototype.clickSwitchs = function (x,y) {
core.ui.drawSwitchs();
break;
case 5:
window.open(core.firstData.name+".zip", "_blank");
break;
case 6:
core.status.event.selection=0;
core.ui.drawSettings(false);
core.ui.drawSettings();
break;
}
}
@ -1271,17 +1501,12 @@ events.prototype.clickSwitchs = function (x,y) {
////// 系统设置界面时,按下某个键的操作 //////
events.prototype.keyDownSwitchs = function (keycode) {
var choices = [
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单"
];
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -1290,11 +1515,11 @@ events.prototype.keyDownSwitchs = function (keycode) {
events.prototype.keyUpSwitchs = function (keycode) {
if (keycode==27 || keycode==88) {
core.status.event.selection=0;
core.ui.drawSettings(false);
core.ui.drawSettings();
return;
}
var choices = [
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "返回主菜单"
"背景音乐", "背景音效", "战斗动画", "怪物显伤", "领域显伤", "下载离线版本", "返回主菜单"
];
if (keycode==13 || keycode==32 || keycode==67) {
var topIndex = 6 - parseInt((choices.length - 1) / 2);
@ -1307,7 +1532,7 @@ events.prototype.keyUpSwitchs = function (keycode) {
events.prototype.clickSettings = function (x,y) {
if (x<5 || x>7) return;
var choices = [
"系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏"
"系统设置", "快捷商店", "浏览地图", "同步存档", "重新开始", "数据统计", "操作帮助", "关于本塔", "返回游戏"
];
var topIndex = 6 - parseInt((choices.length - 1) / 2);
if (y>=topIndex && y<topIndex+choices.length) {
@ -1323,26 +1548,80 @@ events.prototype.clickSettings = function (x,y) {
core.ui.drawQuickShop();
break;
case 2:
if (!core.flags.enableViewMaps) {
core.drawTip("本塔不允许浏览地图!");
}
else {
core.drawText("\t[系统提示]即将进入浏览地图模式。\n\n点击地图上半部分或按[↑]键可查看前一张地图\n点击地图下半部分或按[↓]键可查看后一张地图\n点击地图中间或按[ESC]键可离开浏览地图模式", function () {
core.ui.drawMaps(core.floorIds.indexOf(core.status.floorId));
})
}
break;
case 3:
core.status.event.selection=0;
core.ui.drawSyncSave();
break;
case 3:
case 4:
core.status.event.selection=1;
core.ui.drawConfirmBox("你确定要重新开始吗?", function () {
core.ui.closePanel();
core.restart();
}, function () {
core.status.event.selection=3;
core.ui.drawSettings(false);
core.ui.drawSettings();
});
break;
case 4:
core.ui.drawHelp();
break;
case 5:
core.ui.drawAbout();
core.ui.drawWaiting("正在拉取统计信息,请稍后...");
var formData = new FormData();
formData.append('type', 'getinfo');
formData.append('name', core.firstData.name);
formData.append('version', core.firstData.version);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/games/upload.php");
xhr.onload = function(e) {
if (xhr.status==200) {
var response = JSON.parse(xhr.response);
if (response.code<0) {
core.drawText("出错啦!\n无法拉取统计信息。\n错误原因"+response.msg);
}
else {
var text="\t[本塔统计信息]";
var toAdd=false;
response.data.forEach(function (t) {
if (toAdd) text+="\n\n";
toAdd=true;
if (t.hard!='') text+=t.hard+"难度: "
text+="已有"+t.people+"人次游戏,"+t.score+"人次通关。";
if (core.isset(t.max) && t.max>0) {
text+="\n当前MAX为"+t.max+",最早由 "+(t.username||"匿名")+" 于"+core.formatDate(new Date(1000*t.timestamp))+"打出。";
}
})
core.drawText(text);
}
}
else {
core.drawText("出错啦!\n无法拉取统计信息。\n错误原因HTTP "+xhr.status);
}
};
xhr.ontimeout = function() {
core.drawText("出错啦!\n无法拉取统计信息。\n错误原因Timeout");
}
xhr.onerror = function() {
core.drawText("出错啦!\n无法拉取统计信息。\n错误原因XHR Error");
}
xhr.send(formData);
break;
case 6:
core.ui.drawHelp();
break;
case 7:
core.ui.drawAbout();
break;
case 8:
core.ui.closePanel();
break;
}
@ -1352,17 +1631,12 @@ events.prototype.clickSettings = function (x,y) {
////// 系统菜单栏界面时,按下某个键的操作 //////
events.prototype.keyDownSettings = function (keycode) {
var choices = [
"系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏"
];
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -1374,7 +1648,7 @@ events.prototype.keyUpSettings = function (keycode) {
return;
}
var choices = [
"系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏"
"系统设置", "快捷商店", "浏览地图", "同步存档", "重新开始", "数据统计", "操作帮助", "关于本塔", "返回游戏"
];
if (keycode==13 || keycode==32 || keycode==67) {
var topIndex = 6 - parseInt((choices.length - 1) / 2);
@ -1386,7 +1660,7 @@ events.prototype.keyUpSettings = function (keycode) {
events.prototype.clickSyncSave = function (x,y) {
if (x<5 || x>7) return;
var choices = [
"同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单"
"同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "清空所有存档", "返回主菜单"
];
var topIndex = 6 - parseInt((choices.length - 1) / 2);
if (y>=topIndex && y<topIndex+choices.length) {
@ -1399,18 +1673,61 @@ events.prototype.clickSyncSave = function (x,y) {
core.syncSave("load");
break;
case 2:
var saves = [];
for (var i=1;i<=150;i++) {
var data = core.getLocalStorage("save"+i, null);
if (core.isset(data)) {
saves.push(data);
}
}
var content = {
"name": core.firstData.name,
"version": core.firstData.version,
"data": saves
}
core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5save", JSON.stringify(content));
break;
case 3:
core.readFile(function (obj) {
if (obj.name!=core.firstData.name) {
alert("存档和游戏不一致!");
return;
}
if (obj.version!=core.firstData.version) {
alert("游戏版本不一致!");
return;
}
if (!core.isset(obj.data)) {
alert("无效的存档!");
return;
}
var data=obj.data;
for (var i=1;i<=150;i++) {
if (i<=data.length) {
core.setLocalStorage("save"+i, data[i-1]);
}
else {
core.removeLocalStorage("save"+i);
}
}
core.drawText("读取成功!\n你的本地所有存档均已被覆盖。");
}, function () {
});
break;
case 4:
core.status.event.selection=1;
core.ui.drawConfirmBox("你确定要清空所有本地存档吗?", function() {
core.ui.drawConfirmBox("你确定要清空所有存档吗?", function() {
localStorage.clear();
core.drawText("\t[操作成功]你的本地所有存档已被清空。");
core.drawText("\t[操作成功]你的所有存档已被清空。");
}, function() {
core.status.event.selection=2;
core.ui.drawSyncSave(false);
})
break;
case 3:
core.status.event.selection=2;
core.ui.drawSettings(false);
case 5:
core.status.event.selection=3;
core.ui.drawSettings();
break;
}
@ -1420,17 +1737,12 @@ events.prototype.clickSyncSave = function (x,y) {
////// 同步存档界面时,按下某个键的操作 //////
events.prototype.keyDownSyncSave = function (keycode) {
var choices = [
"同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单"
];
if (keycode==38) {
core.status.event.selection--;
if (core.status.event.selection<0) core.status.event.selection=0;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
if (keycode==40) {
core.status.event.selection++;
if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1;
core.ui.drawChoices(core.status.event.ui.text, core.status.event.ui.choices);
}
}
@ -1439,11 +1751,11 @@ events.prototype.keyDownSyncSave = function (keycode) {
events.prototype.keyUpSyncSave = function (keycode) {
if (keycode==27 || keycode==88) {
core.status.event.selection=2;
core.ui.drawSettings(false);
core.ui.drawSettings();
return;
}
var choices = [
"同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单"
"同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "清空所有存档", "返回主菜单"
];
if (keycode==13 || keycode==32 || keycode==67) {
var topIndex = 6 - parseInt((choices.length - 1) / 2);
@ -1451,6 +1763,65 @@ events.prototype.keyUpSyncSave = function (keycode) {
}
}
////// “虚拟键盘”界面时的点击操作 //////
events.prototype.clickKeyBoard = function (x, y) {
if (y==3 && x>=1 && x<=11) {
core.ui.closePanel();
core.keyUp(112+x-1); // F1-F12: 112-122
}
if (y==4 && x>=1 && x<=10) {
core.ui.closePanel();
core.keyUp(x==10?48:48+x); // 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==5 && x>=1 && x<=10) {
core.ui.closePanel();
core.keyUp(lines[0][x-1].charCodeAt(0));
}
if (y==6 && x>=1 && x<=9) {
core.ui.closePanel();
core.keyUp(lines[1][x-1].charCodeAt(0));
}
if (y==7 && x>=1 && x<=7) {
core.ui.closePanel();
core.keyUp(lines[2][x-1].charCodeAt(0));
}
if (y==8 && x>=1 && x<=11) {
core.ui.closePanel();
if (x==1) core.keyUp(189); // -
if (x==2) core.keyUp(187); // =
if (x==3) core.keyUp(219); // [
if (x==4) core.keyUp(221); // ]
if (x==5) core.keyUp(220); // \
if (x==6) core.keyUp(186); // ;
if (x==7) core.keyUp(222); // '
if (x==8) core.keyUp(188); // ,
if (x==9) core.keyUp(190); // .
if (x==10) core.keyUp(191); // /
if (x==11) core.keyUp(192); // `
}
if (y==9 && x>=1 && x<=10) {
core.ui.closePanel();
if (x==1) core.keyUp(27); // ESC
if (x==2) core.keyUp(9); // TAB
if (x==3) core.keyUp(20); // CAPS
if (x==4) core.keyUp(16); // SHIFT
if (x==5) core.keyUp(17); // CTRL
if (x==6) core.keyUp(18); // ALT
if (x==7) core.keyUp(32); // SPACE
if (x==8) core.keyUp(8); // BACKSPACE
if (x==9) core.keyUp(13); // ENTER
if (x==10) core.keyUp(46); // DEL
}
if (y==10 && x>=9 && x<=11)
core.ui.closePanel();
}
////// “关于”界面时的点击操作 //////
events.prototype.clickAbout = function () {
if (core.isPlaying())

View File

@ -50,7 +50,6 @@ main.floors.sample0 = {
"\t[老人,womanMagician]这些是路障、楼梯、传送门。",
"\t[老人,womanMagician]血网的伤害数值、中毒后每步伤害数值、衰弱时攻防下降的数值,都在 data.js 内定义。\n\n路障同样会尽量被自动寻路绕过。",
"\t[老人,womanMagician]楼梯和传送门需要在changeFloor中定义目标楼层和位置可参见样板里已有的的写法。",
"\t[老人,womanMagician]楼梯和传送门是否可“穿透”由data.js中的全局变量所决定你也可以单独设置。\n穿透的意思是自动寻路得到的路径中间经过了楼梯行走时是否触发楼层转换事件。\n例如下面的“下箭头”就是不能穿透的。",
{"type": "hide", "time": 500}
],
"2,8": [ // 守着第一批怪物的老人
@ -75,7 +74,6 @@ main.floors.sample0 = {
{"type": "hide", "time": 500}
]
},
},
"changeFloor": { // 楼层转换事件该事件不能和上面的events有冲突同位置点否则会被覆盖
"6,0": {"floorId": "sample1", "stair": "downFloor"}, // 目标点sample1层的下楼梯位置
@ -85,8 +83,8 @@ main.floors.sample0 = {
"2,12": {"floorId": "sample0", "loc": [2,12]},
"3,12": {"floorId": "sample0", "loc": [6,1], "direction": "up"}, // 切换楼层后勇士面对上方
"4,12": {"floorId": "sample0", "loc": [0,9], "direction": "left", "time": 1000}, // 切换楼层后勇士面对左边切换动画1000ms
"5,12": {"floorId": "sample0", "loc": [6,10], "portalWithoutTrigger": false}, // 不能穿透
"6,12": {"floorId": "sample0", "loc": [10,10], "direction": "left", "time": 1000, "portalWithoutTrigger": false},
"5,12": {"floorId": "sample0", "loc": [6,10], "time": 0}, // time=0表示无切换时间
"6,12": {"floorId": "sample0", "loc": [10,10], "direction": "left", "time": 1000},
},
"afterBattle": { // 战斗后可能触发的事件列表
"2,6": ["\t[ghostSkeleton]不可能,你怎么可能打败我!\n一个打败怪物触发的事件"]

View File

@ -14,23 +14,23 @@ icons.prototype.init = function () {
'ground': 0,
'grass': 1,
'grass2': 2,
'snowGround': 3,
'ground2': 4,
'ground3': 5,
'ground4': 6,
'sand': 7,
'ground5': 8,
'yellowWall2': 9,
'whiteWall2': 10,
'blueWall2': 11,
'blockWall': 12,
'grayWall': 13,
'white': 14,
'ground6': 15,
'soil': 16,
'yellowWall': 17,
'whiteWall': 18,
'blueWall': 19,
'yellowWall': 3,
'whiteWall': 4,
'blueWall': 5,
'snowGround': 6,
'ground2': 7,
'ground3': 8,
'ground4': 9,
'sand': 10,
'ground5': 11,
'yellowWall2': 12,
'whiteWall2': 13,
'blueWall2': 14,
'blockWall': 15,
'grayWall': 16,
'white': 17,
'ground6': 18,
'soil': 19,
'star': 20,
'lava': 21,
'ice': 22,

View File

@ -143,8 +143,11 @@ items.prototype.getItemEffectTip = function(itemId) {
}
////// 使用道具 //////
items.prototype.useItem = function (itemId) {
if (!this.canUseItem(itemId)) return;
items.prototype.useItem = function (itemId, callback) {
if (!this.canUseItem(itemId)) {
if (core.isset(callback)) callback();
return;
}
var itemCls = core.material.items[itemId].cls;
if (itemId=='book') core.ui.drawBook(0);
@ -174,6 +177,7 @@ items.prototype.useItem = function (itemId) {
// 上楼器/下楼器
core.changeFloor(core.status.event.data.id, null, {'direction': core.status.hero.loc.direction, 'x': core.status.event.data.x, 'y': core.status.event.data.y}, null, function (){
core.drawTip(core.material.items[itemId].name + "使用成功");
core.replay();
});
}
if (itemId == 'poisonWine') core.setFlag('poison', false);
@ -193,11 +197,19 @@ items.prototype.useItem = function (itemId) {
core.setFlag('curse', false);
}
core.updateStatusBar();
// 记录路线
if (itemId!='book' && itemId!='fly') {
core.status.route.push("item:"+itemId);
}
// 道具使用完毕:删除
if (itemCls=='tools')
core.status.hero.items[itemCls][itemId]--;
if (core.status.hero.items[itemCls][itemId]==0)
delete core.status.hero.items[itemCls][itemId];
if (core.isset(callback)) callback();
}
////// 当前能否使用道具 //////

View File

@ -356,16 +356,7 @@ maps.prototype.load = function (data, floorId) {
}
////// 将当前地图重新变成二维数组形式 //////
maps.prototype.getMapArray = function (maps, floorId){
if (!core.isset(floorId)) {
var map = {};
for (var id in maps) {
map[id] = this.getMapArray(maps, id);
}
return map;
}
var thisFloor = maps[floorId];
maps.prototype.getMapArray = function (blockArray){
var blocks = [];
for (var x=0;x<13;x++) {
@ -374,7 +365,7 @@ maps.prototype.getMapArray = function (maps, floorId){
blocks[x].push(0);
}
}
thisFloor.blocks.forEach(function (block) {
blockArray.forEach(function (block) {
if (!(core.isset(block.enable) && !block.enable))
blocks[block.y][block.x] = block.id;
});

View File

@ -257,6 +257,8 @@ ui.prototype.drawChoices = function(content, choices) {
if (choices.length>0) {
if (!core.isset(core.status.event.selection)) core.status.event.selection=0;
if (core.status.event.selection<0) core.status.event.selection=0;
if (core.status.event.selection>=choices.length) core.status.event.selection=choices.length-1;
var len = core.canvas.ui.measureText(core.replaceText(choices[core.status.event.selection].text || choices[core.status.event.selection])).width;
core.strokeRect('ui', 208-len/2-5, choice_top + 32 * core.status.event.selection - 20, len+10, 28, "#FFD700", 2);
}
@ -271,7 +273,8 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) {
core.status.event.data = {'yes': yesCallback, 'no': noCallback};
core.status.event.ui = text;
if (!core.isset(core.status.event.selection)) core.status.event.selection=1;
if (!core.isset(core.status.event.selection) || core.status.event.selection>1) core.status.event.selection=1;
if (core.status.event.selection<0) core.status.event.selection=0;
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
core.clearMap('ui', 0, 0, 416, 416);
@ -319,29 +322,26 @@ ui.prototype.drawSwitchs = function() {
var choices = [
"背景音乐:"+(core.musicStatus.bgmStatus ? "[ON]" : "[OFF]"),
"背景音效:"+(core.musicStatus.soundStatus ? "[ON]" : "[OFF]"),
"战斗动画: " + (core.flags.battleAnimate ? "[ON]" : "[OFF]"),
"怪物显伤: " + (core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"),
"领域显伤: " + (core.flags.displayExtraDamage ? "[ON]" : "[OFF]"),
"战斗动画: "+(core.flags.battleAnimate ? "[ON]" : "[OFF]"),
"怪物显伤: "+(core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"),
"领域显伤: "+(core.flags.displayExtraDamage ? "[ON]" : "[OFF]"),
"下载离线版本",
"返回主菜单"
];
this.drawChoices(null, choices);
}
////// 绘制系统菜单栏 //////
ui.prototype.drawSettings = function (need) {
if (!core.checkStatus('settings', need))
return;
ui.prototype.drawSettings = function () {
core.status.event.id = 'settings';
this.drawChoices(null, [
"系统设置", "快捷商店", "同步存档", "重新开始", "操作帮助", "关于本塔", "返回游戏"
"系统设置", "快捷商店", "浏览地图", "同步存档", "重新开始", "数据统计", "操作帮助", "关于本塔", "返回游戏"
]);
}
////// 绘制快捷商店选择栏 //////
ui.prototype.drawQuickShop = function (need) {
if (core.isset(need) && !core.checkStatus('selectShop', need))
return;
ui.prototype.drawQuickShop = function () {
core.status.event.id = 'selectShop';
@ -654,12 +654,17 @@ ui.prototype.drawWaiting = function(text) {
core.setAlpha('ui', 1);
core.setFillStyle('ui', background);
var left = 97, top = 208 - 32 - 16, right = 416 - 2 * left, bottom = 416 - 2 * top;
core.setFont('ui', 'bold 17px Verdana');
var text_length = core.canvas.ui.measureText(text).width;
var right = Math.max(text_length+50, 220);
var left = 208-right/2, top = 208 - 32 - 16, bottom = 416 - 2 * top;
core.fillRect('ui', left, top, right, bottom, background);
core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2);
core.canvas.ui.textAlign = "center";
core.fillText('ui', text, 208, top + 56, "#FFFFFF", "bold 17px Verdana");
core.fillText('ui', text, 208, top + 56, '#FFFFFF');
}
@ -669,7 +674,7 @@ ui.prototype.drawSyncSave = function () {
core.status.event.id = 'syncSave';
this.drawChoices(null, [
"同步存档到服务器", "从服务器加载存档", "清空本地存档", "返回主菜单"
"同步存档到服务器", "从服务器加载存档", "存档至本地文件", "从本地文件读档", "清空所有存档", "返回主菜单"
]);
}
@ -902,6 +907,38 @@ ui.prototype.drawFly = function(page) {
this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 20, 100, 273);
}
////// 绘制浏览地图界面 //////
ui.prototype.drawMaps = function (index) {
if (!core.isset(index)) index=core.floorIds.indexOf(core.status.floorId);
if (index<0) index=0;
if (index>=core.floorIds.length) index=core.floorIds.length-1;
core.lockControl();
core.status.event.id = 'viewMaps';
core.status.event.data = index;
var floorId = core.floorIds[index];
clearTimeout(core.interval.tipAnimate);
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 1);
this.drawThumbnail(floorId, 'ui', core.status.maps[floorId].blocks, 0, 0, 416);
core.clearMap('data', 0, 0, 416, 416);
core.setOpacity('data', 0.2);
core.canvas.data.textAlign = 'left';
core.setFont('data', '16px Arial');
var text = core.floors[floorId].title;
var textX = 16, textY = 18, width = textX + core.canvas.data.measureText(text).width + 16, height = 42;
core.fillRect('data', 5, 5, width, height, '#000');
core.setOpacity('data', 0.5);
core.fillText('data', text, textX + 5, textY + 15, '#fff');
}
////// 绘制道具栏 //////
ui.prototype.drawToolbox = function(index) {
@ -1020,16 +1057,15 @@ ui.prototype.drawToolbox = function(index) {
////// 绘制存档/读档界面 //////
ui.prototype.drawSLPanel = function(index) {
if (!core.isset(index)) index=1;
if (index<=0) index=1;
if (index>180) index=180;
if (index<0) index=0;
var page = parseInt(index/10), offset=index%10;
if (page>=30) page=29;
if (offset>5) offset=5;
index=10*page+offset;
core.status.event.data=index;
var page=parseInt((index-1)/6);
// core.status.event.data = page;
// core.status.savePage = page;
core.clearMap('ui', 0, 0, 416, 416);
core.setAlpha('ui', 0.85);
core.fillRect('ui', 0, 0, 416, 416, '#000000');
@ -1040,12 +1076,11 @@ ui.prototype.drawSLPanel = function(index) {
var name=core.status.event.id=='save'?"存档":"读档";
for (var i=0;i<6;i++) {
var id=6*page+i+1;
var data=core.getLocalStorage("save"+id,null);
var id=5*page+i;
var data=core.getLocalStorage(i==0?"autoSave":"save"+id, null);
if (i<3) {
core.fillText('ui', name+id, (2*i+1)*u, 35, '#FFFFFF', "bold 17px Verdana");
core.strokeRect('ui', (2*i+1)*u-size/2, 50, size, size, id==index?'#FFD700':'#FFFFFF', id==index?6:2);
core.fillText('ui', i==0?"自动存档":name+id, (2*i+1)*u, 35, '#FFFFFF', "bold 17px Verdana");
core.strokeRect('ui', (2*i+1)*u-size/2, 50, size, size, i==offset?'#FFD700':'#FFFFFF', i==offset?6:2);
if (core.isset(data) && core.isset(data.floorId)) {
this.drawThumbnail(data.floorId, 'ui', core.maps.load(data.maps, data.floorId).blocks, (2*i+1)*u-size/2, 50, size, data.hero.loc);
core.fillText('ui', core.formatDate(new Date(data.time)), (2*i+1)*u, 65+size, '#FFFFFF', '10px Verdana');
@ -1057,7 +1092,7 @@ ui.prototype.drawSLPanel = function(index) {
}
else {
core.fillText('ui', name+id, (2*i-5)*u, 230, '#FFFFFF', "bold 17px Verdana");
core.strokeRect('ui', (2*i-5)*u-size/2, 245, size, size, id==index?'#FFD700':'#FFFFFF', id==index?6:2);
core.strokeRect('ui', (2*i-5)*u-size/2, 245, size, size, i==offset?'#FFD700':'#FFFFFF', i==offset?6:2);
if (core.isset(data) && core.isset(data.floorId)) {
this.drawThumbnail(data.floorId, 'ui', core.maps.load(data.maps, data.floorId).blocks, (2*i-5)*u-size/2, 245, size, data.hero.loc);
core.fillText('ui', core.formatDate(new Date(data.time)), (2*i-5)*u, 260+size, '#FFFFFF', '10px Verdana');
@ -1091,7 +1126,7 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL
}
}
var mapArray = core.maps.getMapArray(core.status.maps, floorId);
var mapArray = core.maps.getMapArray(blocks);
for (var b in blocks) {
var block = blocks[b];
if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable)) {
@ -1116,6 +1151,44 @@ ui.prototype.drawThumbnail = function(floorId, canvas, blocks, x, y, size, heroL
}
}
ui.prototype.drawKeyBoard = function () {
core.lockControl();
core.status.event.id = 'keyBoard';
core.clearMap('ui', 0, 0, 416, 416);
var left = 16, top = 48, right = 416 - 2 * left, bottom = 416 - 2 * top;
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
core.fillRect('ui', left, top, right, bottom, background);
core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2);
core.canvas.ui.textAlign = "center";
core.fillText('ui', "虚拟键盘", 208, top+35, "#FFD700", "bold 22px Verdana");
core.setFont('ui', '17px Verdana');
core.setFillStyle('ui', '#FFFFFF');
var offset = 128-9;
var lines = [
["F1","F2","F3","F4","F5","F6","F7","F8","F9","10","11"],
["1","2","3","4","5","6","7","8","9","0"],
["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"],
["-","=","[","]","\\",";","'",",",".","/","`"],
["ES","TA","CA","SH","CT","AL","SP","BS","EN","DE"]
]
lines.forEach(function (line) {
for (var i=0;i<line.length;i++) {
core.fillText('ui', line[i], 48+32*i, offset);
}
offset+=32;
});
core.fillText("ui", "返回游戏", 416-80, offset-3, '#FFFFFF', 'bold 15px Verdana');
}
////// 绘制“关于”界面 //////
ui.prototype.drawAbout = function() {
@ -1151,11 +1224,13 @@ ui.prototype.drawHelp = function () {
"[CTRL] 跳过对话\n" +
"[X] 打开/关闭怪物手册\n" +
"[G] 打开/关闭楼层传送器\n" +
"[A] 读取自动存档(回退)\n" +
"[S/D] 打开/关闭存/读档页面\n" +
"[K] 打开/关闭快捷商店选择列表\n" +
"[T] 打开/关闭工具栏\n" +
"[ESC] 打开/关闭系统菜单\n" +
"[H] 打开帮助页面\n"+
"[R] 回放\n"+
"[SPACE] 轻按(仅在轻按开关打开时有效)\n" +
"[1] 快捷使用破墙镐\n" +
"[2] 快捷使用炸弹/圣锤\n" +
@ -1165,6 +1240,7 @@ ui.prototype.drawHelp = function () {
"点任意块: 寻路并移动\n"+
"点任意块并拖动: 指定寻路路线\n"+
"单击勇士: 转向\n"+
"双击勇士: 轻按(仅在轻按开关打开时有效)"
"双击勇士: 轻按(仅在轻按开关打开时有效)\n"+
"长按任意位置:打开虚拟键盘"
]);
}

37
main.js
View File

@ -2,7 +2,7 @@ function main() {
//------------------------ 用户修改内容 ------------------------//
this.version = "0.1"; // 游戏版本号如果更改了游戏内容建议修改此version以免造成缓存问题。
this.version = "1.3.2"; // 游戏版本号如果更改了游戏内容建议修改此version以免造成缓存问题。
this.useCompress = false; // 是否使用压缩文件
// 当你即将发布你的塔时请使用“JS代码压缩工具”将所有js代码进行压缩然后将这里的useCompress改为true。
@ -53,7 +53,7 @@ function main() {
'startButtons': document.getElementById('startButtons'),
'playGame': document.getElementById('playGame'),
'loadGame': document.getElementById('loadGame'),
'aboutGame': document.getElementById('aboutGame'),
'replayGame': document.getElementById('replayGame'),
'levelChooseButtons': document.getElementById('levelChooseButtons'),
'easyLevel': document.getElementById('easyLevel'),
'normalLevel': document.getElementById('normalLevel'),
@ -338,7 +338,7 @@ main.statusBar.image.toolbox.onclick = function () {
////// 点击状态栏中的快捷商店时 //////
main.statusBar.image.shop.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawQuickShop(true);
main.core.openQuickShop(true);
}
////// 点击状态栏中的存档按钮时 //////
@ -356,7 +356,7 @@ main.statusBar.image.load.onclick = function () {
////// 点击状态栏中的系统菜单时 //////
main.statusBar.image.settings.onclick = function () {
if (main.core.isPlaying())
main.core.ui.drawSettings(true);
main.core.openSettings(true);
}
////// 点击“开始游戏”时 //////
@ -377,8 +377,33 @@ main.dom.loadGame.onclick = function() {
}
////// 点击“关于本塔”时 //////
main.dom.aboutGame.onclick = function () {
main.core.ui.drawAbout();
main.dom.replayGame.onclick = function () {
// main.core.ui.drawAbout();
core.readFile(function (obj) {
if (obj.name!=core.firstData.name) {
alert("存档和游戏不一致!");
return;
}
if (obj.version!=core.firstData.version) {
alert("游戏版本不一致!");
return;
}
if (!core.isset(obj.route) || !core.isset(obj.hard)) {
alert("无效的录像!");
return;
}
core.dom.startPanel.style.display = 'none';
core.resetStatus(core.firstData.hero, obj.hard, core.firstData.floorId, null, core.initStatus.maps);
core.events.setInitData(obj.hard);
core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() {
core.setHeroMoveTriggerInterval();
core.replay(core.decodeRoute(obj.route));
});
}, function () {
})
}
////// 点击“简单难度”时 //////

View File

@ -90,7 +90,7 @@
margin-top: 8%;
max-width: 100%;
text-align: center;
font: bold 4rem 华文行楷;
font: bold 4rem STXingkai;
}
#startTitle {
@ -147,7 +147,7 @@
#logoLabel {
margin-top: 8%;
font: bold 3rem 华文行楷;
font: bold 3rem STXingkai;
margin-left: auto;
margin-right: auto;
}
@ -169,7 +169,7 @@
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
background: url(images/ground.png) round;
background: url(images/ground.png) repeat;
z-index: 7;
display: none;
}
@ -198,7 +198,7 @@
}
#toolBar {
position: absolute;
background: url(images/ground.png) round;
background: url(images/ground.png) repeat;
z-index: 6;
box-sizing: border-box;
-moz-box-sizing: border-box;

View File

@ -3,14 +3,17 @@
=== 全局 ===
[↑][↓][←][→] 移动勇士
[CTRL] 跳过对话
[Z] 转向
[X] 打开/关闭怪物手册
[G] 打开/关闭楼层传送器
[A] 读取自动存档
[S] 打开/关闭存档页面
[D] 打开/关闭读档页面
[K] 打开/关闭快捷商店选择列表
[T] 打开/关闭工具栏
[ESC] 打开/关闭系统菜单
[H] 打开帮助页面
[R] 回放
[SPACE] 轻按(仅在轻按开关打开时有效)
[1] 快捷使用破墙镐
[2] 快捷使用炸弹/圣锤(先检测有没有炸弹,没有再检测圣锤)

View File

@ -1,4 +1,12 @@
HTML5魔塔样板V1.3
HTML5魔塔样板V1.3.1
增加虚拟键盘。
增加自动存档回退A键可快速读档。
修复几处较为严重的Bug。
-----------------------------------------------------------------------
HTML5魔塔样板V1.3
支持全键盘操作。
支持将某个图片作为某层的背景素材。