Merge pull request #130 from ckcz123/v2.0

V2.0
This commit is contained in:
Zhang Chen 2018-05-25 10:22:21 +08:00 committed by GitHub
commit 2b8af059f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 470 additions and 155 deletions

View File

@ -1000,7 +1000,7 @@ function_s
/* function_s
tooltip : function: 自定义JS脚本\n双击进行多行编辑,常见可能会被用到的系统API参见文档附录
tooltip : 可双击多行编辑,请勿使用异步代码。常见API参见文档附录
helpUrl : https://ckcz123.github.io/mota-js/#/event?id=function%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89js%E8%84%9A%E6%9C%AC
default : ["alert(core.getStatus(\"atk\"));"]
colour : this.dataColor
@ -1385,8 +1385,7 @@ ActionParser.prototype.parseAction = function() {
break;
case "tip":
this.next = MotaActionBlocks['tip_s'].xmlText([
data.data,this.next]);
this.parseAction();
data.text,this.next]);
break;
case "show": // 显示
if (!(data.loc[0] instanceof Array))

View File

@ -415,6 +415,12 @@ data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc =
"_bool": "bool",
"_data": "是否允许等级提升进阶如果上面enableExperience为false则此项恒视为false"
},
"enableKeys": {
"_leaf": true,
"_type": "checkbox",
"_bool": "bool",
"_data": "是否在状态栏显示三色钥匙数量"
},
"enableDebuff": {
"_leaf": true,
"_type": "checkbox",

View File

@ -104,6 +104,7 @@ editor_file = function (editor, callback) {
}
datastr = datastr.concat(['\n}']);
datastr = datastr.join('');
alertWhenCompress();
fs.writeFile(filename, encode(datastr), 'base64', function (err, data) {
callback(err);
});
@ -717,9 +718,17 @@ editor_file = function (editor, callback) {
}))
}
var alertWhenCompress = function(){
if(editor.useCompress===true){
editor.useCompress=null;
setTimeout("alert('当前游戏使用的是压缩文件,修改完成后请重新压缩')",1000)
}
}
var saveSetting = function (file, actionList, callback) {
//console.log(file);
//console.log(actionList);
alertWhenCompress();
if (file == 'icons') {
actionList.forEach(function (value) {

View File

@ -2,37 +2,44 @@
fs = {};
var _isset = function (val) {
if (val == undefined || val == null || (typeof val=='number' && isNaN(val))) {
return false;
}
return true
}
var _http = function (type, url, formData, success, error, mimeType, responseType) {
var xhr = new XMLHttpRequest();
xhr.open(type, url, true);
if (core.isset(mimeType))
if (_isset(mimeType))
xhr.overrideMimeType(mimeType);
if (core.isset(responseType))
if (_isset(responseType))
xhr.responseType = responseType;
xhr.onload = function(e) {
if (xhr.status==200) {
if (core.isset(success)) {
if (_isset(success)) {
success(xhr.response);
}
}
else {
if (core.isset(error))
if (_isset(error))
error("HTTP "+xhr.status);
}
};
xhr.onabort = function () {
if (core.isset(error))
if (_isset(error))
error("Abort");
}
xhr.ontimeout = function() {
if (core.isset(error))
if (_isset(error))
error("Timeout");
}
xhr.onerror = function() {
if (core.isset(error))
if (_isset(error))
error("Error on Connection");
}
if (core.isset(formData))
if (_isset(formData))
xhr.send(formData);
else xhr.send();
}

View File

@ -43,7 +43,8 @@ core.material.items
core.debug()
将攻防设置为10000近似于无敌模式。
开启调试模式。此模式下可以按Ctrl键进行穿墙并忽略一切事件。
此模式下不可回放录像和上传成绩。
core.updateStatusBar()
@ -312,27 +313,21 @@ core.enemys.getExtraDamage(enemyId)
core.enemys.nextCriticals(enemyId, number)
返回接下来number个该怪物的临界。列表每一项类似 "x:y" 表示临界值为x该临界减伤为y。
返回一个列表为接下来number可忽略默认为1个该怪物的临界值和临界减伤。
列表每一项类似 [x,y] 表示临界值为x且临界减伤为y。
如果无临界值,则返回空列表。
core.enemys.getCritical(enemyId)
返回怪物的下一个临界值。无临界(如坚固、魔防等)返回'???'
core.enemys.getDefDamage(enemyId, k)
获得k可忽略默认为1防减伤值
core.enemys.getCriticalDamage(enemyId)
获得怪物的下一个临界减伤。
core.enemys.getDefDamage(enemyId)
获得一防减伤值。
core.enemys.getDamageInfo(enemyId, hero_hp, hero_atk, hero_def, hero_mdef)
core.enemys.getDamageInfo(enemy, hero_hp, hero_atk, hero_def, hero_mdef)
获得实际战斗信息,比如伤害,回合数,每回合伤害等等。
此函数是实际战斗过程的计算。
core.enemys.calDamage(enemyId, hero_hp, hero_atk, hero_def, hero_mdef)
core.enemys.calDamage(enemy, hero_hp, hero_atk, hero_def, hero_mdef)
计算战斗伤害实际返回的是上面getDamageInfo中伤害的数值。
@ -371,6 +366,10 @@ core.events.canUseQuickShop(shopId)
当前能否使用某个快捷商店
core.events.setHeroIcon(name)
设置勇士行走图
========== core.items.XXX 和道具相关的函数 ==========
items.js将处理和道具相关的内容比如道具的使用获取和删除等等。

View File

@ -30,10 +30,32 @@
本塔支持6种门黄蓝红绿铁花。前五种门需要有对应的钥匙打开花门只能通过调用`openDoor`事件进行打开。
本塔支持暗墙,但是暗墙也必须通过`openDoor`事件开启。例如样板2层的小偷事件就是可以打开一个暗墙的。
开门后可触发该层的`afterOpenDoor`事件,有关事件的详细介绍请参见第四章。
## 暗墙
本塔支持暗墙。
要制作一个暗墙非常简单:在该点直接放一个普通墙壁,然后事件写“开门”,坐标为该点就行。
``` js
// 该点画一个普通的墙壁,比如`yellowWall`
// 在该点的事件events中:
"x,y": [
{"type": "openDoor", "loc": [x,y]} // 直接使用开门事件即可。
]
```
系统会自动调用animates中的开暗墙动画。
目前只有如下ID支持以这种方式开门
``` text
yellowDoor, blueDoor, redDoor, greenDoor, specialDoor, steelDoor,
yellowWall, blueWall, whiteWall, lava, star
```
## 怪物
本塔支持的怪物列表参见`project/enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。

View File

@ -1513,7 +1513,7 @@ core.insertAction([
而在我们的存档中是不会对怪物数据进行存储的只会存各个变量和Flag因此我们需要在读档后根据变量或Flag来调整怪物数据。
我们可以在functions.js中的`afterLoadData`进行处理。
我们可以在脚本编辑中的`afterLoadData`进行处理。
``` js
////// 读档事件后,载入事件前,可以执行的操作 //////
@ -1576,7 +1576,7 @@ core.insertAction([
// effect也允许写一个function代表本次升级将会执行的操作比如可以显示一段提示文字或者触发一个事件
{"need": 40, "effect": function () {
core.drawText("恭喜升级!");
core.drawTip("恭喜升级!");
core.status.hero.hp *= 2;
core.status.hero.atk += 100;
core.status.hero.def += 100;

View File

@ -582,7 +582,7 @@ core.statusBar.mana.style.fontStyle = 'normal'; // 这一行会取消斜体。
我们可以在魔力那一行显示当前值和最大值:
``` js
core.setStatus('mana', Math.min(core.getStatus('mana'), core.getStatus('manaMax')); // 如果魔力存在上限,则不能超过其上限值
core.setStatus('mana', Math.min(core.getStatus('mana'), core.getStatus('manaMax'))); // 如果魔力存在上限,则不能超过其上限值
core.statusBar.mana.innerHTML = core.getStatus('mana') + '/' + core.getStatus('manaMax', 0); // 显示比如 6/30 这样
```
@ -676,13 +676,13 @@ if (core.getFlag('skill', 0)==1) { // 开启了技能1
你只需要如下几步来达到多角色的效果。
1. 每个勇士弄一张行走图。相关信息参见[自定义事件setHeroIcon](event#setHeroIcon更改角色行走图)。
1. 每个角色弄一张行走图。相关信息参见[自定义事件setHeroIcon](event#setHeroIcon更改角色行走图)。
2. [覆盖楼传事件](#覆盖楼传事件),这样可以通过点工具栏的楼层传送按钮来切换角色。当然你也完全可以自己写一个道具,或[自定义快捷键](#自定义快捷键)来进行绑定。
3. 在脚本编辑的setInitData中初始化新角色的属性值。
```js
// 所有需要保存的内容;这些保存的内容不会多角色共用,在切换时会进行恢复。
// 你也可以自行新增或删除,比如不共用金币则可以加上"money"的初始化,不共用道具则可以加上"items"的初始化,
// 多勇士共用hp的话则删除hp等等
// 多角色共用hp的话则删除hp等等。总之不共用的属性都在这里进行定义就好
var initData = {
"floorId": "MT0", // 该角色楼层ID
"icon": "hero2.png", // 角色的行走图名称

View File

@ -30,6 +30,8 @@
* “JS代码压缩工具”能对JS代码进行压缩从而减少IO请求数和文件大小。
* “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。
!> **整个造塔过程中,启动服务必须全程处于开启状态!切不可手滑关闭,否则做的都是无用功!**
## 绘制地图
有两种绘制地图的方式从头绘制地图从RMXP中导入已有的地图。
@ -186,7 +188,7 @@ HTML5的塔都是可以进行控制台调试的。
- `core.status.hero` 获得当前勇士状态信息。例如`core.status.hero.atk`就是当前勇士的攻击力数值。
- `core.material.enemys` 获得所有怪物信息。例如`core.material.enemys.greenSlime`就是获得绿色史莱姆的属性数据。
- `core.material.items` 获得所有道具的信息。例如`core.material.items.pickaxe`就是获得破墙镐的信息。
- `core.debug()` 无敌模式使用此命令将会把攻防都置为10000方便进行乱撞
- `core.debug()` 开启调试模式此模式下可以按住Ctrl键进行穿墙
- `core.updateStatusBar()` 立刻更新状态栏和地图显伤。
- `core.setStatus('atk', 1000)` 直接设置勇士的某项属性。本句等价于 `core.status.hero.atk = 1000`
- `core.getStatus('atk')` 返回勇士当前某项属性数值。本句等价于 `core.status.hero.atk`

View File

@ -429,6 +429,8 @@ if (location.protocol.indexOf("http")!=0) {
</script>
<script src='_server/editor.js'></script>
<script>
var useCompress = main.useCompress;
main.useCompress = false;
main.init('editor', function () {
editor.init(function () {
editor.pos = {x: 0, y: 0};
@ -442,6 +444,8 @@ if (location.protocol.indexOf("http")!=0) {
editor.mode.listen();
editor_multi = editor_multi();
editor_blockly = editor_blockly();
editor.useCompress = useCompress;
delete(useCompress);
});
});

View File

@ -82,7 +82,7 @@
<img id="img-up">
<p class='statusLabel' id='up'></p>
</div>
<div class="status">
<div class="status" id='keyCol'>
<span class='statusLabel' id='yellowKey' style="color:#FFCCAA"></span>
<span class='statusLabel' id='blueKey' style="color:#AAAADD"></span>
<span class='statusLabel' id='redKey' style="color:#FF8888"></span>

View File

@ -25,6 +25,7 @@ actions.prototype.onkeyDown = function (e) {
core.status.holdingKeys.push(e.keyCode);
this.pressKey(e.keyCode);
} else {
if (e.keyCode==17) core.status.ctrlDown = true;
this.keyDown(e.keyCode);
}
}
@ -60,6 +61,7 @@ actions.prototype.onkeyUp = function(e) {
}
this.keyUp(e.keyCode);
} else {
if (e.keyCode==17) core.status.ctrlDown = false;
this.keyUp(e.keyCode);
}
}
@ -337,8 +339,14 @@ actions.prototype.keyUp = function(keyCode, fromReplay) {
core.ui.drawHelp();
break;
case 82: // R
if (core.status.heroStop)
core.ui.drawReplay();
if (core.status.heroStop) {
if (core.hasFlag('debug')) {
core.drawText("\t[系统提示]调试模式下无法回放录像");
}
else {
core.ui.drawReplay();
}
}
break;
case 33: case 34: // PAGEUP/PAGEDOWN
if (core.status.heroStop) {
@ -409,7 +417,7 @@ actions.prototype.ondown = function (x ,y) {
core.timeout.onDownTimeout = setTimeout(function () {
if (core.interval.onDownInterval == null) {
core.interval.onDownInterval = setInterval(function () {
if (!core.actions.longClick()) {
if (!core.actions.longClick(x, y, true)) {
clearInterval(core.interval.onDownInterval);
core.interval.onDownInterval = null;
}
@ -480,10 +488,7 @@ actions.prototype.onup = function () {
// 长按
if (!core.status.lockControl && stepPostfix.length==0 && core.status.downTime!=null && new Date()-core.status.downTime>=1000) {
core.waitHeroToStop(function () {
// 绘制快捷键
core.ui.drawKeyBoard();
});
this.longClick(posx, posy);
}
else {
//posx,posy是寻路的目标点,stepPostfix是后续的移动
@ -709,15 +714,23 @@ actions.prototype.onmousewheel = function (direct) {
/////////////////// 在某个界面时的按键点击效果 ///////////////////
////// 长按 //////
actions.prototype.longClick = function () {
actions.prototype.longClick = function (x, y, fromEvent) {
if (!core.isPlaying()) return false;
if (core.status.event.id=='text') {
core.drawText();
return true;
if (core.status.lockControl) {
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=='text') {
core.doAction();
return true;
else if (!fromEvent) {
core.waitHeroToStop(function () {
// 绘制快捷键
core.ui.drawKeyBoard();
});
}
return false;
}
@ -1549,6 +1562,8 @@ actions.prototype.clickSettings = function (x,y) {
});
break;
case 5:
core.ui.drawStatistics();
/*
core.ui.drawWaiting("正在拉取统计信息,请稍后...");
var formData = new FormData();
@ -1596,6 +1611,7 @@ actions.prototype.clickSettings = function (x,y) {
core.drawText("出错啦!\n无法拉取统计信息。\n错误原因XHR Error");
}
xhr.send(formData);
*/
break;
case 6:
core.ui.drawHelp();
@ -1703,6 +1719,10 @@ actions.prototype.clickSyncSave = function (x,y) {
});
break;
case 4:
if (core.hasFlag('debug')) {
core.drawText("\t[系统提示]调试模式下无法下载录像");
break;
}
core.download(core.firstData.name+"_"+core.formatDate2(new Date())+".h5route", JSON.stringify({
'name': core.firstData.name,
'hard': core.status.hard,

View File

@ -59,6 +59,13 @@ control.prototype.setRequestAnimationFrame = function () {
core.animateFrame.moveTime = core.animateFrame.moveTime||timestamp;
core.animateFrame.weather.time = core.animateFrame.weather.time||timestamp;
// move time
if (core.isPlaying() && core.isset(core.status) && core.isset(core.status.hero)
&& core.isset(core.status.hero.statistics)) {
core.status.hero.statistics.totalTime += timestamp-(core.status.hero.statistics.start||timestamp);
core.status.hero.statistics.start=timestamp;
}
// Global Animate
if (core.animateFrame.globalAnimate && core.isPlaying()) {
@ -237,6 +244,12 @@ control.prototype.clearStatus = function() {
////// 重置游戏状态和初始数据 //////
control.prototype.resetStatus = function(hero, hard, floorId, route, maps) {
var totalTime=0;
if (core.isset(core.status) && core.isset(core.status.hero)
&& core.isset(core.status.hero.statistics) && core.isset(route)) {
totalTime=core.status.hero.statistics.totalTime;
}
this.clearStatus();
// 初始化status
@ -249,6 +262,20 @@ control.prototype.resetStatus = function(hero, hard, floorId, route, maps) {
core.material.enemys = core.clone(core.enemys.getEnemys());
// 初始化人物属性
core.status.hero = core.clone(hero);
// 统计数据
if (!core.isset(core.status.hero.statistics))
core.status.hero.statistics = {
'totalTime': totalTime,
'hp': 0,
'battleDamage': 0,
'poisonDamage': 0,
'extraDamage': 0,
'moveDirectly': 0,
'ignoreSteps': 0,
}
core.status.hero.statistics.totalTime = Math.max(core.status.hero.statistics.totalTime, totalTime);
core.status.hero.statistics.start = null;
core.status.hard = hard;
// 初始化路线
if (core.isset(route))
@ -349,12 +376,15 @@ control.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
core.status.automaticRoute.moveDirectly = true;
setTimeout(function () {
if (core.status.automaticRoute.moveDirectly && core.status.heroMoving==0) {
if (core.canMoveDirectly(destX, destY)) {
var ignoreSteps = core.canMoveDirectly(destX, destY);
if (ignoreSteps>0) {
core.clearMap('hero', 0, 0, 416, 416);
core.setHeroLoc('x', destX);
core.setHeroLoc('y', destY);
core.drawHero();
core.status.route.push("move:"+destX+":"+destY);
core.status.hero.statistics.moveDirectly++;
core.status.hero.statistics.ignoreSteps+=ignoreSteps;
}
}
core.status.automaticRoute.moveDirectly = false;
@ -621,6 +651,7 @@ control.prototype.moveAction = function (callback) {
if (core.status.event.id!='ski')
core.status.route.push(direction);
core.status.automaticRoute.moveStepBeforeStop = [];
core.status.automaticRoute.lastDirection = core.getHeroLoc('direction');
if (canMove) // 非箭头:触发
core.trigger(x + scan[direction].x, y + scan[direction].y);
core.drawHero(direction, x, y);
@ -676,7 +707,7 @@ control.prototype.turnHero = function() {
////// 让勇士开始移动 //////
control.prototype.moveHero = function (direction, callback) {
// 如果正在移动直接return
if (core.status.heroMoving>0) return;
if (core.status.heroMoving!=0) return;
if (core.isset(direction))
core.setHeroLoc('direction', direction);
if (!core.isset(callback)) { // 如果不存在回调函数则使用heroMoveTrigger
@ -685,8 +716,18 @@ control.prototype.moveHero = function (direction, callback) {
var doAction = function () {
if (!core.status.heroStop) {
core.moveAction();
setTimeout(doAction, 50);
if (core.hasFlag('debug') && core.status.ctrlDown) {
if (core.status.heroMoving!=0) return;
core.status.heroMoving=-1;
core.eventMoveHero([core.getHeroLoc('direction')], 100, function () {
core.status.heroMoving=0;
doAction();
});
}
else {
core.moveAction();
setTimeout(doAction, 50);
}
}
else {
core.stopHero();
@ -770,6 +811,7 @@ control.prototype.moveOneStep = function() {
core.status.hero.steps++;
// 中毒状态
if (core.hasFlag('poison')) {
core.status.hero.statistics.poisonDamage += core.values.poisonDamage;
core.status.hero.hp -= core.values.poisonDamage;
if (core.status.hero.hp<=0) {
core.status.hero.hp=0;
@ -1013,6 +1055,7 @@ control.prototype.checkBlock = function () {
core.playSound('zone.mp3');
core.drawAnimate("zone", x, y);
}
core.status.hero.statistics.extraDamage += damage;
if (core.status.hero.hp<=0) {
core.status.hero.hp=0;
@ -1320,7 +1363,9 @@ control.prototype.updateFg = function () {
// 临界显伤
if (core.flags.displayCritical) {
var critical = core.formatBigNumber(core.enemys.getCritical(id));
var critical = core.enemys.nextCriticals(id);
if (critical.length>0) critical=critical[0];
critical = core.formatBigNumber(critical[0]);
if (critical == '???') critical = '?';
core.setFillStyle('fg', '#000000');
core.canvas.fg.fillText(critical, 32 * x + 2, 32 * (y + 1) - 2 - 10);
@ -1341,6 +1386,7 @@ control.prototype.updateFg = function () {
for (var y=0;y<13;y++) {
var damage = core.status.checkBlock.damage[13*x+y];
if (damage>0) {
damage = core.formatBigNumber(damage);
core.setFillStyle('fg', '#000000');
core.canvas.fg.fillText(damage, 32 * x + 17, 32 * (y + 1) - 13);
core.canvas.fg.fillText(damage, 32 * x + 15, 32 * (y + 1) - 15);
@ -1371,8 +1417,11 @@ control.prototype.doEffect = function (expression) {
}
}
////// 作弊 //////
////// 开启debug模式 //////
control.prototype.debug = function() {
core.setFlag('debug', true);
core.insertAction(["\t[调试模式开启]此模式下按住Ctrl键可以穿墙并忽略一切事件。\n同时录像将失效也无法上传成绩。"]);
/*
core.setStatus('hp', 999999);
core.setStatus('atk', 10000);
core.setStatus('def', 10000);
@ -1389,6 +1438,7 @@ control.prototype.debug = function() {
core.status.hero.flyRange.push(i);
core.updateStatusBar();
core.drawTip("作弊成功");
*/
}
////// 开始播放 //////
@ -1663,12 +1713,16 @@ control.prototype.replay = function () {
else if (action.indexOf('move:')==0) {
var pos=action.substring(5).split(":");
var x=parseInt(pos[0]), y=parseInt(pos[1]);
if (core.canMoveDirectly(x,y)) {
var ignoreSteps = core.canMoveDirectly(x, y);
if (ignoreSteps>0) {
core.clearMap('hero', 0, 0, 416, 416);
core.setHeroLoc('x', x);
core.setHeroLoc('y', y);
core.drawHero();
core.status.route.push("move:"+x+":"+y);
core.status.hero.statistics.moveDirectly++;
core.status.hero.statistics.ignoreSteps+=ignoreSteps;
core.replay();
return;
}
@ -2365,6 +2419,7 @@ control.prototype.resize = function(clientWidth, clientHeight) {
if (!core.flags.enableExperience) count--;
if (!core.flags.enableLevelUp) count--;
if (!core.flags.enableDebuff) count--;
if (core.isset(core.flags.enableKeys) && !core.flags.enableKeys) count--;
var statusLineHeight = BASE_LINEHEIGHT * 9 / count;
var statusLineFontSize = DEFAULT_FONT_SIZE;
@ -2649,6 +2704,12 @@ control.prototype.resize = function(clientWidth, clientHeight) {
display: core.flags.enableLevelUp ? 'block': 'none'
}
},
{
id: 'keyCol',
rules: {
display: !core.isset(core.flags.enableKeys)||core.flags.enableKeys?'block':'none'
}
},
{
'id': 'debuffCol',
rules: {

View File

@ -107,6 +107,7 @@ function core() {
// 按下键的时间:为了判定双击
'downTime': null,
'ctrlDown': false,
// 路线&回放
'route': [],
@ -888,6 +889,16 @@ core.prototype.debug = function() {
core.control.debug();
}
////// 存档前 //////
core.prototype.beforeSaveData = function (data) {
return core.events.beforeSaveData(data);
}
////// 读档后 //////
core.prototype.afterLoadData = function (data) {
return core.events.afterLoadData(data);
}
////// 重置当前地图 //////
core.prototype.resetMap = function(floorId) {
core.maps.resetMap(floorId);

View File

@ -132,92 +132,63 @@ enemys.prototype.getExtraDamage = function (monster) {
return extra_damage;
}
////// 接下来若干个临界值计算 /////
////// 接下来N个临界值和临界减伤计算 //////
enemys.prototype.nextCriticals = function (monsterId, number) {
number = number||1;
var useTurn = true; // 是否使用回合法计算临界值如果要用循环法则直接改为false。
number = number||1;
var monster = core.material.enemys[monsterId];
// 坚固、模仿怪物没有临界!
if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return [];
var info = this.getDamageInfo(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef);
if (info == null) {
if (core.status.hero.atk<=monster.def) {
return [(monster.def+1-core.status.hero.atk)+":?"];
return [[monster.def+1-core.status.hero.atk,'?']];
}
return [];
}
if (info.damage <= 0) return [];
var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn;
if (turn<=1) return [];
var list = [], pre = null;
var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn;
for (var t = turn-1;t>=1;t--) {
var nextAtk = Math.ceil(mon_hp/t) + mon_def;
if (nextAtk<=hero_atk) break;
if (nextAtk!=pre) {
var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, nextAtk, core.status.hero.def, core.status.hero.mdef);
if (nextInfo==null) break;
list.push((nextAtk-hero_atk)+":"+(info.damage-nextInfo.damage));
if (nextInfo.damage<=0) break;
pre = nextAtk;
if (useTurn) { // 回合数计算法
for (var t = turn-1;t>=1;t--) {
var nextAtk = Math.ceil(mon_hp/t) + mon_def;
if (nextAtk<=hero_atk) break;
if (nextAtk!=pre) {
var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, nextAtk, core.status.hero.def, core.status.hero.mdef);
if (nextInfo==null) break;
list.push([nextAtk-hero_atk,info.damage-nextInfo.damage]);
pre = nextAtk;
}
if (list.length>=number)
break;
}
if (list.length>=number)
break;
}
else { // 暴力for循环法
pre = info.damage;
for (var atk=hero_atk+1;atk<=mon_hp+mon_def;atk++) {
var nextInfo = this.getDamageInfo(monster, core.status.hero.hp, atk, core.status.hero.def, core.status.hero.mdef);
if (nextInfo==null) break;
if (pre>nextInfo.damage) {
pre = nextInfo.damage;
list.push([atk-hero_atk, info.damage-nextInfo.damage]);
if (list.length>=number) break;
}
}
}
if (list.length==0) list.push([0,0]);
return list;
}
////// 临界值计算 //////
enemys.prototype.getCritical = function (monsterId) {
var monster = core.material.enemys[monsterId];
// 坚固、模仿怪物没有临界!
if (this.hasSpecial(monster.special, 3) || this.hasSpecial(monster.special, 10)) return "???";
var info = this.getDamageInfo(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef);
if (info == null) {
if (core.status.hero.atk<=monster.def)
return monster.def+1-core.status.hero.atk;
return '???';
}
if (info.damage <= 0) return 0;
var mon_hp = info.mon_hp, hero_atk = core.status.hero.atk, mon_def = monster.def, turn = info.turn;
// turn 是勇士攻击次数
if (turn<=1) return 0; // 攻杀
// 每回合最小伤害 = ⎡怪物生命/勇士攻击次数⎤
var nextAtk = Math.ceil(mon_hp/(turn-1)) + mon_def;
if (nextAtk <= hero_atk) return 0;
return nextAtk - hero_atk;
}
////// 临界减伤计算 //////
enemys.prototype.getCriticalDamage = function (monsterId) {
var c = this.getCritical(monsterId);
if (c == '???') return '???';
if (c <= 0) return 0;
var monster = core.material.enemys[monsterId];
var last = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef);
var now = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk+c, core.status.hero.def, core.status.hero.mdef);
if (last == null || now==null) return '???';
return last - now;
}
////// 1防减伤计算 //////
enemys.prototype.getDefDamage = function (monsterId) {
////// N防减伤计算 //////
enemys.prototype.getDefDamage = function (monsterId, k) {
k = k || 1;
var monster = core.material.enemys[monsterId];
var nowDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def, core.status.hero.mdef);
var nextDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def + 1, core.status.hero.mdef);
var nextDamage = this.calDamage(monster, core.status.hero.hp, core.status.hero.atk, core.status.hero.def + k, core.status.hero.mdef);
if (nowDamage == null || nextDamage ==null) return "???";
return nowDamage - nextDamage;
}
@ -225,6 +196,10 @@ enemys.prototype.getDefDamage = function (monsterId) {
////// 获得战斗伤害信息 //////
enemys.prototype.getDamageInfo = function(monster, hero_hp, hero_atk, hero_def, hero_mdef) {
if (typeof monster == 'string') {
monster = core.material.enemys[monster];
}
var mon_hp = monster.hp, mon_atk = monster.atk, mon_def = monster.def, mon_special = monster.special;
hero_hp=Math.max(0, hero_hp);
hero_atk=Math.max(0, hero_atk);
@ -310,6 +285,10 @@ enemys.prototype.getDamageInfo = function(monster, hero_hp, hero_atk, hero_def,
////// 具体的伤害计算公式 //////
enemys.prototype.calDamage = function (monster, hero_hp, hero_atk, hero_def, hero_mdef) {
if (typeof monster == 'string') {
monster = core.material.enemys[monsterId];
}
var info = this.getDamageInfo(monster, hero_hp, hero_atk, hero_def, hero_mdef);
if (info == null) return null;
return info.damage;
@ -341,6 +320,9 @@ enemys.prototype.getCurrentEnemys = function (floorId) {
if (specialText.length>=3) specialText = "多属性...";
else specialText = specialText.join(" ");
var critical = this.nextCriticals(monsterId);
if (critical.length>0) critical=critical[0];
enemys.push({
'id': monsterId,
'name': monster.name,
@ -352,8 +334,8 @@ enemys.prototype.getCurrentEnemys = function (floorId) {
'point': monster.point||0, // 加点
'special': specialText,
'damage': this.getDamage(monsterId),
'critical': this.getCritical(monsterId),
'criticalDamage': this.getCriticalDamage(monsterId),
'critical': critical[0],
'criticalDamage': critical[1],
'defDamage': this.getDefDamage(monsterId)
});

View File

@ -228,6 +228,11 @@ events.prototype.gameOver = function (ending, fromReplay) {
core.restart();
});
}
else if (core.hasFlag('debug')) {
core.drawText("\t[系统提示]调试模式下无法上传成绩", function () {
core.restart();
})
}
else {
confirmUpload();
}
@ -638,13 +643,7 @@ events.prototype.doAction = function() {
break;
case "setHeroIcon":
{
var name = "hero.png";
if (core.isset(core.material.images.images[data.name]) && core.material.images.images[data.name].width==128)
name = data.name;
core.setFlag("heroIcon", name);
core.material.images.hero.src = core.material.images.images[name].src;
core.material.icons.hero.height = core.material.images.images[name].height/4;
core.drawHero();
this.setHeroIcon(data.name);
this.doAction();
break;
}
@ -1251,6 +1250,16 @@ events.prototype.canUseQuickShop = function(shopId) {
return null;
}
////// 设置角色行走图 //////
events.prototype.setHeroIcon = function (name) {
if (core.isset(core.material.images.images[name]) && core.material.images.images[name].width==128) {
core.setFlag("heroIcon", name);
core.material.images.hero.src = core.material.images.images[name].src;
core.material.icons.hero.height = core.material.images.images[name].height/4;
core.drawHero();
}
}
////// 检查升级事件 //////
events.prototype.checkLvUp = function () {
if (!core.flags.enableLevelUp || core.status.hero.lv>=core.firstData.levelUp.length) return;
@ -1342,7 +1351,7 @@ events.prototype.passNet = function (data) {
if (data.event.id=='weakNet') { // 衰网
if (core.hasFlag('weak')) return;
core.setFlag('weak', true);
var weakValue = core.status.weakValue;
var weakValue = core.values.weakValue;
var weakAtk = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.atk);
var weakDef = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.def);
core.setFlag('weakAtk', weakAtk);

View File

@ -23,7 +23,9 @@ items.prototype.getItemEffect = function(itemId, itemNum) {
// 消耗品
if (itemCls === 'items') {
var ratio = parseInt(core.floors[core.status.floorId].item_ratio) || 1;
var curr_hp = core.status.hero.hp;
if (itemId in this.itemEffect)eval(this.itemEffect[itemId]);
core.status.hero.statistics.hp += core.status.hero.hp - curr_hp;
}
else {
core.addItem(itemId, itemNum);

View File

@ -255,20 +255,20 @@ maps.prototype.canMoveHero = function(x,y,direction,floorId) {
////// 能否瞬间移动 //////
maps.prototype.canMoveDirectly = function (destX,destY) {
if (!core.flags.enableMoveDirectly) return false;
if (!core.flags.enableMoveDirectly) return -1;
// 中毒状态:不能
if (core.hasFlag('poison')) return false;
if (core.hasFlag('poison')) return -1;
var fromX = core.getHeroLoc('x'), fromY = core.getHeroLoc('y');
if (fromX==destX&&fromY==destY) return false;
if (fromX==destX&&fromY==destY) return -1;
if (core.getBlock(fromX,fromY)!=null||core.status.checkBlock.damage[13*fromX+fromY]>0)
return false;
return -1;
// BFS
var visited=[], queue=[];
visited[13*fromX+fromY]=true;
visited[13*fromX+fromY]=0;
queue.push(13*fromX+fromY);
var directions = [[-1,0],[1,0],[0,1],[0,-1]];
@ -278,12 +278,12 @@ maps.prototype.canMoveDirectly = function (destX,destY) {
for (var dir in directions) {
var nx=nowX+directions[dir][0], ny=nowY+directions[dir][1];
if (nx<0||nx>=13||ny<0||ny>=13||visited[13*nx+ny]||core.getBlock(nx,ny)!=null||core.status.checkBlock.damage[13*nx+ny]>0) continue;
if (nx==destX&&ny==destY) return true;
visited[13*nx+ny]=true;
visited[13*nx+ny]=visited[13*nowX+nowY]+1;
if (nx==destX&&ny==destY) return visited[13*nx+ny];
queue.push(13*nx+ny);
}
}
return false;
return -1;
}
maps.prototype.drawBlock = function (block, animate, dx, dy) {
@ -739,7 +739,7 @@ maps.prototype.removeBlock = function (x, y, floorId) {
// 删除Index
core.removeBlockById(index, floorId);
core.updateFg();
core.updateStatusBar();
}
////// 根据block的索引删除该块 //////

View File

@ -668,8 +668,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;
while (core.status.event.selection<0) core.status.event.selection+=choices.length;
while (core.status.event.selection>=choices.length) core.status.event.selection-=choices.length;
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);
}
@ -1341,7 +1341,11 @@ ui.prototype.drawBookDetail = function (index) {
hints.push("该怪物无特殊属性。");
hints.push("");
hints.push("临界表:"+JSON.stringify(core.enemys.nextCriticals(enemyId,10)))
var criticals = core.enemys.nextCriticals(enemyId, 10).map(function (v) {
return v[0]+":"+v[1];
});
while (criticals[0]=='0:0') criticals.shift();
hints.push("临界表:"+JSON.stringify(criticals))
var content=hints.join("\n");
@ -1745,6 +1749,167 @@ ui.prototype.drawKeyBoard = function () {
core.fillText("ui", "返回游戏", 416-80, offset-3, '#FFFFFF', 'bold 15px Verdana');
}
////// 绘制“数据统计”界面 //////
ui.prototype.drawStatistics = function () {
// 数据统计要统计如下方面:
// 1. 当前全塔剩余下的怪物数量,总金币数,总经验数,总加点数
// 2. 当前全塔剩余的黄蓝红铁门数量,和对应的钥匙数量
// 3. 当前全塔剩余的三种宝石数量,血瓶数量,装备数量;总共增加的攻防生命值
// 4. 当前层的上述信息
// 5. 当前已走的步数;瞬间移动的步数,瞬间移动的次数(和少走的步数);游戏时长
// 6. 当前已恢复的生命值;当前总伤害、战斗伤害、阻激夹域血网伤害、中毒伤害。
var total = {
'monster': {
'count': 0, 'money': 0, 'experience': 0, 'point': 0,
},
'count': {
'yellowDoor': 0, 'blueDoor': 0, 'redDoor': 0, 'steelDoor': 0,
'yellowKey': 0, 'blueKey': 0, 'redKey': 0, 'steelKey': 0,
'redJewel': 0, 'blueJewel': 0, 'greenJewel': 0, 'yellowJewel': 0,
'redPotion': 0, 'bluePotion': 0, 'greenPotion': 0, 'yellowPotion': 0, 'superPotion': 0,
'pickaxe': 0, 'bomb': 0, 'centerFly': 0,
'poisonWine': 0, 'weakWine': 0, 'curseWine': 0, 'superWine': 0,
'sword1': 0, 'sword2': 0, 'sword3': 0, 'sword4': 0, 'sword5': 0,
'shield1': 0, 'shield2': 0, 'shield3': 0, 'shield4': 0, 'shield5': 0,
},
'add': {
'hp': 0, 'atk': 0, 'def': 0, 'mdef': 0
}
};
var current = core.clone(total);
core.floorIds.forEach(function (floorId) {
var floor=core.floors[floorId];
var blocks=core.status.maps[floorId].blocks;
// 隐藏层不给看
if (floor.cannotViewMap && floorId!=core.status.floorId) return;
blocks.forEach(function (block) {
if (!core.isset(block.event) || (core.isset(block.enable) && !block.enable))
return;
var event = block.event;
if (event.cls.indexOf("enemy")==0) {
var enemyId = event.id, enemy = core.material.enemys[enemyId];
total.monster.money+=enemy.money||0;
total.monster.experience+=enemy.experience||0;
total.monster.point+=enemy.point||0;
total.monster.count++;
if (floorId==core.status.floorId) {
current.monster.money+=enemy.money||0;
current.monster.experience+=enemy.experience||0;
current.monster.point+=enemy.point||0;
current.monster.count++;
}
}
else {
var id = event.id;
var temp = core.clone(core.status.hero);
if (core.isset(total.count[id])) {
var hp=0, atk=0, def=0, mdef=0;
if (core.isset(core.material.items[id]) && core.material.items[id].cls=='items' && id!='superPotion') {
var ratio = floor.item_ratio||1;
if (core.isset(core.items.itemEffect[id])) {
eval(core.items.itemEffect[id]);
}
hp = core.status.hero.hp - temp.hp;
atk = core.status.hero.atk - temp.atk;
def = core.status.hero.def - temp.def;
mdef = core.status.hero.mdef - temp.mdef;
}
else {
if (id.indexOf('sword')==0 && core.isset(core.values[id])) {
var x = core.values[id];
if (typeof x == 'number') x = {'atk': x};
atk += x.atk||0;
def += x.def||0;
mdef += x.mdef||0;
}
if (id.indexOf('shield')==0 && core.isset(core.values[id])) {
var x = core.values[id];
if (typeof x == 'number') x = {'def': x};
atk += x.atk||0;
def += x.def||0;
mdef += x.mdef||0;
}
}
core.status.hero = core.clone(temp);
total.count[id]++;
total.add.hp+=hp;
total.add.atk+=atk;
total.add.def+=def;
total.add.mdef+=mdef;
if (floorId==core.status.floorId) {
current.count[id]++;
current.add.hp+=hp;
current.add.atk+=atk;
current.add.def+=def;
current.add.mdef+=mdef;
}
}
}
})
})
var getText = function (type, data) {
var text = type+"地图中:\n";
text += "共有怪物"+data.monster.count+"个";
if (core.flags.enableMoney) text+=",总金币数"+data.monster.money;
if (core.flags.enableExperience) text+=",总经验数"+data.monster.experience;
if (core.flags.enableAddPoint) text+=",总加点数"+data.monster.point;
text+="。\n\n";
Object.keys(data.count).forEach(function (key) {
var value=data.count[key];
if (value>0) {
var name="";
if (key=='yellowDoor') name="黄门";
else if (key=='blueDoor') name="蓝门";
else if (key=='redDoor') name="红门";
else if (key=='steelDoor') name="铁门";
else name=core.material.items[key].name;
if (core.isset(name)) {
text+=name+value+"个;";
}
}
})
text+="\n\n";
text+="共加生命值"+core.formatBigNumber(data.add.hp)+"点,攻击"
+core.formatBigNumber(data.add.atk)+"点,防御"
+core.formatBigNumber(data.add.def)+"点,魔防"
+core.formatBigNumber(data.add.mdef)+"点。";
return text;
}
var formatTime = function (time) {
return core.setTwoDigits(parseInt(time/3600000))
+":"+core.setTwoDigits(parseInt(time/60000)%60)
+":"+core.setTwoDigits(parseInt(time/1000)%60);
}
var statistics = core.status.hero.statistics;
core.drawText([
getText("全塔", total),
getText("当前", current),
"当前总步数:"+core.status.hero.steps+",游戏时长:"+formatTime(statistics.totalTime)
+"。\n瞬间移动次数"+statistics.moveDirectly+",共计少走"+statistics.ignoreSteps+"步。"
+"\n\n总计通过血瓶恢复生命值为"+core.formatBigNumber(statistics.hp)+"点。\n\n"
+"总计受到的伤害为"+core.formatBigNumber(statistics.battleDamage+statistics.poisonDamage+statistics.extraDamage)
+",其中战斗伤害"+core.formatBigNumber(statistics.battleDamage)+"点"
+(core.flags.enableDebuff?(",中毒伤害"+core.formatBigNumber(statistics.poisonDamage)+"点"):"")
+",领域/夹击/阻击/血网伤害"+core.formatBigNumber(statistics.extraDamage)+"点。",
"\t[说明]1. 地图数据统计的效果仅模拟当前立刻获得该道具的效果。\n2. 不会计算“不可被浏览地图”的隐藏层的数据。\n" +
"3. 不会计算任何通过事件得到的道具(显示事件、改变图块、或直接增加道具等)。\n"+
"4. 在自定义道具例如其他宝石需在ui.js的drawStatistics中注册不然不会进行统计。\n"+
"5. 所有统计信息仅供参考,如有错误,概不负责。"
])
}
////// 绘制“关于”界面 //////
ui.prototype.drawAbout = function () {
return this.uidata.drawAbout();

30
main.js
View File

@ -57,6 +57,7 @@ function main() {
'moneyCol': document.getElementById('moneyCol'),
'expCol': document.getElementById('expCol'),
'upCol': document.getElementById('upCol'),
'keyCol': document.getElementById('keyCol'),
'debuffCol': document.getElementById('debuffCol'),
'hard': document.getElementById('hard'),
};
@ -458,8 +459,13 @@ main.dom.playGame.onclick = function () {
main.dom.startButtons.style.display='none';
if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly
&& main.core.musicStatus.bgmStatus && main.core.musicStatus.playingBgm==null)
main.core.playBgm(main.core.bgms[0]);
&& main.core.musicStatus.bgmStatus) {
if (main.core.musicStatus.playingBgm==null
|| core.material.bgms[main.core.musicStatus.playingBgm].paused) {
main.core.musicStatus.playingBgm=null;
main.core.playBgm(main.core.bgms[0]);
}
}
if (main.core.isset(main.core.flags.startDirectly) && main.core.flags.startDirectly) {
core.events.startGame("");
@ -473,8 +479,13 @@ main.dom.playGame.onclick = function () {
main.dom.loadGame.onclick = function() {
if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly
&& main.core.musicStatus.bgmStatus && main.core.musicStatus.playingBgm==null)
main.core.playBgm(main.core.bgms[0]);
&& main.core.musicStatus.bgmStatus) {
if (main.core.musicStatus.playingBgm==null
|| core.material.bgms[main.core.musicStatus.playingBgm].paused) {
main.core.musicStatus.playingBgm=null;
main.core.playBgm(main.core.bgms[0]);
}
}
main.core.load();
}
@ -483,8 +494,13 @@ main.dom.loadGame.onclick = function() {
main.dom.replayGame.onclick = function () {
if (main.core.isset(main.core.musicStatus) && main.core.musicStatus.startDirectly
&& main.core.musicStatus.bgmStatus && main.core.musicStatus.playingBgm==null)
main.core.playBgm(main.core.bgms[0]);
&& main.core.musicStatus.bgmStatus) {
if (main.core.musicStatus.playingBgm==null
|| core.material.bgms[main.core.musicStatus.playingBgm].paused) {
main.core.musicStatus.playingBgm=null;
main.core.playBgm(main.core.bgms[0]);
}
}
core.readFile(function (obj) {
if (obj.name!=core.firstData.name) {
@ -503,9 +519,9 @@ main.dom.replayGame.onclick = function () {
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.setFlag('seed', obj.seed);
core.setFlag('rand', obj.seed);
core.events.setInitData(obj.hard);
core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() {
core.startReplay(core.decodeRoute(obj.route));
}, true);

View File

@ -49,11 +49,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
},
"flyRange": [],
"loc": {"direction": "up", "x": 6, "y": 10},
"flags": {
"poison": false,
"weak": false,
"curse": false,
},
"flags": {},
"steps": 0,
},
"startText": [
@ -144,6 +140,7 @@ data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"enableMoney": true,
"enableExperience": false,
"enableLevelUp": false,
"enableKeys": true,
"enableDebuff": false,
"flyNearStair": true,
"pickaxeFourDirections": false,

View File

@ -119,6 +119,10 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
// 扣减体力值
core.status.hero.hp -= damage;
// 记录
core.status.hero.statistics.battleDamage += damage;
if (core.status.hero.hp<=0) {
core.status.hero.hp=0;
core.updateStatusBar();
@ -155,7 +159,7 @@ functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a =
// 衰弱
if (core.enemys.hasSpecial(special, 13) && !core.hasFlag('weak')) {
core.setFlag('weak', true);
var weakValue = core.status.weakValue;
var weakValue = core.values.weakValue;
var weakAtk = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.atk);
var weakDef = weakValue>=1?weakValue:Math.floor(weakValue*core.status.hero.def);
core.setFlag('weakAtk', weakAtk);

Binary file not shown.

View File

@ -4,15 +4,15 @@
事件while循环处理 √
事件:等待用户操作并获得按键或点击信息 √
衰弱可以减少攻防的比例 √
地图数据统计
地图数据统计
支持 status:x 获得当前坐标 √
core.debug()改成调试模式可以Ctrl穿墙 √
最大存档个数提到main处理 √
新建地图可以保留楼层属性 √
地图编辑器可用PageUp和PageDown切换楼层 √
道具描述过长时可以自动换行 √
除Autotile外均可自动注册 √
重写大部分教程,新增大量拓展描述 √
便捷PS工具偶尔的闪退问题
大量细节进行优化所有已知的bug进行了修复 √
-----------------------------------------------------------------------