Merge remote-tracking branch 'refs/remotes/ckcz123/master'
This commit is contained in:
commit
30f8fccef0
@ -2,4 +2,4 @@
|
||||
Prop3=19,2
|
||||
[InternetShortcut]
|
||||
IDList=
|
||||
URL=http://ckcz123.github.io/mota-js/
|
||||
URL=https://ckcz123.github.io/mota-js/
|
||||
|
||||
52
README.md
52
README.md
@ -1,17 +1,61 @@
|
||||
# H5 魔塔样板
|
||||
# HTML5 魔塔样板
|
||||
|
||||
## 简介
|
||||
|
||||
HTML5 canvas制作的魔塔样板,支持全平台游戏!
|
||||
**即使完全不会编程的用户,按照模板和说明文档也能很快做出一个魔塔游戏!**
|
||||
|
||||
* [Demo / 样板效果](http://ckcz123.com/games/template/)
|
||||
* [Docs / 使用文档说明](http://ckcz123.github.io/mota-js)
|
||||
* [Demo / 样板效果](https://ckcz123.com/games/template/)
|
||||
* [Docs / 使用文档说明](https://ckcz123.github.io/mota-js)
|
||||
|
||||

|
||||
|
||||
## 目录结构
|
||||
|
||||
``` bash
|
||||
├── /_server/ # 为可视化地图编辑器提供一些支持的目录
|
||||
├── /docs/ # 文档目录
|
||||
├── /images/ # 所有图片素材目录
|
||||
│ ├─ /常用素材/ # 可以被直接替换的素材
|
||||
│ └─ *.png # 对应的某个具体的图片素材
|
||||
├── /libs/ # JS源代码目录
|
||||
│ ├─ /floors/ # 剧本文件,记录了每个地图的数据和事件
|
||||
│ ├─ core.js # 系统核心文件
|
||||
│ ├─ data.js # 记录了勇士的初始化信息、各个全局变量和全局Flag值
|
||||
│ ├─ enemys.js # 记录了怪物的信息,包括怪物的数据和特殊属性、伤害计算公式、临界值计算等。
|
||||
│ ├─ events.js # 处理事件的文件,所有自定义事件都会在此文件中进行处理
|
||||
│ ├─ icons.js # 记录了图标信息,将元件的ID和images目录下的素材图标对应起来
|
||||
│ ├─ items.js # 记录了道具的信息,包括道具说明、道具效果等。
|
||||
│ ├─ maps.js # 记录了地图信息,负责将数字与元件的ID一一对应起来。
|
||||
│ └─ ui.js # UI绘制信息,主要负责绘制各个UI窗口。
|
||||
├── /sounds/ # 音效目录
|
||||
├── /常用工具/ # 一些常用工具,可以辅助造塔
|
||||
│ ├─ JS代码压缩工具.exe # 能对Javascript代码进行压缩和整合,从而减少IO请求量。 http://github.com/ckcz123/JSCompressor/
|
||||
│ ├─ 便捷PS工具.exe # 能只用复制和粘贴来快速对素材进行PS操作。 http://github.com/ckcz123/ps/
|
||||
│ ├─ 地图生成器.exe # 能从一张截图识别出来具体的数字数组,方便复刻已有的塔。 http://github.com/ckcz123/map_generator/
|
||||
│ └─ 伤害和临界值计算器.exe # 一个能帮助计算怪物的伤害和临界值的小工具。 http://github.com/ckcz123/magic-tower-calculator/
|
||||
├── drawMapGUI.html # 可视化地图编辑工具,能简单地在界面上绘制地图
|
||||
├── index.html # 主程序,游戏的入口
|
||||
├── main.js # JS程序的入口,将动态对所需JS进行加载
|
||||
├── style.css # 游戏所需要用到的样式表
|
||||
└── 启动服务.exe # 一个本地的HTTP服务器,也能支撑前端的一些POST请求从而能拓展JS的IO功能。 http://github.com/ckcz123/mota-js-server/
|
||||
```
|
||||
|
||||
## 更新说明
|
||||
|
||||
### 2017.12.21
|
||||
|
||||
* [x] 新增:本地HTTP服务器。
|
||||
* [x] 新增:可视化地图编辑工具。
|
||||
* [x] 新增:便捷PS工具。
|
||||
* [x] 移除了meaning.txt,现在“地图生成器”将直接从js文件中读取数字和图块对应关系。
|
||||
* [x] 新增:对Autotile图块的支持。
|
||||
* [x] 新增:怪物支持多种属性;添加仇恨属性。
|
||||
* [x] 移除了不再支持的checkBlock,现在对于领域和夹击无需再手动指定可能的点了。
|
||||
* [x] 新增:单向箭头、感叹号(单次通行)的支持。
|
||||
* [x] 新增:更多的默认素材,现在对于大多数地图风格无需P图,直接替换即可。
|
||||
* [x] 添加部分自定义事件,部分细节优化,一些已知的Bug进行了修复。
|
||||
|
||||
### 2017.12.16
|
||||
|
||||
* [x] 新增:战斗过程显示,可以在设置中关闭
|
||||
@ -24,7 +68,7 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
|
||||
|
||||
### 2017.12.9
|
||||
|
||||
* 发布初版HTML5魔塔样板
|
||||
* [x] 发布初版HTML5魔塔样板
|
||||
|
||||
## 联系我们
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
python _server.py
|
||||
140
_server.py
140
_server.py
@ -1,140 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import socket
|
||||
import threading
|
||||
import os
|
||||
import time
|
||||
|
||||
def httpserver(port = 7945):
|
||||
import http.server
|
||||
http.server.test(http.server.SimpleHTTPRequestHandler,port=port)
|
||||
|
||||
def voidfunc(*a,**k):
|
||||
pass
|
||||
sysecho=print
|
||||
mecho=voidfunc
|
||||
|
||||
homepage='homepage'
|
||||
strtemplate='HTTP/1.0 302 Move temporarily\r\nContent-Length: 0\r\nLocation: {urlstr}\r\n\r\n' #{urlstr}
|
||||
|
||||
def mainget(urlstr):
|
||||
funcAfter=lambda:0
|
||||
if False and urlstr == '/':
|
||||
sysecho(''.join([
|
||||
'GET / ',addr[0],':',str(addr[1])
|
||||
]))
|
||||
return (200,homepage,funcAfter)
|
||||
if True:
|
||||
return (strtemplate.format(urlstr='//127.0.0.1:7945'+urlstr),'',funcAfter)
|
||||
return (404,'404')
|
||||
|
||||
def mainpost(urlstr,body):
|
||||
funcAfter=lambda:0
|
||||
if urlstr == '/':
|
||||
out='name not match'
|
||||
try:
|
||||
op=json.loads(body)
|
||||
name=op['name']
|
||||
op['func']
|
||||
op['args']
|
||||
except Exception as e:
|
||||
return (200,'error format')
|
||||
if name=='readUTF8file' and op['func']=='open':
|
||||
with open('./'+op['args'][0],encoding='utf-8') as fid:
|
||||
out=fid.read()
|
||||
if name=='writeUTF8file' and op['func']=='open':
|
||||
with open('./'+op['args'][0],'w',encoding='utf-8') as fid:
|
||||
out=str(fid.write(op['args'][1]))
|
||||
return (200,out,funcAfter)
|
||||
return (403,'no service this url')
|
||||
|
||||
def mainparse(header,body):
|
||||
funcAfter=lambda:0
|
||||
for _tmp in [1]:
|
||||
if header[:3]=='GET':
|
||||
urlstr=header.split(' ',2)[1]
|
||||
mainre = mainget(urlstr)
|
||||
if len(mainre)==2:
|
||||
header,body=mainre
|
||||
else:
|
||||
header,body,funcAfter=mainre
|
||||
break
|
||||
if header[:4]=='POST':
|
||||
urlstr=header.split(' ',2)[1]
|
||||
mainre = mainpost(urlstr,body)
|
||||
if len(mainre)==2:
|
||||
header,body=mainre
|
||||
else:
|
||||
header,body,funcAfter=mainre
|
||||
break
|
||||
if header=='':
|
||||
header,body= (403,'')
|
||||
break
|
||||
header,body= (403,'')
|
||||
body=body.encode('utf-8')
|
||||
if type(header)==int:
|
||||
codeDict={200:'200 OK',302:'302 Move temporarily',403:'403 Forbidden',404:'404 Not Found'}
|
||||
header=('HTTP/1.0 {statu}\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: '.format(statu=codeDict[header])+str(len(body))+'\r\nAccess-Control-Allow-Origin: *\r\n\r\n')
|
||||
#\r\nAccess-Control-Allow-Origin: * null : to test in chrome
|
||||
header=header.encode('utf-8')
|
||||
return (header,body,funcAfter)
|
||||
|
||||
def tcplink(sock, addr):
|
||||
mecho('\n\nAccept new connection from %s:%s...' % addr)
|
||||
tempbuffer = ['']
|
||||
data=''
|
||||
header=''
|
||||
body=''
|
||||
while True:
|
||||
d = sock.recv(512)
|
||||
if d:
|
||||
d=d.decode('utf-8')
|
||||
tempbuffer.append(d)
|
||||
tempend=tempbuffer[-1][-4:]+d
|
||||
if '\r\n\r\n' in tempend:
|
||||
headend=True
|
||||
data=''.join(tempbuffer)
|
||||
header, body = data.split('\r\n\r\n', 1)
|
||||
if header[:3]=='GET':
|
||||
tempbuffer=[]
|
||||
break
|
||||
tempbuffer=[body]
|
||||
a=int(header.split('Content-Length:',1)[1].split('\r\n',1)[0])-len(body.encode('utf-8'))#str.len not equal byte.len
|
||||
while a>0:
|
||||
tempbuffer.append(sock.recv(min(a,512)).decode('utf-8'))
|
||||
a=a-min(a,512)
|
||||
break
|
||||
else:
|
||||
break
|
||||
mecho('recv end\n===')
|
||||
body = ''.join(tempbuffer)
|
||||
mecho(header)
|
||||
mecho('---')
|
||||
if len(body)>250:
|
||||
mecho(body[:100])
|
||||
mecho('...\n')
|
||||
mecho(body[-100:])
|
||||
else:
|
||||
mecho(body)
|
||||
if True:
|
||||
header,body,funcAfter=mainparse(header,body)
|
||||
mecho('===\nsend start\n')
|
||||
sock.send(header)
|
||||
sock.send(body)
|
||||
mecho('\nsend end\n===')
|
||||
sock.close()
|
||||
mecho('Connection from %s:%s closed.' % addr)
|
||||
funcAfter()
|
||||
|
||||
if __name__ == '__main__':
|
||||
out = threading.Thread(target=httpserver)
|
||||
out.start()
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.bind(('127.0.0.1', 80))
|
||||
s.listen(500)
|
||||
sysecho('Waiting for connection...')
|
||||
os.popen('explorer http://127.0.0.1:7945/drawMapGUI.html')
|
||||
while True:
|
||||
sock, addr = s.accept()
|
||||
t = threading.Thread(target=tcplink, args=(sock, addr))
|
||||
t.start()
|
||||
126
_server/fsTest_cs.html
Normal file
126
_server/fsTest_cs.html
Normal file
@ -0,0 +1,126 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"></head>
|
||||
<body>
|
||||
<script>
|
||||
(function(){
|
||||
fs = {};
|
||||
var postsomething = function (data,_ip,callback) {
|
||||
//callback:function(err, data)
|
||||
//data:字符串
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
switch(xhr.readyState){
|
||||
case 4 :
|
||||
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
|
||||
if (Boolean(callback)){
|
||||
if (xhr.responseText.slice(0,6)=='error:'){
|
||||
callback(xhr.responseText,null);
|
||||
} else {
|
||||
callback(null,xhr.responseText);
|
||||
}
|
||||
}
|
||||
//printf(xhr.responseText)
|
||||
}else{
|
||||
if (Boolean(callback))callback(xhr.status,null);
|
||||
//printf('error:' + xhr.status+'<br>'+(xhr.responseText||''));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
xhr.open('post',_ip);
|
||||
xhr.setRequestHeader('Content-Type','text/plain');
|
||||
if(typeof(data)==typeof([][0]) || data==null)data=JSON.stringify({1:2});
|
||||
xhr.send(data);
|
||||
}
|
||||
|
||||
fs.readFile = function (filename,encoding,callback) {
|
||||
if (typeof(filename)!=typeof(''))
|
||||
throw 'Type Error in fs.readFile';
|
||||
if (encoding=='utf-8'){
|
||||
//读文本文件
|
||||
//filename:支持"/"做分隔符
|
||||
//callback:function(err, data)
|
||||
//data:字符串
|
||||
var data='';
|
||||
data+='type=utf8&';
|
||||
data+='name='+filename;
|
||||
postsomething(data,'/readFile',callback);
|
||||
return;
|
||||
}
|
||||
if (encoding=='base64'){
|
||||
//读二进制文件
|
||||
//filename:支持"/"做分隔符
|
||||
//callback:function(err, data)
|
||||
//data:base64字符串
|
||||
var data='';
|
||||
data+='type=base64&';
|
||||
data+='name='+filename;
|
||||
postsomething(data,'/readFile',callback);
|
||||
return;
|
||||
}
|
||||
throw 'Type Error in fs.readFile';
|
||||
}
|
||||
|
||||
fs.writeFile = function (filename,datastr,encoding,callback) {
|
||||
if (typeof(filename)!=typeof('') || typeof(datastr)!=typeof(''))
|
||||
throw 'Type Error in fs.writeFile';
|
||||
if (encoding=='utf-8'){
|
||||
//写文本文件
|
||||
//filename:支持"/"做分隔符
|
||||
//callback:function(err)
|
||||
//datastr:字符串
|
||||
var data='';
|
||||
data+='type=utf8&';
|
||||
data+='name='+filename;
|
||||
data+='&value='+datastr;
|
||||
postsomething(data,'/writeFile',callback);
|
||||
return;
|
||||
}
|
||||
if (encoding=='base64'){
|
||||
//写二进制文件
|
||||
//filename:支持"/"做分隔符
|
||||
//callback:function(err)
|
||||
//datastr:base64字符串
|
||||
var data='';
|
||||
data+='type=base64&';
|
||||
data+='name='+filename;
|
||||
data+='&value='+datastr;
|
||||
postsomething(data,'/writeFile',callback);
|
||||
return;
|
||||
}
|
||||
throw 'Type Error in fs.writeFile';
|
||||
}
|
||||
|
||||
fs.readdir = function (path, callback) {
|
||||
//callback:function(err, data)
|
||||
//path:支持"/"做分隔符,不以"/"结尾
|
||||
//data:[filename1,filename2,..] filename是字符串,只包含文件不包含目录
|
||||
if (typeof(path)!=typeof(''))
|
||||
throw 'Type Error in fs.readdir';
|
||||
var data='';
|
||||
data+='name='+path;
|
||||
postsomething(data,'/listFile',function(err, data){callback(err,JSON.parse(data))});
|
||||
return;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
fs.writeFile('_test.txt','123中a文bc','utf-8',function(e,d){console.log(e);console.log(d);})
|
||||
setTimeout(function() {
|
||||
fs.writeFile('_test_bin.txt','abc=','base64',function(e,d){console.log(e);console.log(d);})
|
||||
}, 1000);
|
||||
setTimeout(function() {
|
||||
fs.readFile('_test.txt','utf-8',function(e,d){console.log(e);console.log(d);})
|
||||
}, 2000);
|
||||
setTimeout(function() {
|
||||
fs.readFile('_test_bin.txt','base64',function(e,d){console.log(e);console.log(d);})
|
||||
}, 3000);
|
||||
setTimeout(function() {
|
||||
fs.readdir('.',function(e,d){console.log(e);console.log(d);})
|
||||
}, 4000);
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
6
_server/vendor/vue.min.js
vendored
Normal file
6
_server/vendor/vue.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -18,6 +18,8 @@
|
||||
|
||||
如需修改某个道具的效果,在不同区域宝石数据发生变化等问题,请参见[自定义道具效果](personalization#自定义道具效果)的说明。
|
||||
|
||||
**有关轻按,在data.js的系统变量中有定义。如果`enableGentleClick`为true,则鼠标(触摸屏)通过双击勇士,键盘通过空格可达到轻按效果,即不向前移动而获得前方物品。**
|
||||
|
||||
## 门
|
||||
|
||||
本塔支持6种门,黄蓝红绿铁花。前五种门需要有对应的钥匙打开,花门只能通过调用`openDoor`事件进行打开。
|
||||
@ -31,9 +33,41 @@
|
||||
本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。
|
||||
如有自己的怪物素材需求请参见[自定义素材](personalization#自定义素材)的内容。
|
||||
|
||||
怪物可以由特殊属性,每个怪物最多只能有一个特殊属性。怪物的特殊属性所对应的数字(special)在下面的`getSpecialText`中定义,请勿对已有的属性进行修改。
|
||||
怪物可以有特殊属性,每个怪物可以有多个自定义属性。
|
||||
|
||||

|
||||
怪物的特殊属性所对应的数字(special)在下面的`getSpecialText`中定义,请勿对已有的属性进行修改。
|
||||
|
||||
``` js
|
||||
enemys.prototype.getSpecialText = function (enemyId) {
|
||||
if (enemyId == undefined) return "";
|
||||
var special = this.enemys[enemyId].special;
|
||||
var text = [];
|
||||
if (this.hasSpecial(special, 1)) text.push("先攻");
|
||||
if (this.hasSpecial(special, 2)) text.push("魔攻");
|
||||
if (this.hasSpecial(special, 3)) text.push("坚固");
|
||||
if (this.hasSpecial(special, 4)) text.push("2连击");
|
||||
if (this.hasSpecial(special, 5)) text.push("3连击");
|
||||
if (this.hasSpecial(special, 6)) text.push("4连击");
|
||||
if (this.hasSpecial(special, 7)) text.push("破甲");
|
||||
if (this.hasSpecial(special, 8)) text.push("反击");
|
||||
if (this.hasSpecial(special, 9)) text.push("净化");
|
||||
if (this.hasSpecial(special, 10)) text.push("模仿");
|
||||
if (this.hasSpecial(special, 11)) text.push("吸血");
|
||||
if (this.hasSpecial(special, 12)) text.push("中毒");
|
||||
if (this.hasSpecial(special, 13)) text.push("衰弱");
|
||||
if (this.hasSpecial(special, 14)) text.push("诅咒");
|
||||
if (this.hasSpecial(special, 15)) text.push("领域");
|
||||
if (this.hasSpecial(special, 16)) text.push("夹击");
|
||||
if (this.hasSpecial(special, 17)) text.push("仇恨");
|
||||
return text.join(" ");
|
||||
}
|
||||
```
|
||||
|
||||
如果需要双属性,则采用100x+y的写法。例如 103 则视为同时拥有1和3的属性,即先攻且坚固。同理1314为衰弱诅咒双属性。
|
||||
|
||||
如果需要三属性,则采用10000x+100y+z的写法。例如71116视为同时拥有7,11和16的属性,即破甲、吸血、夹击。
|
||||
|
||||
四个乃至更多的属性以此类推。
|
||||
|
||||
怪物的伤害计算在下面的`calDamage`函数中,如有自己需求的伤害计算公式请修改该函数的代码。
|
||||
|
||||
@ -46,6 +80,7 @@
|
||||

|
||||
|
||||
中毒怪让勇士中毒后,每步扣减的生命值由`data.js`中的values定义。
|
||||
|
||||
衰弱怪让勇士衰弱后,攻防会暂时下降一定的数值(直到衰弱状态解除恢复);这个下降的数值同在`data.js`中的values定义。
|
||||
|
||||

|
||||
@ -54,17 +89,11 @@
|
||||
|
||||
领域怪需要在怪物后添加value,代表领域伤害的数值。如果勇士生命值扣减到0,则直接死亡触发lose事件。
|
||||
|
||||

|
||||

|
||||
|
||||
出于游戏性能的考虑,我们不可能每走一步都对领域和夹击进行检查。因此我们需要在本楼层的 checkBlock 中指明哪些点可能会触发领域和夹击事件,在这些点才会对领域和夹击进行检查和处理。
|
||||
请注意如果吸血和领域同时存在,则value会冲突。**因此请勿将吸血和领域放置在同一个怪物身上。**
|
||||
|
||||

|
||||
|
||||
!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。**
|
||||
|
||||
本塔不支持阻击、激光、仇恨、自爆、退化等属性。
|
||||
|
||||
(这些属性基本都太恶心了,恶心到都不想加上去)
|
||||
本塔暂不支持阻击、激光、自爆、退化等属性。
|
||||
|
||||
如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。
|
||||
|
||||
|
||||
@ -215,6 +215,18 @@
|
||||
|
||||

|
||||
|
||||
### tip:显示一段提示文字
|
||||
|
||||
`{"type": "tip"}`可以在左上角显示一段提示文字。
|
||||
|
||||
``` js
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "tip", "text": "这段话将在左上角以气泡形式显示"}
|
||||
]
|
||||
```
|
||||
|
||||
值得注意的是,提示的text内容是可以使用`${ }`来计算表达式的值的。
|
||||
|
||||
### setValue:设置勇士的某个属性、道具个数,或某个变量/Flag的值
|
||||
|
||||
`{"type": "setValue"}` 能修改勇士的某个属性、道具个数、或某个自定义变量或`Flag`的值。
|
||||
@ -245,6 +257,7 @@ value是一个表达式,将通过这个表达式计算出的结果赋值给nam
|
||||
]
|
||||
```
|
||||
|
||||
另外注意一点的是,如果hp被设置成了0或以下,将触发lose事件,直接死亡。
|
||||
|
||||
### show: 将一个禁用事件启用
|
||||
|
||||
@ -392,8 +405,8 @@ revisit常常使用在一些商人之类的地方,当用户购买物品后不
|
||||
|
||||
``` js
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门
|
||||
{"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId
|
||||
{"type": "openDoor", "loc": [3,6], "floorId": "MT1"}, // 打开MT1层的[3,6]位置的门
|
||||
{"type": "openDoor", "loc": [3,6]}, // 如果是本层则可省略floorId
|
||||
]
|
||||
```
|
||||
|
||||
@ -426,6 +439,8 @@ direction为可选的,指定的话将使勇士的朝向变成该方向
|
||||
|
||||
time为可选的,指定的话将作为楼层切换动画的时间。
|
||||
|
||||
!> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。**
|
||||
|
||||
### changePos: 当前位置切换/勇士转向
|
||||
|
||||
有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。
|
||||
@ -454,14 +469,15 @@ time为可选的,指定的话将作为楼层切换动画的时间。
|
||||
|
||||
``` js
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "setFg", "color": [255,255,255], "time": 1000}, // 更改画面色调为纯白,动画时间1000毫秒
|
||||
{"type": "setFg", "color": [0,0,0]}, // 更改画面色调为纯黑,不指定动画时间(使用默认时间)
|
||||
{"type": "setFg", "color": [255,255,255,0.6], "time": 1000}, // 更改画面色调为纯白,不透明度0.6,动画时间1000毫秒
|
||||
{"type": "setFg", "color": [0,0,0]}, // 更改画面色调为纯黑,不透明度1,不指定动画时间(使用默认时间)
|
||||
{"type": "setFg"} // 如果不指定color则恢复原样。
|
||||
]
|
||||
```
|
||||
|
||||
color为需要更改画面色调的颜色。它是一个三元数组,分别指定目标颜色的R,G,B值。
|
||||
- 常见颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,255,0],等等。
|
||||
color为需要更改画面色调的颜色。它是一个数组,分别指定目标颜色的R,G,B,A值。
|
||||
- 常见RGB颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,0,0],等等。
|
||||
- 第四元为Alpha值,即不透明度,为一个0到1之间的数。可以不指定,则默认为Alpha=1
|
||||
|
||||
如果color不指定则恢复原样。
|
||||
|
||||
@ -475,7 +491,7 @@ time为可选的,如果指定,则会作为更改画面色调的时间。
|
||||
|
||||
``` js
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "move", "time": 750, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组
|
||||
{"type": "move", "time": 750, "loc": [x,y], "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),loc为位置可选,steps为移动数组
|
||||
{"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失
|
||||
"down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1
|
||||
], "immediateHide": true }, //immediateHide可选,制定为true则立刻消失,否则渐变消失
|
||||
@ -484,13 +500,15 @@ time为可选的,如果指定,则会作为更改画面色调的时间。
|
||||
|
||||
time选项必须指定,为每移动一步所需要用到的时间。
|
||||
|
||||
loc为需要移动的事件位置。可以省略,如果省略则移动本事件。
|
||||
|
||||
steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。
|
||||
|
||||
如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。
|
||||
|
||||
immediateHide为一个可选项,代表该事件移动完毕后是否立刻消失。如果该项指定了并为true,则移动完毕后直接消失,否则以动画效果消失。
|
||||
|
||||
值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用hide事件。
|
||||
值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用该点的hide事件。
|
||||
|
||||
换句话说,当move事件被调用后,该点本身的事件将被禁用。
|
||||
|
||||
@ -516,6 +534,25 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate
|
||||
|
||||
即,在移动的到达点指定一个初始禁用的相同NPC,然后move事件中指定immediateHide使立刻消失,并show该到达点坐标使其立刻显示(看起来就像没有消失),然后就可以触发目标点的事件了。
|
||||
|
||||
### moveHero:移动勇士
|
||||
|
||||
如果我们需要移动勇士,可以使用`{"type": "moveHero"}`。
|
||||
|
||||
下面是该事件常见的写法:
|
||||
|
||||
``` js
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "moveHero", "time": 750, "steps": [// 动画效果,time为移动速度(比如这里每750ms一步),steps为移动数组
|
||||
{"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失
|
||||
"down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1
|
||||
]},
|
||||
]
|
||||
```
|
||||
|
||||
可以看到,和上面的move事件几乎完全相同,除了不能指定loc,且少了immediateHide选项。
|
||||
|
||||
不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。
|
||||
|
||||
### playSound: 播放音效
|
||||
|
||||
使用playSound可以立刻播放一个音效。
|
||||
@ -524,6 +561,8 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate
|
||||
|
||||
值得注意的是,如果是额外添加进文件的音效,则需在main.js中this.sounds里加载它。
|
||||
|
||||
!> 自定义添加的音效名请勿包含`-`,否则将无法正常使用!
|
||||
|
||||
由于考虑到用户流量问题,每个额外音效最好不超过20KB。
|
||||
|
||||
### win: 获得胜利
|
||||
@ -600,7 +639,7 @@ choices是一个很麻烦的事件,它将弹出一个列表供用户进行选
|
||||
其大致写法如下:
|
||||
|
||||
``` js
|
||||
"x, y": [ // 实际执行的事件列表
|
||||
"x,y": [ // 实际执行的事件列表
|
||||
{"type": "choices", "text": "...", // 提示文字
|
||||
"choices": [
|
||||
{"text": "选项1文字", "action": [
|
||||
|
||||
BIN
docs/img/mapgui.png
Normal file
BIN
docs/img/mapgui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
BIN
docs/img/ps.png
Normal file
BIN
docs/img/ps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/img/server.png
Normal file
BIN
docs/img/server.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@ -6,99 +6,100 @@
|
||||
|
||||
所有素材的图片都在`images`目录下。
|
||||
- `animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。
|
||||
- `enemys.png` 为所有怪物的图片。地图生成器中对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可)
|
||||
- `heros.png`为勇士行走图。
|
||||
- `autotile.png` 为Autotile块。全塔只支持一种Autotile,其ID为20。
|
||||
- `enemys.png` 为所有怪物的图片。其对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可)
|
||||
- `heros.png` 为勇士行走图。
|
||||
- `items.png` 为所有道具的图标。
|
||||
- `npcs.png` 为所有NPC的图标,也是两帧。
|
||||
- `terrains.png` 为所有地形的图标。
|
||||
|
||||
系统会读取`icon.js`文件,并获取每个ID对应的图标所在的位置。
|
||||
|
||||
### 地图生成器使用自定义素材
|
||||
### 使用预定义的素材
|
||||
|
||||
地图生成器可以将数字和图标一一对应,从数字生成图标或从图标生成数字。
|
||||
在images目录的“默认素材”下给定了若干预定义的自定义素材。包括野外(草地),星空,木板等等都已经被预先给定。
|
||||
|
||||
在使用自定义素材后,我们可以使用地图生成器来识别新的素材。打开同目录下的`meaning.txt`,按照已有的方式来增加或编辑内容即可。
|
||||
如果你需要某个素材已经存在,则可以直接将其覆盖images目录下的同名文件,就能看到效果。
|
||||
|
||||
第一列是地图生成器中的数字,第二列是它所在的文件名,第三列是坐标。
|
||||
### 使用便捷PS工具生成素材
|
||||
|
||||

|
||||
如果我们有更多的素材要求,我们可以使用“便捷PS工具”进行处理。
|
||||
|
||||
### 使用自定义地形(路面、墙壁等)
|
||||

|
||||
|
||||
你需要自行P一张图,里面是你需要的地形素材。然后将其覆盖`terrains.png`文件,请注意所有元件的位置需要完全相同对应。
|
||||
我们可以打开有需求改变的素材,和我们需要被替换的素材,然后简单的Ctrl+C和Ctrl+V操作即可。
|
||||
|
||||
岩浆、星空、传送门、箭头、门的开启动画、暗墙的开启动画在`animates.png`中,可能也需要对应进行修改。
|
||||
用这种方式,我们能极快地替换或素材,包括需要新增的怪物。
|
||||
|
||||
请打开`items.js`中,对比素材的位置。
|
||||
### 添加素材到游戏
|
||||
|
||||
另外需要注意的一点是,本样板不支持`Autotile`,换句话说野外地图(草地等)这种自然风可能不会太适合。
|
||||
在使用地图编辑器编辑的过程中,我们有可能会出现“该数字和ID未被定义”的错误提示。
|
||||
|
||||
### 使用自定义道具图标
|
||||
这是因为,该素材没有被定义,无法被游戏所识别。
|
||||
|
||||
如果你觉得已有的道具图标不够,想自己添加图标也是可以的。
|
||||
#### 新添加自定义地形(路面、墙壁等)
|
||||
|
||||
如果`items.png`中不存在你需要的图标,则可以自己P一张图,将你需要的图标覆盖到某个用不到的图标上。或者也可以接着后面向下拉伸。
|
||||
如果你在terrains.png中新增了一行:
|
||||
|
||||
所有道具必须是`32x32`像素。
|
||||
1. 指定一个唯一的英文ID,不能和terrains中现有的重复。
|
||||
2. 进入icons.js,在terrains分类下进行添加(对应图标在图片上的位置,即index)
|
||||
3. 指定一个数字,在maps.js的getBlock下类似进行添加。
|
||||
``` js
|
||||
if (id == 13) tmp.event = {'cls': 'animates', 'id': 'weakNet', 'noPass': false, 'trigger': 'passNet'}; // 衰网
|
||||
if (id == 14) tmp.event = {'cls': 'animates', 'id': 'curseNet', 'noPass': false, 'trigger': 'passNet'}; // 咒网
|
||||
if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
|
||||
// 可以在此处类似添加数字-ID对应关系,但不能和任何已有的数字重复
|
||||
// autotile: 20
|
||||
if (id == 20) tmp.event = {'cls': 'autotile', 'id': 'autotile', 'noPass': true}; // autotile
|
||||
```
|
||||
|
||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
||||
#### 新添加道具
|
||||
|
||||
要在系统中启用你的图标,你需要自己指定一个道具的ID(不能和任何已有的重名),然后进行如下操作:
|
||||
1. 在`items.js`中道具的定义列表中编辑,修改你的自定义道具的名称,类型,说明文字等。
|
||||
- 即捡即用类道具的cls为items,消耗类道具的cls为tools,永久类道具的cls为constants。
|
||||
2. 在`icon.js`中,找到items一栏,往里面添加你的图标位置。
|
||||
3. 在`maps.js`中,找到对应位置,往里面添加自己的数字和道具的一一对应关系。(该数字可任意指定,不能和已有的冲突),类似下面这样
|
||||
如果你需要新增一个未被定义的道具:
|
||||
|
||||
1. 指定一个唯一的英文ID,不能和items中现有的重复。
|
||||
2. 进入icons.js,在items分类下进行添加(对应图标在图片上的位置,即index)
|
||||
3. 指定一个数字,在maps.js的getBlock下类似进行添加。
|
||||
4. 在items.js中仿照其他道具,来添加道具的信息。
|
||||
|
||||
``` js
|
||||
if (id == 63) tmp.event = {'cls': 'items', 'id': 'moneyPocket'} // 金钱袋
|
||||
if (id == 64) tmp.event = {'cls': 'items', 'id': 'shoes'} // 绿鞋
|
||||
if (id == 65) tmp.event = {'cls': 'items', 'id': 'hammer'} // 圣锤
|
||||
// 可以在这里添加自己的数字-道具对应关系
|
||||
```
|
||||
// 可以在这里添加自己的数字-ID对应关系,但不能和任何已有的数字重复
|
||||
```
|
||||
|
||||
有关如何自行实现一个道具的效果,参见[自定义道具效果](#自定义道具效果)。
|
||||
|
||||
### 使用自定义怪物图标
|
||||
#### 新添加怪物
|
||||
|
||||
如果你觉得已有的怪物图标不够,也可以自行向后添加你需要的怪物。
|
||||
如果我们需要新添加怪物,请在enemys.png中新增一行,然后复制粘贴上四帧怪物图的**1和3帧**。
|
||||
|
||||
在`enemys.js`中,直接向后P图,并将你需要的怪物的两帧动画放到正确的位置(注意:普通四帧动画的1和3帧)。
|
||||
然后执行如下操作:
|
||||
|
||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
||||
1. 指定一个唯一的英文ID,不能和enemys中现有的重复。
|
||||
2. 进入icons.js,在enemys分类下进行添加(对应图标在图片上的位置,即index)
|
||||
3. 在maps.js的getBlock下继续进行添加。请注意其ID为200开始的顺序,即如果新增一行为261,依次类推
|
||||
4. 在items.js中仿照其他道具,来添加道具的信息。
|
||||
|
||||
要在系统中启用你的图标,你需要自己指定一个怪物的ID(不能和任何已有的重名),然后进行如下操作:
|
||||
|
||||
1. 在`enemys.js`中,向怪物的定义列表中,添加一个怪物。
|
||||
2. 在`icon.js`中,找到enemys一栏,往里面添加你的图标位置。
|
||||
3. 在`maps.js`中,找到对应位置,往里面添加自己的数字和怪物的一一对应关系。一般对于怪物而言,对应的数字就是`200+`位置。
|
||||
``` js
|
||||
if (id == 258) tmp.event = {'cls': 'enemys', 'id': 'octopus'};
|
||||
if (id == 259) tmp.event = {'cls': 'enemys', 'id': 'fairy'};
|
||||
if (id == 260) tmp.event = {'cls': 'enemys', 'id': 'greenKnight'};
|
||||
// 在此依次添加,数字要求是递增的
|
||||
```
|
||||
|
||||
有关如何自行实现一个怪物的特殊属性或伤害计算公式,参见[怪物的特殊属性](#怪物的特殊属性)。
|
||||
|
||||
### 使用自定义NPC图标
|
||||
#### 新添加NPC
|
||||
|
||||
同上,你也可以自定义NPC图标。只需要将你NPC的两帧动画接到`npcs.js`中即可。
|
||||
类似同上,给NPC指定ID,在icons.js中指定ID-index关系,在maps.js中指定ID-数字的关系,即可。
|
||||
|
||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
||||
### 地图生成器使用自定义素材
|
||||
|
||||
要在系统中启用你的图标,你需要自己指定一个NPC的ID(不能和任何已有的重名),然后进行如下操作:
|
||||
地图生成器是直接从js文件中读取数字-图标对应关系的。
|
||||
|
||||
1. 在`icon.js`中,找到npcs一栏,往里面添加你的图标位置。
|
||||
2. 在`maps.js`中,找到对应位置,往里面添加自己的数字和NPC的一一对应关系。
|
||||
|
||||
### 使用自定义勇士图标
|
||||
|
||||
我们同样可以使用自定义的勇士图标,比如小可绒之类。
|
||||
|
||||
直接拿一个勇士的行走图覆盖`hero.png`即可。
|
||||
|
||||
**支持任何行走大图,如`32x32`, `48x32`, `64x32`等等。**
|
||||
|
||||
|
||||
|
||||
通过上述这几种方式,我们可以修改素材图片(使用自定义素材),指定数字并放入地图生成器中,然后在系统中进行启用。
|
||||
|
||||
!> **请注意:除了勇士行走图外,其他所有素材强制要求必须是`32x32`的,不然可能会造成不可预料的后果。**
|
||||
因此,在你修改了icons.js和maps.js两个文件,也就是将素材添加到游戏后,地图生成器的对应关系也将同步更新。
|
||||
|
||||
## 自定义道具效果
|
||||
|
||||
@ -167,24 +168,26 @@ if (itemId === 'shield5') {
|
||||
``` js
|
||||
enemys.prototype.getExtraDamage = function (monster) {
|
||||
var extra_damage = 0;
|
||||
if (monster.special == 11) { // 吸血
|
||||
if (this.hasSpecial(monster.special, 11)) { // 吸血
|
||||
// 吸血的比例
|
||||
extra_damage = core.status.hero.hp * monster.value;
|
||||
if (core.hasFlag("shield5")) extra_damage = 0; // 如果存在神圣盾,则免疫吸血
|
||||
extra_damage = parseInt(extra_damage);
|
||||
}
|
||||
return extra_damage;
|
||||
}
|
||||
// ... 下略
|
||||
```
|
||||
3. 免疫领域、夹击效果:在`events.js`中,找到checkBlock函数,并编辑成如果有神圣盾标记,则直接返回。
|
||||
3. 免疫领域、夹击效果:在`core.js`中,找到updateCheckBlock函数,并编辑成如果有神圣盾标记,则直接返回。
|
||||
``` js
|
||||
////// 检查领域、夹击事件 //////
|
||||
events.prototype.checkBlock = function (x,y) {
|
||||
if (core.hasFlag("shield5")) return; // 如果拥有神圣盾立刻返回,免疫领域和夹击
|
||||
var damage = 0;
|
||||
// 获得四个方向的怪物
|
||||
var directions = [[0,-1],[-1,0],[0,1],[1,0]]; // 上,左,下,右
|
||||
var enemys = [null,null,null,null];
|
||||
// 更新领域、显伤点
|
||||
core.prototype.updateCheckBlock = function() {
|
||||
if (!core.isset(core.status.thisMap)) return;
|
||||
if (!core.isset(core.status.checkBlockMap)) core.updateCheckBlockMap();
|
||||
core.status.checkBlock = [];
|
||||
if (core.hasItem('shield5')) return; // 如拥有神圣盾则直接返回
|
||||
for (var x=0;x<13;x++) {
|
||||
for (var y=0;y<13;y++) {
|
||||
// 计算(x,y)点伤害
|
||||
var damage = 0;
|
||||
// ... 下略
|
||||
```
|
||||
4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。
|
||||
@ -199,15 +202,28 @@ events.prototype.checkBlock = function (x,y) {
|
||||
|
||||
因此无敌属性可以这样设置:
|
||||
|
||||
``` js
|
||||
// 我们需要对无敌属性指定一个数字,比如18
|
||||
先给无敌属性指定一个数字,例如18,在getSpecialText中定义
|
||||
|
||||
``` js
|
||||
// ... 上略
|
||||
if (this.hasSpecial(special, 15)) text.push("领域");
|
||||
if (this.hasSpecial(special, 16)) text.push("夹击");
|
||||
if (this.hasSpecial(special, 17)) text.push("仇恨");
|
||||
if (this.hasSpecial(special, 18)) text.push("无敌"); // 添加无敌的显示
|
||||
return text.join(" ");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
然后修改calDamage,如果无敌属性且勇士没有拥有十字架,则立刻返回无穷大。
|
||||
|
||||
``` js
|
||||
enemys.prototype.calDamage = function (hero_atk, hero_def, hero_mdef, mon_hp, mon_atk, mon_def, mon_special) {
|
||||
if (mon_special==18 && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架
|
||||
if (this.hasSpecial(mon_special, 18) && !core.hasItem("cross")) // 如果是无敌属性,且勇士未持有十字架
|
||||
return 999999999; // 返回无限大
|
||||
|
||||
// 魔攻
|
||||
if (mon_special == 2) hero_def = 0;
|
||||
if (this.hasSpecial(mon_special, 2)) hero_def = 0;
|
||||
// ... 下略
|
||||
```
|
||||
|
||||
|
||||
@ -6,14 +6,26 @@
|
||||
|
||||
你需要有满足如下条件才能进行制作:
|
||||
|
||||
- Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“地图生成器.exe”即可)
|
||||
- Windows 8以上操作系统;Windows 7需要安装.Net Framework 4.0。(能打开同目录下的“启动服务.exe”即可)
|
||||
- 任一款现代浏览器。强烈推荐Chrome。
|
||||
- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。
|
||||
([VSCode下载地址](https://code.visualstudio.com/),群里的群文件中也有,强烈推荐之。)
|
||||
- RPG Maker XP,任一个魔塔样板(推荐魔塔样板7630)
|
||||
|
||||
只要满足了上述条件,你就可以开始做自己的塔啦!
|
||||
|
||||
## 启动HTTP服务
|
||||
|
||||
在根目录下有一个“启动服务.exe”,运行之。
|
||||
|
||||

|
||||
|
||||
* “启动游戏”按钮将打开一个网页,你能在里面看到现在游戏的效果。
|
||||
* “地图编辑器”允许你以可视化的方式进行编辑地图。
|
||||
* “便捷PS工具”能让你很方便的对自定义素材进行添加。参见[自定义素材](personalization#自定义素材)。
|
||||
* “地图生成器”能让你从已有的截图(如RMXP项目)中立刻生成可被本样板识别的地图数据。
|
||||
* “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。
|
||||
* “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。
|
||||
|
||||
## 新建剧本
|
||||
|
||||
类似于RMXP,本塔每层楼都是一个“剧本”,剧本内主要定义了本层的地图和各种事件。主函数将读取每个剧本,并生成实际的地图供游戏使用。
|
||||
@ -30,43 +42,48 @@
|
||||
|
||||
## 绘制地图
|
||||
|
||||
遗憾的是,我们的样板是没有像RMXP那样有着很好的UI界面,供大家直接进行绘图可视化操作的。然而,我们仍然可以利用已有的RMXP和魔塔样板,绘制好地图,然后利用目录中的“地图生成器”来转成样板所识别的格式。
|
||||
有两种绘制地图的方式:从头绘制地图;从RMXP中导入已有的地图。
|
||||
|
||||
首先,我们打开RMXP和魔塔样板,来到绘制地图页面。
|
||||
### 从头绘制地图
|
||||
|
||||

|
||||
我们直接打开“地图编辑器”,可以看到一个可视化的UI界面。
|
||||
|
||||
然后,任意绘制一张地图。
|
||||

|
||||
|
||||
在这里我们就以1层小塔的地图为例。你也可以任意绘制自己的地图。
|
||||
然后可以在上面任意进行绘制地图。
|
||||
|
||||
!> **如果地图的数字和ID未被定义,则会进行提示:数字和ID未被定义!此时可能需要手动在icons.js和maps.js中定义对应的数字和ID。请参见[自定义素材](personalization#自定义素材)。**
|
||||
|
||||
绘制地图完毕后,点击"导出地图",即可在左边看到对应的JSON数组,并且已经复制到了剪切板。将其粘贴到剧本中的map位置即可。
|
||||
|
||||

|
||||
|
||||
### 从RMXP导入已有的地图
|
||||
|
||||
如果我们想复刻一个现有的,已经被RMXP所制作的塔,也有很便捷的方式,那就是用到我们的“地图生成器”。
|
||||
|
||||
首先,我们打开RMXP和对应的项目,可以看到它的地图。
|
||||
|
||||

|
||||
|
||||
(我把原塔素材改成了经典样式,但是本质上是一样的。)
|
||||
|
||||
请注意,我们无需编辑任何事件。只需要让地图“看起来长成这个样子”就行。
|
||||
|
||||
当绘制好需要的地图后,我们打开Windows自带的“截图工具”,并将整个地图有效区域截图下来,并将其复制到剪切板。
|
||||
我们打开Windows自带的“截图工具”,并将整个地图有效区域截图下来,并将其复制到剪切板。
|
||||
|
||||

|
||||
|
||||
截图时请注意:**只截取有效游戏空间内数据,并且有效空间内的范围必须是13x13。(如果地图小于13*13,请用星空或墙壁填充到13x13)。**
|
||||
|
||||
确认地图的图片文件已经复制到剪切板后,我们打开工具中的“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。
|
||||
确认地图的图片文件已经复制到剪切板后,我们打开“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。
|
||||
|
||||

|
||||
|
||||
如果有识别不一致的存在,即生成的地图和实际的地图不符,则需要在左边的输入框内实际手动修改,然后再点“图片生成”即可。有关每个数字对应的图块名称,请参见images目录下的`meaning.txt`
|
||||
然后点击“复制地图”,即可将地图数据复制到剪切板。
|
||||
|
||||
!> **如果有识别不一致的存在,即生成的地图和实际的地图不符,我们可以在地图编辑器中粘贴,再可视化进行编辑。**
|
||||
|
||||
!> **地图生成器默认只支持经典素材。如果有自定义素材需求(例如原版的1层小塔那种素材),请参见[自定义素材](personalization#自定义素材)。**
|
||||
|
||||
!> **请确保截图范围刚好为13x13,并且保证每个位置的像素都是32x32。**
|
||||
|
||||
经过确认,生成的地图和原始地图保持一致后,点击“复制地图”,然后粘贴到刚刚剧本文件里的maps中。
|
||||
|
||||

|
||||
|
||||
通过这种在RMXP中画图,截图复制,再用地图生成器识别的方式,我们成功将我们需要的地图变成了样板可识别的格式。
|
||||
|
||||
## 录入数据
|
||||
|
||||
|
||||
747
drawMapGUI.html
747
drawMapGUI.html
@ -4,109 +4,342 @@
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
html,body,div,img{margin:0;padding:0;}
|
||||
|
||||
body{
|
||||
font-family: -apple-system,"Helvetica Neue",Helvetica,Arial,"PingFang SC","Hiragino Sans GB","WenQuanYi Micro Hei","Microsoft Yahei",sans-serif;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
.main {
|
||||
max-width: 100%;
|
||||
min-height: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#left, #mid, #right{
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
#left{
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
left: 5px;
|
||||
top: 10px;
|
||||
width: 416px;
|
||||
width: 435px;
|
||||
height: 400px;
|
||||
border: 1px solid rgb(238, 13, 13);
|
||||
}
|
||||
#left #pout{
|
||||
|
||||
#editArea{
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
height: 70%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
/* padding: 10px 5px; */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#pout{
|
||||
display: block;
|
||||
width: 410px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-left: 22px;
|
||||
margin-top: 21px;
|
||||
line-height: 20px;
|
||||
font-size: 13.3333px;
|
||||
font-family: monospace;
|
||||
}
|
||||
#editTip{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
bottom:10px;
|
||||
left: 10px;
|
||||
}
|
||||
#editArea p{
|
||||
margin: 10px;
|
||||
display: block;
|
||||
width: 70%;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
#editTip .btn{
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#mid{
|
||||
position: absolute;
|
||||
left: 448px;
|
||||
top: 0;
|
||||
top: 10px;
|
||||
width: 440px;
|
||||
height: 630px;
|
||||
}
|
||||
.map {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 21px;
|
||||
width: 416px;
|
||||
height: 600px;
|
||||
border: 1px solid rgb(238, 13, 13);
|
||||
height: 416px;
|
||||
}
|
||||
#mid .tools{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
width: 425px;
|
||||
height: 180px;
|
||||
left: 0;
|
||||
top: 448px;
|
||||
bottom: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
padding: 10px 5px;
|
||||
margin-left: 8px;;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.btn{
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
margin: 10px;
|
||||
#tip{
|
||||
float: right;
|
||||
width: 50%;
|
||||
height: 95%;
|
||||
padding: 5px 10px 10px 10px;
|
||||
margin-right: 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
font-size: 15px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.files {
|
||||
width: 50%;
|
||||
height: 120px;
|
||||
/* padding: 10px; */
|
||||
margin-top: 15px;
|
||||
}
|
||||
.files .input{
|
||||
display: block;
|
||||
max-width: 150px;
|
||||
height: 20px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
margin-top: 10px;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
/* background-color: green; */
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||
}
|
||||
.input{
|
||||
width: 100px;
|
||||
margin: 10px;
|
||||
#printOut{
|
||||
margin-top: 10px;
|
||||
height: 20px;
|
||||
|
||||
}
|
||||
.btn {
|
||||
width: 80px;
|
||||
border-radius: 2px;
|
||||
line-height: 30px;
|
||||
margin: 0;
|
||||
min-width: 50px;
|
||||
padding: 0 5px;
|
||||
display: inline-block;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
/* text-transform: uppercase; */
|
||||
letter-spacing: 0;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
border: 0;
|
||||
background: rgba(158,158,158,.2);
|
||||
box-shadow: 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.2), 0 1px 3px 0 rgba(0,0,0,.12);
|
||||
color: #fff;
|
||||
background-color: #26A69A;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: #009688;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
#right{
|
||||
position: absolute;
|
||||
left: 896px;
|
||||
top: 0;
|
||||
width: 450px;
|
||||
height: 600px;
|
||||
border: 1px solid rgb(238, 13, 13);
|
||||
left: 900px;
|
||||
top: 10px;
|
||||
width: 440px;
|
||||
height: 630px;
|
||||
/* border: 1px solid rgb(238, 13, 13); */
|
||||
}
|
||||
#iconLib{
|
||||
position: absolute;
|
||||
width: 435px;
|
||||
height: 620px;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
overflow:auto;
|
||||
}
|
||||
.gameCanvas {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#dataSelection{
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:320px;
|
||||
/* top:0;
|
||||
left:320px; */
|
||||
z-index:75;
|
||||
width:32px;height:32px;
|
||||
|
||||
margin:-2px 0 0 -2px;
|
||||
padding:0;
|
||||
|
||||
/* display: none; */
|
||||
|
||||
background-color:rgba(255, 255, 255, 0.0);
|
||||
border: 2px solid rgb(87, 198, 232);
|
||||
box-shadow: 0px 0px 2px rgb(87, 198, 232);
|
||||
border: 2px solid #30DFF3;
|
||||
box-shadow: 0px 0px 2px #30DFF3;
|
||||
}
|
||||
.warnText{
|
||||
color: #D50000;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
}
|
||||
.infoText{
|
||||
color: #2196F3;
|
||||
}
|
||||
.successText{
|
||||
color: #00897B
|
||||
}
|
||||
|
||||
table, td {
|
||||
border: 1px solid #fff;
|
||||
color: #fff;
|
||||
}
|
||||
table.col{
|
||||
position: relative;
|
||||
|
||||
text-align: center;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.col td{
|
||||
background-color: #4DB6AC;
|
||||
}
|
||||
#arrColMark td{
|
||||
width: 16px;
|
||||
}
|
||||
#arrColMark {
|
||||
top: 2px;
|
||||
left: 36px;
|
||||
width: 385px;
|
||||
height: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
#mapColMark {
|
||||
top: 2px;
|
||||
left: 19px;
|
||||
width: 418px;
|
||||
height: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
#mapColMark td{
|
||||
width: 29px;
|
||||
}
|
||||
#mapColMark td:hover .colBlock{
|
||||
position: absolute;
|
||||
top: 19px;
|
||||
height: 416px;
|
||||
width: 32px;
|
||||
z-index: 100;
|
||||
background-color: rgba(38,166,154,.5);
|
||||
}
|
||||
table.row{
|
||||
position: relative;
|
||||
text-align: right;
|
||||
vertical-align:middle;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.row td{
|
||||
background-color: #A1887F;
|
||||
}
|
||||
#arrRowMark{
|
||||
top: 5px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 262px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#mapRowMark{
|
||||
top: 1px;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 416px;
|
||||
font-size: 12px;
|
||||
}
|
||||
#mapRowMark td{
|
||||
height: 29px;
|
||||
}
|
||||
#mapRowMark td:hover .rowBlock{
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
height: 32px;
|
||||
width: 416px;
|
||||
z-index: 100;
|
||||
background-color: rgba(121,85,72,.5);
|
||||
}
|
||||
/* for vue dom */
|
||||
[v-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div id="left">
|
||||
|
||||
<p id='pout'>可以在console中通过printf(str)来改变这里的值</p>
|
||||
<table class="col" id='arrColMark'></table>
|
||||
<table class="row" id='arrRowMark'></table>
|
||||
<div id="editArea" v-cloak>
|
||||
<textarea cols="10" rows="10" id="pout" v-model="mapArr"></textarea>
|
||||
<p class="warnText" v-if="error">{{ errors[error-1] }}</p>
|
||||
</div>
|
||||
<div id="editTip" v-cloak>
|
||||
<input class='btn' type="button" value="复制地图" v-on:click="copyMap"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="mid">
|
||||
<div class="map">
|
||||
<table class="col" id='mapColMark'></table>
|
||||
<table class="row" id='mapRowMark'></table>
|
||||
<div class="map" id="mapEdit">
|
||||
<canvas class='gameCanvas' id='bg' width='416' height='416' style='z-index:1'></canvas>
|
||||
<canvas class='gameCanvas' id='eventLayer' width='416' height='416' style='z-index:2'></canvas>
|
||||
<canvas class='gameCanvas' id='ui' width='416' height='416' style='z-index:100'></canvas>
|
||||
|
||||
</div>
|
||||
<div class="tools">
|
||||
<input class='btn' type="button" value="exportMap" onclick="exportMap()" style="top:600px;left:50px"/>
|
||||
<input class='btn' type="button" value="read" onclick="readUTF8file(pin.value)" style="top:600px;left:150px"/>
|
||||
<input class='btn' type="button" value="write" onclick="writeUTF8file(pin.value,pout.innerText)" style="top:600px;left:200px"/>
|
||||
<div id="tip" v-cloak >
|
||||
<div v-if="isSelectedBlock" >
|
||||
<p v-if="hasId">图块编号:<span class="infoText">{{ infos['idnum'] }}</span></p>
|
||||
<p v-if="hasId">图块ID:<span class="infoText">{{ infos['id'] }}</span></p>
|
||||
<p v-else class="warnText">该图块无对应的数字或ID存在,请先前往icons.js和maps.js中进行定义!</p>
|
||||
<p>图块所在素材:<span class="infoText">{{ infos['images'] }}</span></p>
|
||||
<p>图块索引:<span class="infoText">{{ infos['y'] }}</span></p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p v-if="whichShow" v-bind:class="[ (whichShow%2) ? 'warnText' : 'successText']">{{ mapMsg }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<input class='btn' id='clear' type="button" value="清除地图" v-on:click="clearMap"/>
|
||||
<input class='btn' type="button" value="导出地图" id="exportM" v-on:click="exportMap"/>
|
||||
|
||||
<input class='input' id='pin' style='top:630px;left:12px;width:200px;height:20px' value="文件名"/>
|
||||
<div class="files">
|
||||
<input class='btn' type="button" value="read" onclick="readUTF8file(pin.value)" />
|
||||
<input class='btn' type="button" value="write" onclick="writeUTF8file(pin.value,pout.innerText)" />
|
||||
|
||||
<input class='input' id='pin' placeholder="请输入文件名"/>
|
||||
<div id="printOut"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="right">
|
||||
<canvas class='gameCanvas' id='data' width='416' height='416' style='z-index:0'></canvas>
|
||||
<div id='dataSelection'></div>
|
||||
<div id="iconLib">
|
||||
<canvas class='gameCanvas' id='data' width='416' height='416' style='z-index:0'></canvas>
|
||||
<div id="selectBox">
|
||||
<div id='dataSelection' v-show="isSelected" v-cloak></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -114,35 +347,55 @@
|
||||
<script>main={'instance':{}}</script>
|
||||
<script src='libs/icons.js'></script>
|
||||
<script src='libs/maps.js'></script>
|
||||
<script src='_server/vendor/vue.min.js'></script>
|
||||
<!-- <script src="https://unpkg.com/vue"></script> -->
|
||||
<script>
|
||||
main.instance.icons.init();//不知道为什么,需要手动init,明明maps是正常的
|
||||
icons=main.instance.icons.getIcons();
|
||||
ids=[];
|
||||
for(var ii=0;ii<=400;ii++)ids[ii]=main.instance.maps.getBlock(0,0,ii);
|
||||
|
||||
var ids = [], indexs = []
|
||||
ids.push({'idnum':0,'id':'ground','images':'terrains','y':0});
|
||||
|
||||
for(var ii=0;ii<=400;ii++){
|
||||
if('event' in ids[ii]){
|
||||
var id =ids[ii].event.id;
|
||||
var cls =ids[ii].event.cls;
|
||||
if(id=='autotile'){ids[ii]={'idnum':ii,'id':'autotile','images':'autotile','y':0};continue;}
|
||||
if (id in icons[cls])ids[ii]={'idnum':ii,'id':id,'images':cls,'y':icons[cls][id]};
|
||||
|
||||
var point = 0
|
||||
for(var i=0; i<400; i++){
|
||||
var indexBlock = main.instance.maps.getBlock(0,0,i);
|
||||
indexs[i] = [];
|
||||
if('event' in indexBlock){
|
||||
var id = indexBlock.event.id;
|
||||
var indexId = indexBlock.id;
|
||||
if(id=='autotile'){
|
||||
ids.push({'idnum':indexId,'id':'autotile','images':'autotile','y':0});
|
||||
point++;
|
||||
indexs[i].push(point);
|
||||
continue;
|
||||
}
|
||||
var allCls = Object.keys(icons);
|
||||
for(var j=0; j<allCls.length; j++){
|
||||
if(id in icons[allCls[j]] ){
|
||||
|
||||
ids.push({'idnum':indexId,'id':id,'images':allCls[j],'y':icons[allCls[j]][id]});
|
||||
point++;
|
||||
indexs[i].push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
else ids[ii]=null;
|
||||
}
|
||||
ids[0]={'idnum':0,'id':'ground','images':'terrains','y':0}
|
||||
|
||||
console.log(icons);
|
||||
console.log(ids);
|
||||
|
||||
// ids
|
||||
// {'idnum':20,'id':'autotile','images':'autotile','y':0}
|
||||
// {'idnum':21,'id':'yellowKey','images':'items','y':0}
|
||||
// {'idnum':22,'id':'blueKey','images':'items','y':1}
|
||||
|
||||
//var cxt = eventLayer.getContext("2d");
|
||||
//cxt.drawImage(core.material.images['spriter_name'], sx*32, sy*32, 32, 32, xx*32, yy*32, 32, 32);
|
||||
// 生成定位编号
|
||||
var colNum = ' ';
|
||||
for(var i=0; i<13; i++){
|
||||
var tpl = '<td>'+i+'<div class="colBlock" style="left:'+(i*32+1)+'px;"></div></td>';
|
||||
colNum += tpl;
|
||||
}
|
||||
arrColMark.innerHTML = '<tr>'+colNum+'</tr>';
|
||||
mapColMark.innerHTML = '<tr>'+colNum+'</tr>';
|
||||
var rowNum = ' ';
|
||||
for(var i=0; i<13; i++){
|
||||
var tpl = '<tr><td>'+i+'<div class="rowBlock" style="top:'+(i*32+1)+'px;"></div></td></tr>';
|
||||
rowNum += tpl;
|
||||
}
|
||||
arrRowMark.innerHTML = rowNum;
|
||||
mapRowMark.innerHTML = rowNum;
|
||||
</script>
|
||||
|
||||
|
||||
@ -299,10 +552,9 @@
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
updateMap = function () {
|
||||
//clearGrass();
|
||||
// console.log(map)
|
||||
for (var xx = 0; xx <= fullX; xx++) {
|
||||
for (var yy = 0; yy <= fullY; yy++) {
|
||||
if (!isGrass(xx, yy)) continue;
|
||||
@ -341,7 +593,15 @@
|
||||
for (var yy = 0; yy <= fullY; yy++) {
|
||||
if (isGrass(xx, yy)) continue;
|
||||
var mapxy=map[m(xx,yy)];
|
||||
if (typeof(mapxy) == typeof(-1) || typeof(mapxy) == typeof([][0]))continue;
|
||||
if (typeof(mapxy) == typeof(-1)){
|
||||
if(mapxy == 0) cxt.clearRect(xx*32, yy*32, 32, 32);
|
||||
continue;
|
||||
}
|
||||
else if(typeof(mapxy) == typeof([][0])) {//未定义块画红块
|
||||
cxt.fillStyle = 'red';
|
||||
cxt.fillRect(xx*32, yy*32, 32, 32);
|
||||
continue;
|
||||
}
|
||||
//context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
|
||||
cxt.clearRect(xx*32, yy*32, 32, 32);
|
||||
cxt.drawImage(core.material.images[mapxy.images], 0, mapxy.y*32, 32, 32, xx*32, yy*32, 32, 32);
|
||||
@ -359,7 +619,7 @@
|
||||
var prefix='<span class="result">',postfix='</span>';
|
||||
if (weak){prefix='<span class="weakresult">';}
|
||||
if (typeof(str)==="undefined")str='';
|
||||
pout.innerHTML=prefix+String(str)+postfix;
|
||||
printOut.innerHTML=prefix+String(str)+postfix;
|
||||
}
|
||||
|
||||
drawInitData = function(){
|
||||
@ -417,8 +677,8 @@
|
||||
|
||||
function eToLoc(e) {
|
||||
var loc = {
|
||||
'x': document.documentElement.scrollLeft+e.clientX - mid.offsetLeft,
|
||||
'y': document.documentElement.scrollTop+e.clientY - mid.offsetTop,
|
||||
'x': document.documentElement.scrollLeft+e.clientX - mid.offsetLeft-mapEdit.offsetLeft,
|
||||
'y': document.documentElement.scrollTop+e.clientY - mid.offsetTop-mapEdit.offsetTop,
|
||||
'size': 32
|
||||
};
|
||||
return loc; }//返回可用的组件内坐标
|
||||
@ -444,6 +704,11 @@
|
||||
}//用于鼠标移出canvas时的自动清除状态
|
||||
|
||||
ui.onmousedown = function (e) {
|
||||
if(!selectBox.isSelected) {
|
||||
tip.whichShow = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
holdingPath = 1;
|
||||
mouseOutCheck = 2;
|
||||
setTimeout(clear1);
|
||||
@ -457,6 +722,10 @@
|
||||
}
|
||||
|
||||
ui.onmousemove = function (e) {
|
||||
if(!selectBox.isSelected) {
|
||||
// tip.whichShow = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (holdingPath == 0) { return; }
|
||||
mouseOutCheck = 2;
|
||||
@ -482,48 +751,89 @@
|
||||
}
|
||||
|
||||
ui.onmouseup = function (e) {
|
||||
if(!selectBox.isSelected) {
|
||||
tip.whichShow = 1;
|
||||
return;
|
||||
}
|
||||
holdingPath = 0;
|
||||
e.stopPropagation();
|
||||
var loc = eToLoc(e);
|
||||
if (stepPostfix.length) {
|
||||
console.log(stepPostfix);
|
||||
preMapData = JSON.parse(JSON.stringify(map));
|
||||
currDrawData.pos = JSON.parse(JSON.stringify(stepPostfix));
|
||||
currDrawData.info = JSON.parse(JSON.stringify(info));
|
||||
reDo = null;
|
||||
// console.log(stepPostfix);
|
||||
for (var ii = 0; ii < stepPostfix.length; ii++)
|
||||
map[m(stepPostfix[ii].x, stepPostfix[ii].y)] = info;
|
||||
map[fullX + 1 + fullY * (fullX + 1)] = -2;
|
||||
console.log(map);
|
||||
// console.log(map);
|
||||
updateMap();
|
||||
holdingPath = 0;
|
||||
stepPostfix = [];
|
||||
uc.clearRect(0, 0, 416, 416);
|
||||
}
|
||||
}
|
||||
|
||||
var preMapData = {};
|
||||
var currDrawData = {
|
||||
pos: [],
|
||||
info: {}
|
||||
};
|
||||
var reDo = null;
|
||||
document.body.onkeydown = function(e) {
|
||||
// 禁止快捷键的默认行为
|
||||
if( e.ctrlKey && ( e.keyCode == 90 || e.keyCode == 89 ) )
|
||||
e.preventDefault();
|
||||
//Ctrl+z 撤销上一步undo
|
||||
if(e.keyCode == 90 && e.ctrlKey && preMapData && currDrawData.pos.length){
|
||||
map = JSON.parse(JSON.stringify(preMapData));
|
||||
updateMap();
|
||||
reDo = JSON.parse(JSON.stringify(currDrawData));
|
||||
currDrawData = {pos: [],info: {}};
|
||||
preMapData = null;
|
||||
}
|
||||
//Ctrl+y 重做一步redo
|
||||
if(e.keyCode == 89 && e.ctrlKey && reDo && reDo.pos.length){
|
||||
preMapData = JSON.parse(JSON.stringify(map));
|
||||
for(var j=0; j<reDo.pos.length;j++)
|
||||
map[m(reDo.pos[j].x, reDo.pos[j].y)] = JSON.parse(JSON.stringify(reDo.info));
|
||||
|
||||
info=ids[20];//autotile
|
||||
map[fullX + 1 + fullY * (fullX + 1)] = -2;
|
||||
updateMap();
|
||||
currDrawData = JSON.parse(JSON.stringify(reDo));
|
||||
reDo = null;
|
||||
}
|
||||
}
|
||||
|
||||
// info=ids[indexs[20][0]];//autotile
|
||||
data.onmousedown = function (e) {
|
||||
e.stopPropagation();
|
||||
var loc = {
|
||||
'x': document.documentElement.scrollLeft + e.clientX - right.offsetLeft,
|
||||
'y': document.documentElement.scrollTop + right.scrollTop+e.clientY - right.offsetTop,
|
||||
'x': document.documentElement.scrollLeft + e.clientX - right.offsetLeft-iconLib.offsetLeft,
|
||||
'y': document.documentElement.scrollTop + e.clientY + iconLib.scrollTop - right.offsetTop-iconLib.offsetTop,
|
||||
'size': 32
|
||||
};
|
||||
pos = locToPos(loc);
|
||||
console.log(pos);
|
||||
for (var spriter in widthsX){
|
||||
if(pos.x>=widthsX[spriter][1] && pos.x<widthsX[spriter][2]){
|
||||
pos.x=widthsX[spriter][1];
|
||||
pos.images=widthsX[spriter][0];
|
||||
pos.x=widthsX[spriter][1];
|
||||
pos.images=widthsX[spriter][0];
|
||||
if(pos.images=='autotile'){
|
||||
pos.y=0;
|
||||
}else if((pos.y+1)*32>core.material.images[pos.images].height)pos.y=~~(core.material.images[pos.images].height/32)-1;
|
||||
pos.y=0;
|
||||
}else if((pos.y+1)*32>core.material.images[pos.images].height)
|
||||
pos.y=~~(core.material.images[pos.images].height/32)-1;
|
||||
|
||||
dataSelection.style.left=pos.x*32+'px';
|
||||
dataSelection.style.top=pos.y*32+'px';
|
||||
selectBox.isSelected = true;
|
||||
// console.log(pos,core.material.images[pos.images].height)
|
||||
dataSelection.style.left = pos.x*32 +'px';
|
||||
dataSelection.style.top = pos.y*32 +'px';
|
||||
info={'images':pos.images,'y':pos.y};
|
||||
for (var ii=0;ii<ids.length;ii++){
|
||||
if(ids[ii]==null)continue;
|
||||
if(pos.images==ids[ii].images && pos.y==ids[ii].y)info=ids[ii];
|
||||
if(pos.images==ids[ii].images && pos.y==ids[ii].y)
|
||||
info = ids[ii];
|
||||
}
|
||||
printf(JSON.stringify(info))
|
||||
tip.infos = JSON.parse(JSON.stringify(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -592,26 +902,251 @@
|
||||
postsomething(JSON.stringify(data),callback);
|
||||
}
|
||||
|
||||
function exportMap(callback){
|
||||
var filestr='';
|
||||
for (var yy = 0; yy < 13; yy++){
|
||||
filestr+='['
|
||||
for (var xx = 0; xx < 13; xx++) {
|
||||
var mapxy=map[m(xx,yy)];
|
||||
if(typeof(mapxy)==typeof({})){
|
||||
if ('idnum' in mapxy)mapxy=mapxy.idnum;
|
||||
else mapxy='!!?';
|
||||
}
|
||||
mapxy=String(mapxy);
|
||||
mapxy=Array(Math.max(4-mapxy.length,0)).join(' ')+mapxy;
|
||||
filestr+=mapxy+','
|
||||
}
|
||||
filestr+='],\n'
|
||||
}
|
||||
printf('<pre>'+filestr+'\n</pre><p>已复制到剪贴板</p>'+'<textarea cols="1" rows="1" id="poutTmp" style="opacity: 0;">'+filestr+'\n</textarea><br><br><br>');
|
||||
poutTmp.select();
|
||||
document.execCommand("Copy");
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
document.body.onmousedown = function(e){
|
||||
selectBox.isSelected = false;
|
||||
info = {};
|
||||
canSelectAg = false;
|
||||
|
||||
}
|
||||
|
||||
var exportM = new Vue({
|
||||
el: '#exportM',
|
||||
|
||||
methods: {
|
||||
exportMap: function(){
|
||||
if(editArea.error) {
|
||||
tip.whichShow = 3;
|
||||
return;
|
||||
}
|
||||
var filestr='';
|
||||
for (var yy = 0; yy < 13; yy++){
|
||||
filestr+='['
|
||||
for (var xx = 0; xx < 13; xx++) {
|
||||
var mapxy=map[m(xx,yy)];
|
||||
if(typeof(mapxy)==typeof({})){
|
||||
if ('idnum' in mapxy)mapxy=mapxy.idnum;
|
||||
else {
|
||||
// mapxy='!!?';
|
||||
tip.whichShow = 3;
|
||||
return;
|
||||
}
|
||||
}else if(typeof(mapxy)=='undefined'){
|
||||
tip.whichShow = 3;
|
||||
return;
|
||||
}
|
||||
mapxy=String(mapxy);
|
||||
mapxy=Array(Math.max(4-mapxy.length,0)).join(' ')+mapxy;
|
||||
filestr+=mapxy+(xx==12?'':',')
|
||||
}
|
||||
|
||||
filestr += ']'+(yy==12?'':',\n');
|
||||
}
|
||||
pout.value = filestr;
|
||||
|
||||
tip.whichShow = 2;
|
||||
}
|
||||
}
|
||||
})
|
||||
var editArea = new Vue({
|
||||
el: '#editArea',
|
||||
data: {
|
||||
mapArr: '',
|
||||
errors: [ // 编号1,2,3,4
|
||||
"格式错误!请使用正确格式(13*13数组,如不清楚,可先点击生成地图查看正确格式)",
|
||||
"当前有未定义ID(在地图区域显示红块),请修改ID或者到icons.js和maps.js中进行定义!",
|
||||
"ID越界(在地图区域显示红块),当前编辑器暂时支持编号小于400,请修改编号!",
|
||||
// "发生错误!",
|
||||
],
|
||||
error: 0,
|
||||
formatTimer: null,
|
||||
},
|
||||
watch: {
|
||||
mapArr: function (val, oldval) {
|
||||
var that = this;
|
||||
if(val=='') return;
|
||||
if(that.formatArr()){
|
||||
that.error = 0;
|
||||
clearTimeout(that.formatTimer);
|
||||
setTimeout(function(){
|
||||
that.drawMap();
|
||||
tip.whichShow = 8
|
||||
}, 1000);
|
||||
that.formatTimer = setTimeout(function(){
|
||||
pout.value = that.formatArr();
|
||||
}, 5000); //5s后再格式化,不然光标跳到最后很烦
|
||||
}else{
|
||||
that.error = 1;
|
||||
}
|
||||
},
|
||||
error: function(){
|
||||
console.log(editArea.mapArr);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
drawMap: function(){
|
||||
var that = this;
|
||||
|
||||
var mapArray = that.mapArr.split(/\D+/).join(' ').trim().split(' ')
|
||||
for(var i=0; i<mapArray.length; i++){
|
||||
var num = parseInt(mapArray[i]);
|
||||
if(num == 0 )
|
||||
map[i] = 0;
|
||||
else if(num >= 400){
|
||||
that.error = 3;
|
||||
map[i] = undefined;
|
||||
}else if(indexs[num][0] == undefined){
|
||||
that.error = 2;
|
||||
map[i] = undefined;
|
||||
}else map[i] = ids[[indexs[num][0]]];
|
||||
}
|
||||
|
||||
updateMap();
|
||||
|
||||
},
|
||||
formatArr: function(){
|
||||
var formatArrStr = '';
|
||||
|
||||
if(this.mapArr.split(/\D+/).join(' ').trim().split(' ').length != 169) return false;
|
||||
var arr = this.mapArr.replace(/\s+/g, '').split('],[');
|
||||
|
||||
if(arr.length != 13) return ;
|
||||
for(var i =0; i<13; i++){
|
||||
var a = [];
|
||||
formatArrStr +='[';
|
||||
if(i==0||i==12) a = arr[i].split(/\D+/).join(' ').trim().split(' ');
|
||||
else a = arr[i].split(/\D+/);
|
||||
if(a.length != 13){
|
||||
formatArrStr = '';
|
||||
return ;
|
||||
}
|
||||
|
||||
for(var k=0; k<13; k++){
|
||||
var num = parseInt(a[k]);
|
||||
formatArrStr += Array(Math.max(4-String(num).length,0)).join(' ')+num+(k==12?'':',');
|
||||
}
|
||||
formatArrStr += ']'+(i==12?'':',\n');
|
||||
}
|
||||
|
||||
return formatArrStr;
|
||||
}
|
||||
}
|
||||
});
|
||||
var editTip = new Vue({
|
||||
el: '#editTip',
|
||||
data: {
|
||||
err: ''
|
||||
},
|
||||
methods: {
|
||||
copyMap: function(){
|
||||
|
||||
tip.whichShow = 0;
|
||||
if(pout.value.trim() != ''){
|
||||
if(editArea.error) {
|
||||
this.err = editArea.errors[editArea.error-1];
|
||||
tip.whichShow = 5
|
||||
return;
|
||||
}
|
||||
try{
|
||||
pout.select();
|
||||
document.execCommand("Copy");
|
||||
tip.whichShow = 6;
|
||||
}catch(e){
|
||||
this.err= e;
|
||||
tip.whichShow = 5;
|
||||
}
|
||||
}else{
|
||||
tip.whichShow = 7;
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
var clear = new Vue({
|
||||
el: '#clear',
|
||||
|
||||
methods: {
|
||||
clearMap: function(){
|
||||
map[fullX + 1 + fullY * (fullX + 1)] = -2;
|
||||
for(var ii=0;ii<fullX + 1 + fullY * (fullX + 1);ii++)map[ii]=0;
|
||||
ec = eventLayer.getContext('2d');
|
||||
ec.clearRect(0, 0, 416, 416);
|
||||
clearTimeout(editArea.formatTimer);
|
||||
clearTimeout(tip.timer);
|
||||
pout.value = '';
|
||||
editArea.mapArr = '';
|
||||
tip.whichShow = 4;
|
||||
editArea.error = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
var tip = new Vue({
|
||||
el: '#tip',
|
||||
data: {
|
||||
infos: ids[indexs[20][0]],
|
||||
hasId: true,
|
||||
isSelectedBlock: false,
|
||||
geneMapSuccess: false,
|
||||
timer: null,
|
||||
msgs: [ //分别编号1,2,3,4,5,6,7,8;奇数警告,偶数成功
|
||||
"当前未选择任何图块,请先在右边选择要画的图块!",
|
||||
"生成地图成功!可点击复制按钮复制地图数组到剪切板",
|
||||
"生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!",
|
||||
"地图清除成功!",
|
||||
"复制失败!",
|
||||
"复制成功!可直接粘贴到楼层文件的地图数组中。",
|
||||
"复制失败!当前还没有数据",
|
||||
"修改成功!可点击复制按钮复制地图数组到剪切板"
|
||||
],
|
||||
mapMsg: '',
|
||||
whichShow: 0,
|
||||
},
|
||||
watch: {
|
||||
infos: {
|
||||
handler: function(val, oldval){
|
||||
if(typeof(val) != 'undefined'){
|
||||
this.infos = val;
|
||||
if('id' in val){
|
||||
this.hasId = true;
|
||||
}else{
|
||||
this.hasId = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
|
||||
whichShow: function(){
|
||||
var that = this;
|
||||
that.mapMsg = '';
|
||||
that.msgs[4] = "复制失败!"+editTip.err;
|
||||
clearTimeout(that.timer);
|
||||
if(that.whichShow){
|
||||
that.mapMsg = that.msgs[that.whichShow-1];
|
||||
that.timer = setTimeout(function() {
|
||||
if(!(that.whichShow%2))
|
||||
that.whichShow = 0;
|
||||
}, 5000); //5秒后自动清除success,warn不清除
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var selectBox = new Vue({
|
||||
el: '#selectBox',
|
||||
data: {
|
||||
isSelected: false
|
||||
},
|
||||
watch: {
|
||||
isSelected: function(){
|
||||
tip.isSelectedBlock = this.isSelected;
|
||||
tip.whichShow = 0;
|
||||
clearTimeout(tip.timer);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,116 +0,0 @@
|
||||
# 此文件是每个数字所代表的意思,可被地图生成器读取和使用
|
||||
# 可以模仿此格式在后面写上任意被识别内容
|
||||
|
||||
|
||||
# 文件格式:
|
||||
# 编号,图标所在的图片名,图标在图片上的索引(从上到下第几个,从0开始计算)
|
||||
# 注意中间以小逗号分开;不要有空格;后面可以加任意"#"代表注释
|
||||
|
||||
### 0-20 地形 ###
|
||||
0,terrains,0 # 路面;此项必须在第一条。
|
||||
1,terrains,1 # 黄色墙(经典墙)
|
||||
2,terrains,2 # 白色墙
|
||||
3,terrains,3 # 蓝色墙
|
||||
4,terrains,4 # 星空
|
||||
5,terrains,5 # 岩浆
|
||||
6,terrains,6 # 冰面
|
||||
7,terrains,15 # 蓝色商店左
|
||||
8,terrains,16 # 蓝色商店右
|
||||
9,terrains,17 # 红色商店左
|
||||
10,terrains,18 # 红色商店右
|
||||
11,animates,23 # 血网(经过受到伤害)
|
||||
12,animates,24 # 毒网(经过中毒)
|
||||
13,animates,25 # 衰网(经过衰弱)
|
||||
14,animates,26 # 咒网(经过诅咒)
|
||||
15,animates,31 # 水
|
||||
# 可自行往后添加
|
||||
20,autotile,0 # Autotile
|
||||
|
||||
### 21-80 物品 ###
|
||||
# 消耗品
|
||||
21,items,0 # 黄钥匙
|
||||
22,items,1 # 蓝钥匙
|
||||
23,items,2 # 红钥匙
|
||||
24,items,3 # 绿钥匙
|
||||
25,items,4 # 铁门钥匙
|
||||
26,items,6 # 大黄门钥匙(钥匙盒)
|
||||
27,items,16 # 红宝石
|
||||
28,items,17 # 蓝宝石
|
||||
29,items,18 # 绿宝石
|
||||
30,items,19 # 黄宝石
|
||||
31,items,20 # 红血瓶
|
||||
32,items,21 # 蓝血瓶
|
||||
33,items,22 # 绿血瓶
|
||||
34,items,23 # 黄血瓶
|
||||
35,items,50 # 铁剑
|
||||
36,items,55 # 铁盾
|
||||
37,items,51 # 银剑
|
||||
38,items,56 # 银盾
|
||||
39,items,52 # 骑士剑
|
||||
40,items,57 # 骑士盾
|
||||
41,items,53 # 圣剑
|
||||
42,items,58 # 圣盾
|
||||
43,items,54 # 神圣剑
|
||||
44,items,59 # 神圣盾
|
||||
# 特殊道具
|
||||
45,items,9 # 怪物手册
|
||||
46,items,12 # 楼层传送器
|
||||
47,items,45 # 破墙镐
|
||||
48,items,44 # 破冰镐
|
||||
49,items,43 # 炸弹
|
||||
50,items,13 # 中心对称飞行器
|
||||
51,items,15 # 上楼器
|
||||
52,items,14 # 下楼器
|
||||
53,items,11 # 幸运金币
|
||||
54,items,41 # 冰冻徽章
|
||||
55,items,40 # 十字架
|
||||
56,items,29 # 圣水
|
||||
57,items,8 # 地震卷轴
|
||||
58,items,24 # 解毒药水
|
||||
59,items,25 # 解衰药水
|
||||
60,items,27 # 解咒药水
|
||||
61,items,28 # 万能药水
|
||||
62,items,42 # 屠龙匕首
|
||||
63,items,46 # 金钱袋
|
||||
64,items,47 # 绿鞋
|
||||
65,items,48 # 圣锤
|
||||
# 可自行往后添加
|
||||
|
||||
### 81-120 门、楼梯、传送门 ###
|
||||
81,terrains,9 # 黄门
|
||||
82,terrains,10 # 蓝门
|
||||
83,terrains,11 # 红门
|
||||
84,terrains,12 # 绿门
|
||||
85,terrains,13 # 机关门
|
||||
86,terrains,14 # 铁门
|
||||
87,terrains,8 # 上楼梯
|
||||
88,terrains,7 # 下楼梯
|
||||
89,animates,21 # 传送门
|
||||
90,animates,19 # 星空传送
|
||||
91,animates,30 # 上箭头传送
|
||||
92,animates,28 # 左箭头传送
|
||||
93,animates,27 # 下箭头传送
|
||||
94,animates,29 # 右箭头传送
|
||||
|
||||
### 121-150 NPC ###
|
||||
121,npcs,0 # 经典老人
|
||||
122,npcs,1 # 经典商人
|
||||
123,npcs,2 # 小偷
|
||||
124,npcs,3 # 仙子
|
||||
125,npcs,4 # 神秘男老人
|
||||
126,npcs,5 # 神秘女老人
|
||||
127,npcs,6 # 老头
|
||||
128,npcs,7 # 小孩
|
||||
129,npcs,8 # 木牌
|
||||
130,npcs,9 # 经验商店
|
||||
131,npcs,10 # 金币商店
|
||||
132,npcs,11 # 公主
|
||||
# 可以添加更多的NPC图标
|
||||
|
||||
|
||||
# 在此可以继续添加更多的事件,比如单向箭头、感叹号开关、箱子等等
|
||||
|
||||
|
||||
### 201-300 怪物 ###
|
||||
# 将会按照enemys.png顺序依次读取怪物
|
||||
# 如201绿色史莱姆,202红色史莱姆,依次类推
|
||||
313
libs/core.js
313
libs/core.js
@ -21,7 +21,8 @@ function core() {
|
||||
'events': {}
|
||||
}
|
||||
this.timeout = {
|
||||
'getItemTipTimeout': null
|
||||
'getItemTipTimeout': null,
|
||||
'turnHeroTimeout': null,
|
||||
}
|
||||
this.interval = {
|
||||
'twoAnimate': null,
|
||||
@ -56,6 +57,7 @@ function core() {
|
||||
'floorId': null,
|
||||
'thisMap': null,
|
||||
'maps': null,
|
||||
'checkBlock': [], // 显伤伤害
|
||||
|
||||
// 勇士状态;自动寻路相关
|
||||
'heroMoving': false,
|
||||
@ -106,7 +108,6 @@ core.prototype.init = function (dom, statusBar, canvas, images, sounds, floorIds
|
||||
core[key] = coreData[key];
|
||||
}
|
||||
core.flags = core.clone(core.data.flags);
|
||||
core.flags.battleAnimate = core.getLocalStorage('battleAnimate', core.flags.battleAnimate);
|
||||
core.values = core.clone(core.data.values);
|
||||
core.firstData = core.data.getFirstData();
|
||||
core.initStatus.shops = core.firstData.shops;
|
||||
@ -296,7 +297,7 @@ core.prototype.clearStatus = function() {
|
||||
core.resize(main.dom.body.clientWidth, main.dom.body.clientHeight);
|
||||
}
|
||||
|
||||
core.prototype.resetStatus = function(hero, hard, floorId, maps) {
|
||||
core.prototype.resetStatus = function(hero, hard, floorId, flags, maps) {
|
||||
|
||||
// 停止各个Timeout和Interval
|
||||
for (var i in core.interval) {
|
||||
@ -314,6 +315,8 @@ core.prototype.resetStatus = function(hero, hard, floorId, maps) {
|
||||
// 初始化人物属性
|
||||
core.status.hero = core.clone(hero);
|
||||
core.status.hard = hard;
|
||||
if (core.isset(flags))
|
||||
core.flags = core.clone(flags);
|
||||
// 保存页面
|
||||
core.status.savePage = core.getLocalStorage('savePage', 0);
|
||||
|
||||
@ -324,7 +327,7 @@ core.prototype.resetStatus = function(hero, hard, floorId, maps) {
|
||||
core.prototype.startGame = function (hard, callback) {
|
||||
console.log('开始游戏');
|
||||
|
||||
core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, core.initStatus.maps);
|
||||
core.resetStatus(core.firstData.hero, hard, core.firstData.floorId, core.flags, core.initStatus.maps);
|
||||
|
||||
core.changeFloor(core.status.floorId, null, core.firstData.hero.loc, null, function() {
|
||||
core.setHeroMoveTriggerInterval();
|
||||
@ -501,6 +504,10 @@ core.prototype.keyUp = function(keyCode) {
|
||||
if (core.status.heroStop)
|
||||
core.ui.drawQuickShop(true);
|
||||
break;
|
||||
case 32: // SPACE
|
||||
if (!core.status.lockControl && core.status.heroStop)
|
||||
core.getNextItem();
|
||||
break;
|
||||
case 37: // UP
|
||||
break;
|
||||
case 38: // DOWN
|
||||
@ -520,12 +527,23 @@ core.prototype.keyUp = function(keyCode) {
|
||||
}
|
||||
break;
|
||||
case 50: // 快捷键2:炸
|
||||
if (core.status.heroStop && core.hasItem('bomb')) {
|
||||
if (core.canUseItem('bomb')) {
|
||||
core.useItem('bomb');
|
||||
if (core.status.heroStop) {
|
||||
if (core.hasItem('bomb')) {
|
||||
if (core.canUseItem('bomb')) {
|
||||
core.useItem('bomb');
|
||||
}
|
||||
else {
|
||||
core.drawTip('当前不能使用炸弹');
|
||||
}
|
||||
}
|
||||
else {
|
||||
core.drawTip('当前不能使用炸弹');
|
||||
else if (core.hasItem('hammer')) {
|
||||
if (core.canUseItem('hammer')) {
|
||||
core.useItem('hammer');
|
||||
}
|
||||
else {
|
||||
core.drawTip('当前不能使用圣锤');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -626,7 +644,7 @@ core.prototype.getClickLoc = function (x, y) {
|
||||
}
|
||||
|
||||
core.prototype.onclick = function (x, y, stepPostfix) {
|
||||
console.log("Click: (" + x + "," + y + ")");
|
||||
// console.log("Click: (" + x + "," + y + ")");
|
||||
|
||||
// 非游戏屏幕内
|
||||
if (x<0 || y<0 || x>12 || y>12) return;
|
||||
@ -649,6 +667,12 @@ core.prototype.onclick = function (x, y, stepPostfix) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开关
|
||||
if (core.status.event.id == 'switchs') {
|
||||
core.events.clickSwitchs(x,y);
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置
|
||||
if (core.status.event.id == 'settings') {
|
||||
core.events.clickSettings(x,y);
|
||||
@ -745,14 +769,14 @@ core.prototype.onmousewheel = function (direct) {
|
||||
}
|
||||
|
||||
// 怪物手册
|
||||
if (core.status.event.id == 'book') {
|
||||
if (core.status.lockControl && core.status.event.id == 'book') {
|
||||
if (direct==1) core.ui.drawEnemyBook(core.status.event.data - 1);
|
||||
if (direct==-1) core.ui.drawEnemyBook(core.status.event.data + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 存读档
|
||||
if (core.status.event.id == 'save' || core.status.event.id == 'load') {
|
||||
if (core.status.lockControl && (core.status.event.id == 'save' || core.status.event.id == 'load')) {
|
||||
if (direct==1) core.ui.drawSLPanel(core.status.event.data - 1);
|
||||
if (direct==-1) core.ui.drawSLPanel(core.status.event.data + 1);
|
||||
return;
|
||||
@ -807,7 +831,18 @@ core.prototype.setAutomaticRoute = function (destX, destY, stepPostfix) {
|
||||
return;
|
||||
}
|
||||
if (destX == core.status.hero.loc.x && destY == core.status.hero.loc.y && stepPostfix.length==0) {
|
||||
core.turnHero();
|
||||
if (core.timeout.turnHeroTimeout==null) {
|
||||
core.timeout.turnHeroTimeout = setTimeout(function() {
|
||||
core.turnHero();
|
||||
clearTimeout(core.timeout.turnHeroTimeout);
|
||||
core.timeout.turnHeroTimeout = null;
|
||||
}, 250);
|
||||
}
|
||||
else {
|
||||
clearTimeout(core.timeout.turnHeroTimeout);
|
||||
core.timeout.turnHeroTimeout = null;
|
||||
core.getNextItem();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var step = 0;
|
||||
@ -960,14 +995,16 @@ core.prototype.automaticRoute = function (destX, destY) {
|
||||
if ( (scan[direction].x + scan[nextArrow].x) == 0 && (scan[direction].y + scan[nextArrow].y) == 0 ) continue;
|
||||
}
|
||||
// 绕过亮灯(因为只有一次通行机会很宝贵)
|
||||
if(nextId == "light") deepAdd=50;
|
||||
if(nextId == "light") deepAdd=100;
|
||||
// 绕过路障
|
||||
if (nextId.substring(nextId.length-3)=="Net") deepAdd=100;
|
||||
if (nextId.substring(nextId.length-3)=="Net") deepAdd=core.values.lavaDamage;
|
||||
// 绕过血瓶
|
||||
if (!core.flags.potionWhileRouting && nextId.substring(nextId.length-6)=="Potion") deepAdd=20;
|
||||
// 绕过可能的夹击点
|
||||
if (nextBlock.block.event.trigger == 'checkBlock') deepAdd=200;
|
||||
// if (nextBlock.block.event.trigger == 'checkBlock') deepAdd=200;
|
||||
}
|
||||
if (core.status.checkBlock[nid]>0)
|
||||
deepAdd = core.status.checkBlock[nid];
|
||||
|
||||
if (nx == destX && ny == destY) {
|
||||
route[nid] = direction;
|
||||
@ -1138,6 +1175,7 @@ core.prototype.setHeroMoveTriggerInterval = function () {
|
||||
core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop');
|
||||
}
|
||||
core.trigger(core.getHeroLoc('x'), core.getHeroLoc('y'));
|
||||
core.checkBlock();
|
||||
clearInterval(core.interval.heroMoveInterval);
|
||||
core.status.heroMoving = false;
|
||||
});
|
||||
@ -1415,6 +1453,7 @@ core.prototype.afterBattle = function(id, x, y, callback) {
|
||||
hint += ",经验+" + core.material.enemys[id].experience;
|
||||
core.drawTip(hint);
|
||||
|
||||
core.updateCheckBlockMap();
|
||||
// 打完怪物,触发事件
|
||||
core.events.afterBattle(id,x,y,callback);
|
||||
|
||||
@ -1491,6 +1530,8 @@ core.prototype.changeFloor = function (floorId, stair, heroLoc, time, callback)
|
||||
core.setHeroLoc('x', heroLoc.x);
|
||||
core.setHeroLoc('y', heroLoc.y);
|
||||
core.drawHero(core.getHeroLoc('direction'), core.getHeroLoc('x'), core.getHeroLoc('y'), 'stop');
|
||||
core.updateCheckBlockMap();
|
||||
core.updateCheckBlock();
|
||||
core.updateFg();
|
||||
}, 15)
|
||||
});
|
||||
@ -1992,6 +2033,7 @@ core.prototype.addBlock = function(x,y,floodId) {
|
||||
// 本身是禁用事件,启用之
|
||||
if (core.isset(block.enable) && !block.enable) {
|
||||
block.enable = true;
|
||||
core.updateCheckBlockMap();
|
||||
// 在本层,添加动画
|
||||
if (floodId == core.status.floorId && core.isset(block.event)) {
|
||||
blockIcon = core.material.icons[block.event.cls][block.event.id];
|
||||
@ -2028,24 +2070,22 @@ core.prototype.removeBlockById = function (index, floorId) {
|
||||
var blocks = core.status.maps[floorId].blocks;
|
||||
var x=blocks[index].x, y=blocks[index].y;
|
||||
|
||||
// 检查该点是否是checkBlock
|
||||
if (core.floors[floorId].checkBlock.indexOf(x+","+y)>=0) {
|
||||
blocks[index] = {'x': x, 'y': y, 'event': {'cls': 'terrains', 'id': 'ground', 'noPass': false, 'trigger': 'checkBlock'}};
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查该点是否存在事件
|
||||
var event = core.floors[floorId].events[x+","+y];
|
||||
if (!core.isset(event))
|
||||
event = core.floors[floorId].changeFloor[x+","+y];
|
||||
|
||||
var shouldUpdateBlockMap = blocks[index].event.cls == 'enemys';
|
||||
// 不存在事件,直接删除
|
||||
if (!core.isset(event)) {
|
||||
blocks.splice(index,1);
|
||||
if (shouldUpdateBlockMap)
|
||||
core.updateCheckBlockMap();
|
||||
return;
|
||||
}
|
||||
|
||||
blocks[index].enable = false;
|
||||
if (shouldUpdateBlockMap)
|
||||
core.updateCheckBlockMap();
|
||||
}
|
||||
|
||||
core.prototype.removeBlockByIds = function (floorId, ids) {
|
||||
@ -2144,32 +2184,154 @@ core.prototype.drawBoxAnimate = function (background) {
|
||||
}
|
||||
}
|
||||
|
||||
core.prototype.updateCheckBlockMap = function() {
|
||||
// 更新领域、夹击地图
|
||||
core.status.checkBlockMap = [];
|
||||
var blocks = core.status.thisMap.blocks;
|
||||
for (var n=0;n<blocks.length;n++) {
|
||||
var block = blocks[n];
|
||||
if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable)) {
|
||||
if (block.event.cls=='enemys') {
|
||||
var enemy = core.enemys.getEnemys(block.event.id);
|
||||
if (core.isset(enemy)) {
|
||||
var value=0;
|
||||
if (core.enemys.hasSpecial(enemy.special, 15)) // 领域
|
||||
value += enemy.value;
|
||||
if (core.enemys.hasSpecial(enemy.special, 16)) // 夹击
|
||||
value += 1000000 * block.id;
|
||||
core.status.checkBlockMap[13*block.x+block.y] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新领域、显伤点
|
||||
core.prototype.updateCheckBlock = function() {
|
||||
if (!core.isset(core.status.thisMap)) return;
|
||||
if (!core.isset(core.status.checkBlockMap)) core.updateCheckBlockMap();
|
||||
core.status.checkBlock = [];
|
||||
for (var x=0;x<13;x++) {
|
||||
for (var y=0;y<13;y++) {
|
||||
// 计算(x,y)点伤害
|
||||
var damage = 0;
|
||||
if (!core.enemyExists(x,y)) { // 如果该点本身不存在怪物(打死怪物会调用更新)
|
||||
|
||||
// 领域
|
||||
[[-1,0],[0,-1],[1,0],[0,1]].forEach(function (dir) {
|
||||
var nx = x+dir[0], ny = y+dir[1];
|
||||
if (nx<0 || nx>12 || ny<0 || ny>12) return;
|
||||
if (core.status.checkBlockMap[13*nx+ny]%1000000>0) {
|
||||
damage += core.status.checkBlockMap[13*nx+ny] % 1000000;
|
||||
}
|
||||
})
|
||||
|
||||
var leftValue = core.status.hero.hp - damage;
|
||||
if (leftValue>1) {
|
||||
var has = false;
|
||||
// 夹击
|
||||
if (x>0 && x<12) {
|
||||
var id1=parseInt(core.status.checkBlockMap[13*(x-1)+y]/1000000),
|
||||
id2=parseInt(core.status.checkBlockMap[13*(x+1)+y]/1000000);
|
||||
if (id1>0 && id1==id2)
|
||||
has = true;
|
||||
}
|
||||
if (y>0 && y<12) {
|
||||
var id1=parseInt(core.status.checkBlockMap[13*x+y-1]/1000000),
|
||||
id2=parseInt(core.status.checkBlockMap[13*x+y+1]/1000000);
|
||||
if (id1>0 && id1==id2)
|
||||
has = true;
|
||||
}
|
||||
if (has) {
|
||||
damage += parseInt((leftValue+1) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
core.status.checkBlock[13*x+y] = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.prototype.checkBlock = function () {
|
||||
// 检查领域、夹击事件
|
||||
var x=core.getHeroLoc('x'), y=core.getHeroLoc('y');
|
||||
if (core.status.checkBlock[13*x+y]>0) {
|
||||
core.status.hero.hp -= core.status.checkBlock[13*x+y];
|
||||
if (core.hasBetweenAttack(x,y)) {
|
||||
core.drawTip('受到夹击,生命变成一半');
|
||||
}
|
||||
else if (core.hasZone(x,y)) {
|
||||
core.drawTip('受到领域伤害'+core.status.checkBlock[13*x+y]+'点');
|
||||
}
|
||||
if (core.status.hero.hp<=0) {
|
||||
core.status.hero.hp=0;
|
||||
core.updateStatusBar();
|
||||
core.events.lose('zone');
|
||||
return;
|
||||
}
|
||||
core.updateStatusBar();
|
||||
}
|
||||
}
|
||||
|
||||
core.prototype.hasZone = function (x,y) {
|
||||
if (!core.isset(core.status.checkBlockMap)) core.updateCheckBlockMap();
|
||||
var isZone = false;
|
||||
// 领域
|
||||
[[-1,0],[0,-1],[1,0],[0,1]].forEach(function (dir) {
|
||||
var nx = x+dir[0], ny = y+dir[1];
|
||||
if (nx<0 || nx>12 || ny<0 || ny>12) return;
|
||||
if (core.status.checkBlockMap[13*nx+ny]%1000000>0) {
|
||||
isZone = true;
|
||||
}
|
||||
})
|
||||
return isZone;
|
||||
}
|
||||
|
||||
core.prototype.hasBetweenAttack = function(x,y) {
|
||||
if (!core.isset(core.status.checkBlockMap)) core.updateCheckBlockMap();
|
||||
// 夹击
|
||||
if (x>0 && x<12) {
|
||||
var id1=parseInt(core.status.checkBlockMap[13*(x-1)+y]/1000000),
|
||||
id2=parseInt(core.status.checkBlockMap[13*(x+1)+y]/1000000);
|
||||
if (id1>0 && id1==id2)
|
||||
return true;
|
||||
}
|
||||
if (y>0 && y<12) {
|
||||
var id1=parseInt(core.status.checkBlockMap[13*x+y-1]/1000000),
|
||||
id2=parseInt(core.status.checkBlockMap[13*x+y+1]/1000000);
|
||||
if (id1>0 && id1==id2)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
core.prototype.setFg = function(color, time, callback) {
|
||||
time = time || 750;
|
||||
core.setOpacity('fg', 1);
|
||||
|
||||
var fromAlpha = 0;
|
||||
if (core.isset(core.status.event.data.currentColor)) {
|
||||
fromAlpha = 1;
|
||||
}
|
||||
else {
|
||||
core.status.event.data.currentColor = [0,0,0];
|
||||
var reset = false;
|
||||
|
||||
if (!core.isset(core.status.event.data.currentColor)) {
|
||||
core.status.event.data.currentColor = [0,0,0,0];
|
||||
}
|
||||
|
||||
var fromColor = core.status.event.data.currentColor;
|
||||
|
||||
var toAlpha = 1;
|
||||
if (!core.isset(color)) {
|
||||
color = [0,0,0];
|
||||
toAlpha=0;
|
||||
color = [0,0,0,0];
|
||||
reset = true;
|
||||
}
|
||||
if (color.length==3) {
|
||||
color.push(1);
|
||||
}
|
||||
if (color[3]<0) color[3]=0;
|
||||
if (color[3]>1) color[3]=1;
|
||||
|
||||
var step=0;
|
||||
var changeAnimate = setInterval(function() {
|
||||
step++;
|
||||
core.clearMap('fg', 0, 0, 416, 416);
|
||||
|
||||
var nowAlpha = fromAlpha+(toAlpha-fromAlpha)*step/25;
|
||||
var nowAlpha = fromColor[3]+(color[3]-fromColor[3])*step/25;
|
||||
var nowR = parseInt(fromColor[0]+(color[0]-fromColor[0])*step/25);
|
||||
var nowG = parseInt(fromColor[1]+(color[1]-fromColor[1])*step/25);
|
||||
var nowB = parseInt(fromColor[2]+(color[2]-fromColor[2])*step/25);
|
||||
@ -2178,12 +2340,12 @@ core.prototype.setFg = function(color, time, callback) {
|
||||
if (nowB<0) nowB=0; if (nowB>255) nowB=255;
|
||||
|
||||
core.setAlpha('fg', nowAlpha);
|
||||
var toRGB = "#"+nowR.toString(16)+nowG.toString(16)+nowB.toString(16);
|
||||
var toRGB = "#"+((1<<24)+(nowR<<16)+(nowG<<8)+nowB).toString(16).slice(1)
|
||||
core.fillRect('fg', 0, 0, 416, 416, toRGB);
|
||||
|
||||
if (step>=25) {
|
||||
clearInterval(changeAnimate);
|
||||
if (toAlpha==0) {
|
||||
if (reset) {
|
||||
core.clearMap('fg', 0, 0, 416, 416);
|
||||
delete core.status.event.data.currentColor;
|
||||
core.setAlpha('fg', 1);
|
||||
@ -2242,7 +2404,8 @@ core.prototype.updateFg = function () {
|
||||
// 如果存在颜色
|
||||
if (core.isset(core.status.event.data) && core.isset(core.status.event.data.currentColor)) {
|
||||
var color=core.status.event.data.currentColor;
|
||||
core.fillRect("fg",0,0,416,416,"#"+color[0].toString(16)+color[1].toString(16)+color[2].toString(16));
|
||||
core.setAlpha('fg', color[3]);
|
||||
core.fillRect("fg",0,0,416,416,"#"+((1<<24)+(color[0]<<16)+(color[1]<<8)+color[2]).toString(16).slice(1));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2254,32 +2417,54 @@ core.prototype.updateFg = function () {
|
||||
if (!core.hasItem('book')) return;
|
||||
core.setFont('fg', "bold 11px Arial");
|
||||
var hero_hp = core.status.hero.hp;
|
||||
for (var b = 0; b < mapBlocks.length; b++) {
|
||||
var x = mapBlocks[b].x, y = mapBlocks[b].y;
|
||||
if (core.isset(mapBlocks[b].event) && mapBlocks[b].event.cls == 'enemys' && mapBlocks[b].event.trigger=='battle'
|
||||
if (core.flags.displayEnemyDamage) {
|
||||
core.canvas.fg.textAlign = 'left';
|
||||
for (var b = 0; b < mapBlocks.length; b++) {
|
||||
var x = mapBlocks[b].x, y = mapBlocks[b].y;
|
||||
if (core.isset(mapBlocks[b].event) && mapBlocks[b].event.cls == 'enemys' && mapBlocks[b].event.trigger == 'battle'
|
||||
&& !(core.isset(mapBlocks[b].enable) && !mapBlocks[b].enable)) {
|
||||
var id = mapBlocks[b].event.id;
|
||||
var id = mapBlocks[b].event.id;
|
||||
|
||||
var damage = core.enemys.getDamage(id);
|
||||
var color = "#000000";
|
||||
if (damage <= 0) color = '#00FF00';
|
||||
else if (damage < hero_hp / 3) color = '#FFFFFF';
|
||||
else if (damage < hero_hp * 2 / 3) color = '#FFFF00';
|
||||
else if (damage < hero_hp) color = '#FF7F00';
|
||||
else color = '#FF0000';
|
||||
var damage = core.enemys.getDamage(id);
|
||||
var color = "#000000";
|
||||
if (damage <= 0) color = '#00FF00';
|
||||
else if (damage < hero_hp / 3) color = '#FFFFFF';
|
||||
else if (damage < hero_hp * 2 / 3) color = '#FFFF00';
|
||||
else if (damage < hero_hp) color = '#FF7F00';
|
||||
else color = '#FF0000';
|
||||
|
||||
if (damage >= 999999999) damage = "???";
|
||||
else if (damage > 100000) damage = (damage / 10000).toFixed(1) + "w";
|
||||
if (damage >= 999999999) damage = "???";
|
||||
else if (damage > 100000) damage = (damage / 10000).toFixed(1) + "w";
|
||||
|
||||
core.setFillStyle('fg', '#000000');
|
||||
core.canvas.fg.fillText(damage, 32 * x + 2, 32 * (y + 1) - 2);
|
||||
core.canvas.fg.fillText(damage, 32 * x, 32 * (y + 1) - 2);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 2, 32 * (y + 1));
|
||||
core.canvas.fg.fillText(damage, 32 * x, 32 * (y + 1));
|
||||
core.setFillStyle('fg', '#000000');
|
||||
core.canvas.fg.fillText(damage, 32 * x + 2, 32 * (y + 1) - 2);
|
||||
core.canvas.fg.fillText(damage, 32 * x, 32 * (y + 1) - 2);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 2, 32 * (y + 1));
|
||||
core.canvas.fg.fillText(damage, 32 * x, 32 * (y + 1));
|
||||
|
||||
core.setFillStyle('fg', color);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 1, 32 * (y + 1) - 1);
|
||||
core.setFillStyle('fg', color);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 1, 32 * (y + 1) - 1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果是领域&夹击
|
||||
if (core.flags.displayExtraDamage) {
|
||||
core.canvas.fg.textAlign = 'center';
|
||||
for (var x=0;x<13;x++) {
|
||||
for (var y=0;y<13;y++) {
|
||||
var damage = core.status.checkBlock[13*x+y];
|
||||
if (damage>0) {
|
||||
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);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 17, 32 * (y + 1) - 15);
|
||||
core.canvas.fg.fillText(damage, 32 * x + 15, 32 * (y + 1) - 13);
|
||||
|
||||
core.setFillStyle('fg', '#FF7F00');
|
||||
core.canvas.fg.fillText(damage, 32 * x + 16, 32 * (y + 1) - 14);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2341,6 +2526,16 @@ core.prototype.addItem = function (itemId, itemNum) {
|
||||
core.status.hero.items[itemCls][itemId] += itemNum;
|
||||
}
|
||||
|
||||
core.prototype.getNextItem = function() {
|
||||
if (!core.status.heroStop || !core.flags.enableGentleClick) return;
|
||||
var nextX = core.nextX(), nextY = core.nextY();
|
||||
var block = core.getBlock(nextX, nextY);
|
||||
if (block==null) return;
|
||||
if (block.block.event.trigger=='getItem') {
|
||||
core.getItem(block.block.event.id, 1, nextX, nextY);
|
||||
}
|
||||
}
|
||||
|
||||
core.prototype.getItem = function (itemId, itemNum, itemX, itemY, callback) {
|
||||
// core.getItemAnimate(itemId, itemNum, itemX, itemY);
|
||||
core.playSound('item', 'ogg');
|
||||
@ -2368,6 +2563,7 @@ core.prototype.drawTip = function (text, itemIcon) {
|
||||
core.setFont('data', "16px Arial");
|
||||
core.saveCanvas('data');
|
||||
core.setOpacity('data', 0);
|
||||
core.canvas.data.textAlign = 'left';
|
||||
if (!core.isset(itemIcon)) {
|
||||
textX = 16;
|
||||
textY = 18;
|
||||
@ -2845,6 +3041,7 @@ core.prototype.saveData = function(dataId) {
|
||||
'hard': core.status.hard,
|
||||
'maps': core.maps.save(core.status.maps),
|
||||
'shops': {},
|
||||
'flags': core.flags,
|
||||
'version': core.firstData.version,
|
||||
"time": new Date().getTime()
|
||||
};
|
||||
@ -2862,7 +3059,7 @@ core.prototype.saveData = function(dataId) {
|
||||
|
||||
core.prototype.loadData = function (data, callback) {
|
||||
|
||||
core.resetStatus(data.hero, data.hard, data.floorId, core.maps.load(data.maps));
|
||||
core.resetStatus(data.hero, data.hard, data.floorId, data.flags, core.maps.load(data.maps));
|
||||
|
||||
// load shop times
|
||||
for (var shop in core.status.shops) {
|
||||
@ -3038,7 +3235,6 @@ core.prototype.updateStatusBar = function () {
|
||||
core.statusBar.weak.innerHTML = core.hasFlag('weak')?"衰":"";
|
||||
core.statusBar.curse.innerHTML = core.hasFlag('curse')?"咒":"";
|
||||
}
|
||||
|
||||
|
||||
core.statusBar.hard.innerHTML = core.status.hard;
|
||||
if (core.hasItem('book')) {
|
||||
@ -3051,6 +3247,7 @@ core.prototype.updateStatusBar = function () {
|
||||
} else {
|
||||
core.statusBar.image.fly.style.opacity = 0.3;
|
||||
}
|
||||
core.updateCheckBlock();
|
||||
core.updateFg();
|
||||
}
|
||||
|
||||
|
||||
@ -111,12 +111,12 @@ data.prototype.init = function() {
|
||||
'breakArmor': 0.9, // 破甲的比例(战斗前,怪物附加角色防御的x%作为伤害)
|
||||
'counterAttack': 0.1, // 反击的比例(战斗时,怪物每回合附加角色攻击的x%作为伤害,无视角色防御)
|
||||
'purify': 3, // 净化的比例(战斗前,怪物附加勇士魔防的x倍作为伤害)
|
||||
'hatred': 2, // 仇恨属性中,每杀死一个怪物获得的仇恨值
|
||||
/****** 系统相关 ******/
|
||||
'animateSpeed': 500, // 动画时间
|
||||
}
|
||||
// 系统FLAG,在游戏运行中中请不要修改它。
|
||||
this.flags = {
|
||||
|
||||
/****** 角色状态相关 ******/
|
||||
"enableMDef": true, // 是否涉及勇士的魔防值;如果此项为false,则状态栏不会显示勇士的魔防值
|
||||
"enableExperience": true, // 是否涉及经验值;如果此项为false,则状态栏和怪物手册均将不会显示经验值
|
||||
@ -124,11 +124,14 @@ data.prototype.init = function() {
|
||||
/****** 道具相关 ******/
|
||||
"flyNearStair": true, // 是否需要在楼梯边使用传送器
|
||||
"pickaxeFourDirections": true, // 使用破墙镐是否四个方向都破坏;如果false则只破坏面前的墙壁
|
||||
"bombFourDirections": true, // 使用炸弹是否四个方向都会炸;如果false则只炸面前的怪物(即和圣锤等价)
|
||||
"bigKeyIsBox": false, // 如果此项为true,则视为钥匙盒,红黄蓝钥匙+1;若为false,则视为大黄门钥匙
|
||||
|
||||
/****** 系统相关 ******/
|
||||
"startDirectly": false, // 点击“开始游戏”后是否立刻开始游戏而不显示难度选择界面
|
||||
"battleAnimate": true, // 是否默认显示战斗动画;用户可以手动在菜单栏中关闭
|
||||
"battleAnimate": true, // 是否默认显示战斗动画;用户可以手动在菜单栏中开关
|
||||
"displayEnemyDamage": true, // 是否地图怪物显伤;用户可以手动在菜单栏中开关
|
||||
"displayExtraDamage": false, // 是否地图高级显伤(领域、夹击等);用户可以手动在菜单栏中开关
|
||||
"enableGentleClick": true, // 是否允许轻触(获得面前物品)
|
||||
"portalWithoutTrigger": true, // 经过楼梯、传送门时是否能“穿透”。穿透的意思是,自动寻路得到的的路径中间经过了楼梯,行走时是否触发楼层转换事件
|
||||
"potionWhileRouting": false, // 寻路算法是否经过血瓶;如果该项为false,则寻路算法会自动尽量绕过血瓶
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ enemys.prototype.init = function () {
|
||||
'poisonZombie': {'name': '绿兽人', 'hp': 100, 'atk': 120, 'def': 0, 'money': 13, 'experience': 0, 'special': 12},
|
||||
'magicDragon': {'name': '魔龙', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
|
||||
'octopus': {'name': '血影', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
|
||||
'fairy': {'name': '仙子', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
|
||||
'darkFairy': {'name': '仙子', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
|
||||
'greenKnight': {'name': '强盾骑士', 'hp': 0, 'atk': 0, 'def': 0, 'money': 0, 'experience': 0, 'special': 0},
|
||||
}
|
||||
}
|
||||
@ -99,6 +99,7 @@ enemys.prototype.getSpecialText = function (enemyId) {
|
||||
if (this.hasSpecial(special, 14)) text.push("诅咒");
|
||||
if (this.hasSpecial(special, 15)) text.push("领域");
|
||||
if (this.hasSpecial(special, 16)) text.push("夹击");
|
||||
if (this.hasSpecial(special, 17)) text.push("仇恨");
|
||||
return text.join(" ");
|
||||
}
|
||||
|
||||
@ -118,6 +119,9 @@ enemys.prototype.getExtraDamage = function (monster) {
|
||||
extra_damage = core.status.hero.hp * monster.value;
|
||||
extra_damage = parseInt(extra_damage);
|
||||
}
|
||||
if (this.hasSpecial(monster.special, 17)) { // 仇恨
|
||||
extra_damage += core.getFlag('hatred', 0);
|
||||
}
|
||||
return extra_damage;
|
||||
}
|
||||
|
||||
|
||||
119
libs/events.js
119
libs/events.js
@ -34,11 +34,6 @@ events.prototype.init = function () {
|
||||
if (core.isset(callback))
|
||||
callback();
|
||||
},
|
||||
"checkBlock": function (data, core, callback) {
|
||||
core.events.checkBlock(data.x, data.y);
|
||||
if (core.isset(callback))
|
||||
callback();
|
||||
},
|
||||
"changeLight": function (data, core, callback) {
|
||||
core.events.changeLight(data.x, data.y);
|
||||
if (core.isset(callback))
|
||||
@ -112,53 +107,9 @@ events.prototype.lose = function(reason) {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
////// 检查领域、夹击事件 //////
|
||||
events.prototype.checkBlock = function (x,y) {
|
||||
var damage = 0;
|
||||
// 获得四个方向的怪物
|
||||
var directions = [[0,-1],[-1,0],[0,1],[1,0]]; // 上,左,下,右
|
||||
var enemys = [null,null,null,null];
|
||||
for (var i in directions) {
|
||||
var block = core.getBlock(x+directions[i][0], y+directions[i][1]);
|
||||
if (block==null) continue;
|
||||
// 是怪物
|
||||
if (block.block.event.cls=='enemys')
|
||||
enemys[i]=core.material.enemys[block.block.event.id];
|
||||
}
|
||||
|
||||
// 领域
|
||||
for (var i in enemys) {
|
||||
if (enemys[i]!=null && core.enemys.hasSpecial(enemys[i].special, 15)) {
|
||||
damage+=enemys[i].value;
|
||||
}
|
||||
}
|
||||
if (damage>0)
|
||||
core.drawTip('受到领域伤害'+damage+'点');
|
||||
core.status.hero.hp-=damage;
|
||||
if (core.status.hero.hp<=0) {
|
||||
core.status.hero.hp=0;
|
||||
core.updateStatusBar();
|
||||
core.events.lose('zone');
|
||||
return;
|
||||
}
|
||||
|
||||
// 夹击
|
||||
var has=false;
|
||||
if (enemys[0]!=null && enemys[2]!=null && enemys[0].id==enemys[2].id && core.enemys.hasSpecial(enemys[0].special, 16))
|
||||
has=true;
|
||||
if (enemys[1]!=null && enemys[3]!=null && enemys[1].id==enemys[3].id && core.enemys.hasSpecial(enemys[1].special, 16))
|
||||
has=true;
|
||||
if (has && core.status.hero.hp>1) { // 1血夹击不死
|
||||
core.status.hero.hp = parseInt(core.status.hero.hp/2);
|
||||
core.drawTip('受到夹击,生命变成一半');
|
||||
}
|
||||
core.updateStatusBar();
|
||||
}
|
||||
|
||||
////// 转换楼层结束的事件 //////
|
||||
events.prototype.afterChangeFloor = function (floorId) {
|
||||
if (!core.hasFlag("visited_"+floorId)) {
|
||||
if (!core.isset(core.status.event.id) && !core.hasFlag("visited_"+floorId)) {
|
||||
this.doEvents(core.floors[floorId].firstArrive);
|
||||
core.setFlag("visited_"+floorId, true);
|
||||
}
|
||||
@ -212,6 +163,9 @@ events.prototype.doAction = function() {
|
||||
case "text": // 文字/对话
|
||||
core.ui.drawTextBox(data.data);
|
||||
break;
|
||||
case "tip":
|
||||
core.drawTip(core.replaceText(data.text));
|
||||
core.events.doAction();
|
||||
case "show": // 显示
|
||||
if (core.isset(data.time) && data.time>0 && (!core.isset(data.floorId) || data.floorId==core.status.floorId)) {
|
||||
core.animateBlock(data.loc[0],data.loc[1],'show', data.time, function () {
|
||||
@ -350,8 +304,16 @@ events.prototype.doAction = function() {
|
||||
}
|
||||
}
|
||||
catch (e) {console.log(e)}
|
||||
core.updateStatusBar();
|
||||
this.doAction();
|
||||
if (core.status.hero.hp<=0) {
|
||||
core.status.hero.hp=0;
|
||||
core.updateStatusBar();
|
||||
core.events.lose('damage');
|
||||
|
||||
}
|
||||
else {
|
||||
core.updateStatusBar();
|
||||
this.doAction();
|
||||
}
|
||||
break;
|
||||
case "if": // 条件判断
|
||||
if (core.calValue(data.condition))
|
||||
@ -451,8 +413,10 @@ events.prototype.disableQuickShop = function (shopId) {
|
||||
}
|
||||
|
||||
////// 降低难度 //////
|
||||
/*
|
||||
|
||||
events.prototype.decreaseHard = function() {
|
||||
core.drawTip("本塔不支持降低难度!");
|
||||
/*
|
||||
if (core.status.hard == 0) {
|
||||
core.drawTip("当前已是难度0,不能再降低难度了");
|
||||
return;
|
||||
@ -470,8 +434,8 @@ events.prototype.decreaseHard = function() {
|
||||
}, function () {
|
||||
core.ui.drawSettings(false);
|
||||
});
|
||||
*/
|
||||
}
|
||||
*/
|
||||
|
||||
////// 能否使用快捷商店 //////
|
||||
events.prototype.canUseQuickShop = function(shopIndex) {
|
||||
@ -506,20 +470,24 @@ events.prototype.afterBattle = function(enemyId,x,y,callback) {
|
||||
// 中毒
|
||||
if (core.enemys.hasSpecial(special, 12) && !core.hasFlag('poison')) {
|
||||
core.setFlag('poison', true);
|
||||
core.updateStatusBar();
|
||||
}
|
||||
// 衰弱
|
||||
if (core.enemys.hasSpecial(special, 13) && !core.hasFlag('weak')) {
|
||||
core.setFlag('weak', true);
|
||||
core.status.hero.atk-=core.values.weakValue;
|
||||
core.status.hero.def-=core.values.weakValue;
|
||||
core.updateStatusBar();
|
||||
}
|
||||
// 诅咒
|
||||
if (core.enemys.hasSpecial(special, 14) && !core.hasFlag('curse')) {
|
||||
core.setFlag('curse', true);
|
||||
core.updateStatusBar();
|
||||
}
|
||||
// 仇恨属性:减半
|
||||
if (core.enemys.hasSpecial(special, 17)) {
|
||||
core.setFlag('hatred', parseInt(core.getFlag('hatred', 0)/2));
|
||||
}
|
||||
// 增加仇恨值
|
||||
core.setFlag('hatred', core.getFlag('hatred',0)+core.values.hatred);
|
||||
core.updateStatusBar();
|
||||
|
||||
// 如果已有事件正在处理中
|
||||
if (core.status.lockControl) {
|
||||
@ -604,11 +572,11 @@ events.prototype.changeLight = function(x, y) {
|
||||
core.canvas.event.clearRect(x * 32, y * 32, 32, 32);
|
||||
var blockIcon = core.material.icons[block.event.cls][block.event.id];
|
||||
core.canvas.event.drawImage(core.material.images[block.event.cls], 0, blockIcon * 32, 32, 32, block.x * 32, block.y * 32, 32, 32);
|
||||
this.afterChangeLight();
|
||||
this.afterChangeLight(x,y);
|
||||
}
|
||||
|
||||
// 改变灯后的事件
|
||||
events.prototype.afterChangeLight = function() {
|
||||
events.prototype.afterChangeLight = function(x,y) {
|
||||
|
||||
}
|
||||
|
||||
@ -822,21 +790,48 @@ events.prototype.clickSL = function(x,y) {
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单栏
|
||||
events.prototype.clickSettings = function (x,y) {
|
||||
events.prototype.clickSwitchs = function (x,y) {
|
||||
if (x<5 || x>7) return;
|
||||
if (y == 3) {
|
||||
if (y==4) {
|
||||
if (core.musicStatus.isIOS) {
|
||||
core.drawTip("iOS设备不支持播放音乐");
|
||||
return;
|
||||
}
|
||||
core.changeSoundStatus();
|
||||
core.ui.drawSwitchs();
|
||||
}
|
||||
if (y==5) {
|
||||
core.flags.battleAnimate=!core.flags.battleAnimate;
|
||||
core.ui.drawSwitchs();
|
||||
}
|
||||
if (y==6) {
|
||||
core.flags.displayEnemyDamage=!core.flags.displayEnemyDamage;
|
||||
core.updateFg();
|
||||
core.ui.drawSwitchs();
|
||||
}
|
||||
if (y==7) {
|
||||
core.flags.displayExtraDamage=!core.flags.displayExtraDamage;
|
||||
core.updateFg();
|
||||
core.ui.drawSwitchs();
|
||||
}
|
||||
if (y==8) {
|
||||
core.ui.drawSettings(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 菜单栏
|
||||
events.prototype.clickSettings = function (x,y) {
|
||||
if (x<5 || x>7) return;
|
||||
if (y == 3) {
|
||||
core.ui.drawSwitchs();
|
||||
}
|
||||
if (y==4) {
|
||||
/*
|
||||
core.flags.battleAnimate=!core.flags.battleAnimate;
|
||||
core.setLocalStorage('battleAnimate', core.flags.battleAnimate);
|
||||
core.ui.drawSettings(false);
|
||||
*/
|
||||
this.decreaseHard();
|
||||
}
|
||||
if (y == 5) core.ui.drawQuickShop();
|
||||
// if (y == 5) this.decreaseHard();
|
||||
|
||||
@ -27,14 +27,6 @@ main.floors.MT0 = {
|
||||
},
|
||||
"afterOpenDoor": { // 开完门后可能触发的事件列表
|
||||
|
||||
},
|
||||
"checkBlock": [
|
||||
/****** 领域、夹击检查事件 ******/
|
||||
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
|
||||
// 另外,如果该点已经存在events事件或changeFloor事件(即上面有相同点位置定义),则会被覆盖
|
||||
// afterBattle, afterGetItem, afterOpenDoor则不受影响(仍能正常工作)。
|
||||
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -59,9 +59,8 @@ main.floors.sample0 = {
|
||||
"\t[老人,magician]模仿、吸血、中毒、衰弱、诅咒。\n\n请注意吸血怪需要设置value为吸血数值,可参见样板中黑暗大法师的写法。",
|
||||
{"type": "hide", "time": 500}
|
||||
],
|
||||
"2,3": [ // 守着第三批怪物额老人
|
||||
"2,3": [ // 守着第三批怪物的老人
|
||||
"\t[老人,magician]领域、夹击。\n请注意领域怪需要设置value为伤害数值,可参见样板中初级巫师的写法。",
|
||||
"\t[老人,magician]出于游戏性能的考虑,我们不可能每走一步都对领域和夹击进行检查。\n因此我们需要在本楼层的 checkBlock 中指明哪些点可能会触发领域和夹击事件,在这些点才会对领域和夹击进行检查和处理。\n具体可参见本层 checkBlock 的写法。",
|
||||
"\t[老人,magician]夹击和领域同时发生时先计算领域,再夹击。\n自动寻路同样会尽量绕过你设置的这些点。\n\n另:本塔不支持阻击怪。",
|
||||
{"type": "hide", "time": 500}
|
||||
],
|
||||
@ -99,7 +98,7 @@ main.floors.sample0 = {
|
||||
],
|
||||
"10,5": ["破墙镐是破面前的墙壁还是四个方向的墙壁,由data.js中的系统Flag所决定。"],
|
||||
"8,4": [
|
||||
"炸弹可以炸四个方向的怪物。\n如只需要炸前方怪物请使用上面的圣锤。\n不能被炸的怪物在enemys中可以定义,可参见样板里黑衣魔王和黑暗大法师的写法。",
|
||||
"炸弹是只能炸面前的怪物还是四个方向的怪物,由data.js中的系统Flag所决定。\n如只能炸前方怪物则和上面的圣锤等价。\n不能被炸的怪物在enemys中可以定义,可参见样板里黑衣魔王和黑暗大法师的写法。",
|
||||
],
|
||||
"10,4": ["“上楼”和“下楼”的目标层由 main.js 的 floorIds顺序所决定。"],
|
||||
"10,3": ["十字架目前未被定义,可能需要自行实现功能。\n有关如何实现一个道具功能参见doc文档。"],
|
||||
@ -108,15 +107,7 @@ main.floors.sample0 = {
|
||||
},
|
||||
"afterOpenDoor": { // 开完门后可能触发的事件列表
|
||||
"11,12": ["你开了一个绿门,触发了一个afterOpenDoor事件"]
|
||||
},
|
||||
"checkBlock": [
|
||||
/****** 领域、夹击检查事件 ******/
|
||||
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
|
||||
// 另外,如果该点已经存在events事件或changeFloor事件(即上面有相同点位置定义),则会被覆盖
|
||||
// afterBattle, afterGetItem, afterOpenDoor则不受影响(仍能正常工作)。
|
||||
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
|
||||
"1,0", "3,0", "0,1", "2,1", "4,1", "1,2", "3,2"
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ main.floors.sample1 = {
|
||||
"4,10": [ // 走到中间时的提示
|
||||
"\t[样板提示]本层楼将会对各类事件进行介绍。",
|
||||
"左边是一个仿50层的陷阱做法,上方是商店、快捷商店的使用方法,右上是一个典型的杀怪开门的例子,右下是各类可能的NPC事件。",
|
||||
"本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)\nopenShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nsetFg: 更改画面色调",
|
||||
"本样板目前支持的事件列表大致有:\ntext: 显示一段文字(比如你现在正在看到的)\ntip: 左上角显示提示\nshow: 使一个事件有效(可见、可被交互)\nhide: 使一个事件失效(不可见、不可被交互)\ntrigger: 触发另一个地点的事件\nbattle: 强制和某怪物战斗\nopenDoor: 无需钥匙开门(例如机关门、暗墙)\nopenShop: 打开一个全局商店\ndisableShop: 禁用一个全局商店\nchangeFloor: 传送勇士到某层某位置\nchangePos: 传送勇士到当层某位置;转向\nsetFg: 更改画面色调",
|
||||
"move: 移动事件效果\nmoveHero: 移动勇士效果\nplaySound: 播放某个音频\nif: 条件判断\nchoices: 提供选项\nsetValue: 设置勇士属性道具,或某个变量/flag\nupdate: 更新状态栏和地图显伤\nwin: 获得胜利(游戏通关)\nlose: 游戏失败\nsleep: 等待多少毫秒\nexit: 立刻结束当前事件\nrevisit: 立刻结束事件并重新触发\nfunction: 自定义JS脚本\n更多支持的事件还在编写中,欢迎您宝贵的意见。",
|
||||
"有关各事件的样例,可参见本层一些NPC的写法。\n所有事件样例本层都有介绍。\n\n一个自定义事件处理完后,需要调用{\"type\": \"hide\"}该事件才不会再次出现。",
|
||||
{"type": "hide"}
|
||||
@ -299,14 +299,6 @@ main.floors.sample1 = {
|
||||
},
|
||||
"afterOpenDoor": { // 开完门后可能触发的事件列表
|
||||
|
||||
},
|
||||
"checkBlock": [
|
||||
/****** 领域、夹击检查事件 ******/
|
||||
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
|
||||
// 另外,如果该点已经存在events事件或changeFloor事件(即上面有相同点位置定义),则会被覆盖
|
||||
// afterBattle, afterGetItem, afterOpenDoor则不受影响(仍能正常工作)。
|
||||
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -365,14 +365,6 @@ main.floors.sample2 = {
|
||||
},
|
||||
"afterOpenDoor": { // 开完门后可能触发的事件列表
|
||||
|
||||
},
|
||||
"checkBlock": [
|
||||
/****** 领域、夹击检查事件 ******/
|
||||
// 所有可能的领域、夹击点均需要在这里给出,否则将不会触发检查事件
|
||||
// 另外,如果该点已经存在events事件或changeFloor事件(即上面有相同点位置定义),则会被覆盖
|
||||
// afterBattle, afterGetItem, afterOpenDoor则不受影响(仍能正常工作)。
|
||||
// 所以 |****** 强烈要求可能的夹击、领域点不要存在自定义事件!! ******|
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ icons.prototype.init = function () {
|
||||
'poisonZombie': 55,
|
||||
'magicDragon': 56,
|
||||
'octopus': 57,
|
||||
'fairy': 58,
|
||||
'darkFairy': 58,
|
||||
'greenKnight': 59,
|
||||
},
|
||||
'items': {
|
||||
|
||||
@ -46,7 +46,7 @@ items.prototype.init = function () {
|
||||
'steelKey': {'cls': 'tools', 'name': '铁门钥匙', 'text': '可以打开一扇铁门'},
|
||||
'pickaxe': {'cls': 'tools', 'name': '破墙镐', 'text': '可以破坏勇士面前的墙'},
|
||||
'icePickaxe': {'cls': 'tools', 'name': '破冰镐', 'text': '可以破坏勇士面前的一堵冰墙'},
|
||||
'bomb': {'cls': 'tools', 'name': '炸弹', 'text': '可以炸掉勇士四周的怪物'},
|
||||
'bomb': {'cls': 'tools', 'name': '炸弹', 'text': '可以炸掉勇士面前的怪物'},
|
||||
'centerFly': {'cls': 'tools', 'name': '中心对称飞行器', 'text': '可以飞向当前楼层中心对称的位置'},
|
||||
'upFly': {'cls': 'tools', 'name': '上楼器', 'text': '可以飞往楼上的相同位置'},
|
||||
'downFly': {'cls': 'tools', 'name': '下楼器', 'text': '可以飞往楼下的相同位置'},
|
||||
@ -67,6 +67,8 @@ items.prototype.getItems = function () {
|
||||
// 面前的墙?四周的墙?
|
||||
if (core.flags.pickaxeFourDirections)
|
||||
this.items.pickaxe.text = "可以破坏勇士四周的墙";
|
||||
if (core.flags.bombFourDirections)
|
||||
this.items.bomb.text = "可以炸掉勇士四周的怪物";
|
||||
return this.items;
|
||||
}
|
||||
|
||||
@ -247,7 +249,8 @@ items.prototype.canUseItem = function (itemId) {
|
||||
if (core.isset(block.event) && !(core.isset(block.enable) && !block.enable) && block.event.cls == 'enemys' && Math.abs(block.x-core.status.hero.loc.x)+Math.abs(block.y-core.status.hero.loc.y)<=1) {
|
||||
var enemy = core.material.enemys[block.event.id];
|
||||
if (core.isset(enemy.bomb) && !enemy.bomb) continue;
|
||||
ids.push(i);
|
||||
if (core.flags.bombFourDirections || (block.x==core.nextX() && block.y==core.nextY()))
|
||||
ids.push(i);
|
||||
}
|
||||
}
|
||||
if (ids.length>0) {
|
||||
|
||||
@ -36,8 +36,6 @@ maps.prototype.loadFloor = function (floorId, map) {
|
||||
}
|
||||
this.addEvent(block,j,i,floor.events[j+","+i])
|
||||
this.addChangeFloor(block,j,i,floor.changeFloor[j+","+i]);
|
||||
if (floor.checkBlock.indexOf(j+","+i)>=0)
|
||||
this.addEvent(block,j,i,{"trigger":"checkBlock"});
|
||||
if (core.isset(block.event)) blocks.push(block);
|
||||
}
|
||||
}
|
||||
@ -78,7 +76,6 @@ maps.prototype.getBlock = function (x, y, id) {
|
||||
if (id == 12) tmp.event = {'cls': 'animates', 'id': 'poisonNet', 'noPass': false, 'trigger': 'passNet'}; // 毒网
|
||||
if (id == 13) tmp.event = {'cls': 'animates', 'id': 'weakNet', 'noPass': false, 'trigger': 'passNet'}; // 衰网
|
||||
if (id == 14) tmp.event = {'cls': 'animates', 'id': 'curseNet', 'noPass': false, 'trigger': 'passNet'}; // 咒网
|
||||
|
||||
if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
|
||||
|
||||
// autotile: 20
|
||||
@ -229,7 +226,7 @@ maps.prototype.getBlock = function (x, y, id) {
|
||||
if (id == 256) tmp.event = {'cls': 'enemys', 'id': 'poisonZombie'};
|
||||
if (id == 257) tmp.event = {'cls': 'enemys', 'id': 'magicDragon'};
|
||||
if (id == 258) tmp.event = {'cls': 'enemys', 'id': 'octopus'};
|
||||
if (id == 259) tmp.event = {'cls': 'enemys', 'id': 'fairy'};
|
||||
if (id == 259) tmp.event = {'cls': 'enemys', 'id': 'darkFairy'};
|
||||
if (id == 260) tmp.event = {'cls': 'enemys', 'id': 'greenKnight'};
|
||||
|
||||
return tmp;
|
||||
|
||||
41
libs/ui.js
41
libs/ui.js
@ -302,6 +302,30 @@ ui.prototype.drawConfirmBox = function (text, yesCallback, noCallback) {
|
||||
core.fillText('ui', "取消", 208 + 38, top + bottom - 35);
|
||||
}
|
||||
|
||||
////// 绘制开关界面 //////
|
||||
ui.prototype.drawSwitchs = function() {
|
||||
// 背景音乐、背景音效、战斗动画、怪物显伤、领域显伤、返回
|
||||
|
||||
core.status.event.id = 'switchs';
|
||||
|
||||
var background = core.canvas.ui.createPattern(core.material.ground, "repeat");
|
||||
core.clearMap('ui', 0, 0, 416, 416);
|
||||
core.setAlpha('ui', 1);
|
||||
core.setFillStyle('ui', background);
|
||||
var left = 97, top = 64 + 32, right = 416 - 2 * left, 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', "背景音乐: " + (core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), 208, top + 56, "#FFFFFF", "bold 17px Verdana");
|
||||
// core.fillText('ui', "背景音效" + (core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), 208, top + 88, "#FFFFFF", "bold 17px Verdana")
|
||||
core.fillText('ui', "战斗动画: " + (core.flags.battleAnimate ? "[ON]" : "[OFF]"), 208, top + 88, "#FFFFFF", "bold 17px Verdana")
|
||||
core.fillText('ui', "怪物显伤: " + (core.flags.displayEnemyDamage ? "[ON]" : "[OFF]"), 208, top + 120, "#FFFFFF", "bold 17px Verdana")
|
||||
core.fillText('ui', "领域显伤: " + (core.flags.displayExtraDamage ? "[ON]" : "[OFF]"), 208, top + 152, "#FFFFFF", "bold 17px Verdana")
|
||||
|
||||
core.fillText('ui', "返回上级菜单", 208, top + 184, "#FFFFFF", "bold 17px Verdana");
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制菜单栏
|
||||
* @param need
|
||||
@ -319,8 +343,8 @@ ui.prototype.drawSettings = function (need) {
|
||||
core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2);
|
||||
|
||||
core.canvas.ui.textAlign = "center";
|
||||
core.fillText('ui', "音乐: " + (core.musicStatus.soundStatus ? "[ON]" : "[OFF]"), 208, top + 56, "#FFFFFF", "bold 17px Verdana");
|
||||
core.fillText('ui', '战斗过程: ' +(core.flags.battleAnimate?'[ON]':'[OFF]'), 208, top + 88, "#FFFFFF", "bold 17px Verdana")
|
||||
core.fillText('ui', "系统设置", 208, top + 56, "#FFFFFF", "bold 17px Verdana");
|
||||
core.fillText('ui', "降低难度", 208, top + 88, "#FFFFFF", "bold 17px Verdana")
|
||||
core.fillText('ui', "快捷商店", 208, top + 120, "#FFFFFF", "bold 17px Verdana");
|
||||
core.fillText('ui', "同步存档", 208, top + 152, "#FFFFFF", "bold 17px Verdana");
|
||||
// core.fillText('ui', "清空存档", 208, top + 152, "#FFFFFF", "bold 17px Verdana");
|
||||
@ -358,8 +382,7 @@ ui.prototype.drawQuickShop = function (need) {
|
||||
core.fillText('ui', shopList[keys[i]].textInList, 208, top + 56 + 32 * i, "#FFFFFF", "bold 17px Verdana");
|
||||
}
|
||||
|
||||
core.fillText('ui', "返回游戏", 208, top + bottom - 40);
|
||||
|
||||
core.fillText('ui', "返回游戏", 208, top + bottom - 40, "#FFFFFF", "bold 17px Verdana");
|
||||
}
|
||||
|
||||
ui.prototype.drawBattleAnimate = function(monsterId, callback) {
|
||||
@ -395,10 +418,15 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) {
|
||||
if (core.enemys.hasSpecial(mon_special, 6)) turns=5;
|
||||
|
||||
|
||||
// 初始伤害(破甲、净化)
|
||||
// 初始伤害
|
||||
var initDamage = 0;
|
||||
if (core.enemys.hasSpecial(mon_special, 7)) initDamage+=parseInt(core.values.breakArmor * hero_def);
|
||||
if (core.enemys.hasSpecial(mon_special, 9)) initDamage=parseInt(core.values.purify * hero_mdef);
|
||||
if (core.enemys.hasSpecial(mon_special, 9)) initDamage+=parseInt(core.values.purify * hero_mdef);
|
||||
if (core.enemys.hasSpecial(mon_special, 11)) { // 吸血
|
||||
var extraDamage = monster.value * hero_hp;
|
||||
initDamage+=parseInt(extraDamage);
|
||||
}
|
||||
if (core.enemys.hasSpecial(mon_special, 17)) initDamage+=core.getFlag('hatred', 0);
|
||||
hero_mdef-=initDamage;
|
||||
if (hero_mdef<0) {
|
||||
hero_hp+=hero_mdef;
|
||||
@ -425,6 +453,7 @@ ui.prototype.drawBattleAnimate = function(monsterId, callback) {
|
||||
core.fillRect('ui', left, top, right, bottom, '#000000');
|
||||
core.setAlpha('ui', 1);
|
||||
core.strokeRect('ui', left - 1, top - 1, right + 1, bottom + 1, '#FFFFFF', 2);
|
||||
core.clearMap('data',0,0,416,416);
|
||||
core.setAlpha('data', 1);
|
||||
core.setOpacity('data', 1);
|
||||
core.status.boxAnimateObjs = [];
|
||||
|
||||
BIN
常用工具/Jint.dll
Normal file
BIN
常用工具/Jint.dll
Normal file
Binary file not shown.
BIN
常用工具/地图生成器.exe
BIN
常用工具/地图生成器.exe
Binary file not shown.
Loading…
Reference in New Issue
Block a user