Ver 1.2
This commit is contained in:
parent
38ac69c26c
commit
9d07b2ba22
11
README.md
11
README.md
@ -12,6 +12,17 @@ HTML5 canvas制作的魔塔样板,支持全平台游戏!
|
|||||||
|
|
||||||
## 更新说明
|
## 更新说明
|
||||||
|
|
||||||
|
### 2017.12.20
|
||||||
|
* [x] 新增:本地HTTP服务器。
|
||||||
|
* [x] 新增:可视化地图编辑工具。
|
||||||
|
* [x] 新增:便捷PS工具。
|
||||||
|
* [x] 新增:对Autotile图块的支持。
|
||||||
|
* [x] 新增:怪物支持多种属性;添加仇恨属性。
|
||||||
|
* [x] 移除了不再支持的checkBlock,现在对于领域和夹击无需再手动指定可能的点了。
|
||||||
|
* [x] 新增:单向箭头、感叹号(单次通行)的支持。
|
||||||
|
* [x] 新增:更多的默认素材,现在对于大多数地图风格无需P图,直接替换即可。
|
||||||
|
* [x] 部分细节优化,一些已知的Bug进行了修复。
|
||||||
|
|
||||||
### 2017.12.16
|
### 2017.12.16
|
||||||
|
|
||||||
* [x] 新增:战斗过程显示,可以在设置中关闭
|
* [x] 新增:战斗过程显示,可以在设置中关闭
|
||||||
|
|||||||
@ -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()
|
|
||||||
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
如需修改某个道具的效果,在不同区域宝石数据发生变化等问题,请参见[自定义道具效果](personalization#自定义道具效果)的说明。
|
如需修改某个道具的效果,在不同区域宝石数据发生变化等问题,请参见[自定义道具效果](personalization#自定义道具效果)的说明。
|
||||||
|
|
||||||
|
**有关轻按,在data.js的系统变量中有定义。如果`enableGentleClick`为true,则鼠标(触摸屏)通过双击勇士,键盘通过空格可达到轻按效果,即不向前移动而获得前方物品。**
|
||||||
|
|
||||||
## 门
|
## 门
|
||||||
|
|
||||||
本塔支持6种门,黄蓝红绿铁花。前五种门需要有对应的钥匙打开,花门只能通过调用`openDoor`事件进行打开。
|
本塔支持6种门,黄蓝红绿铁花。前五种门需要有对应的钥匙打开,花门只能通过调用`openDoor`事件进行打开。
|
||||||
@ -31,9 +33,41 @@
|
|||||||
本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。
|
本塔支持的怪物列表参见`enemys.js`。其与images目录下的`enemys.png`素材按顺序一一对应。如不知道怪物素材长啥样的请打开`enemys.png`对比查看。
|
||||||
如有自己的怪物素材需求请参见[自定义素材](personalization#自定义素材)的内容。
|
如有自己的怪物素材需求请参见[自定义素材](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`函数中,如有自己需求的伤害计算公式请修改该函数的代码。
|
怪物的伤害计算在下面的`calDamage`函数中,如有自己需求的伤害计算公式请修改该函数的代码。
|
||||||
|
|
||||||
@ -46,6 +80,7 @@
|
|||||||

|

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

|

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

|

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

|
本塔暂不支持阻击、激光、自爆、退化等属性。
|
||||||
|
|
||||||
!> **请注意这里的`"x,y"`代表该点的横坐标为x,纵坐标为y;即从左到右第x列,从上到下的第y行(从0开始计算)。**
|
|
||||||
|
|
||||||
本塔不支持阻击、激光、仇恨、自爆、退化等属性。
|
|
||||||
|
|
||||||
(这些属性基本都太恶心了,恶心到都不想加上去)
|
|
||||||
|
|
||||||
如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。
|
如有额外需求,可参见[自定义怪物属性](personalization#自定义自定义怪物属性),里面讲了如何设置一个新的怪物属性。
|
||||||
|
|
||||||
|
|||||||
@ -426,6 +426,8 @@ direction为可选的,指定的话将使勇士的朝向变成该方向
|
|||||||
|
|
||||||
time为可选的,指定的话将作为楼层切换动画的时间。
|
time为可选的,指定的话将作为楼层切换动画的时间。
|
||||||
|
|
||||||
|
!> **changeFloor到达一个新的楼层,将不会执行firstArrive事件!如有需求请在到达点设置自定义事件,然后使用type: trigger立刻调用之。**
|
||||||
|
|
||||||
### changePos: 当前位置切换/勇士转向
|
### changePos: 当前位置切换/勇士转向
|
||||||
|
|
||||||
有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。
|
有时候我们不想要楼层切换的动画效果,而是直接让勇士从A点到B点。
|
||||||
@ -461,7 +463,7 @@ time为可选的,指定的话将作为楼层切换动画的时间。
|
|||||||
```
|
```
|
||||||
|
|
||||||
color为需要更改画面色调的颜色。它是一个三元数组,分别指定目标颜色的R,G,B值。
|
color为需要更改画面色调的颜色。它是一个三元数组,分别指定目标颜色的R,G,B值。
|
||||||
- 常见颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,255,0],等等。
|
- 常见颜色: 纯黑[0,0,0],纯白[255,255,255],纯红[255,0,0],等等。
|
||||||
|
|
||||||
如果color不指定则恢复原样。
|
如果color不指定则恢复原样。
|
||||||
|
|
||||||
@ -475,7 +477,7 @@ time为可选的,如果指定,则会作为更改画面色调的时间。
|
|||||||
|
|
||||||
``` js
|
``` js
|
||||||
"x,y": [ // 实际执行的事件列表
|
"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步,在向下移动一步并消失
|
{"direction": "right", "value": 2},// 这里steps 的效果为向右移动2步,在向下移动一步并消失
|
||||||
"down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1
|
"down" // 如果该方向上只移动一步则可以这样简写,效果等价于上面value为1
|
||||||
], "immediateHide": true }, //immediateHide可选,制定为true则立刻消失,否则渐变消失
|
], "immediateHide": true }, //immediateHide可选,制定为true则立刻消失,否则渐变消失
|
||||||
@ -484,13 +486,15 @@ time为可选的,如果指定,则会作为更改画面色调的时间。
|
|||||||
|
|
||||||
time选项必须指定,为每移动一步所需要用到的时间。
|
time选项必须指定,为每移动一步所需要用到的时间。
|
||||||
|
|
||||||
|
loc为需要移动的事件位置。可以省略,如果省略则移动本事件。
|
||||||
|
|
||||||
steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。
|
steps为一个数组,其每一项为一个 `{"direction" : xxx, "value": n}`,表示该步是向xxx方向移动n步。
|
||||||
|
|
||||||
如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。
|
如果只移动一步可以直接简单的写方向字符串(`up/left/down/right`)。
|
||||||
|
|
||||||
immediateHide为一个可选项,代表该事件移动完毕后是否立刻消失。如果该项指定了并为true,则移动完毕后直接消失,否则以动画效果消失。
|
immediateHide为一个可选项,代表该事件移动完毕后是否立刻消失。如果该项指定了并为true,则移动完毕后直接消失,否则以动画效果消失。
|
||||||
|
|
||||||
值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用hide事件。
|
值得注意的是,当调用move事件时,实际上是使事件脱离了原始地点。为了避免冲突,规定:move事件会自动调用该点的hide事件。
|
||||||
|
|
||||||
换句话说,当move事件被调用后,该点本身的事件将被禁用。
|
换句话说,当move事件被调用后,该点本身的事件将被禁用。
|
||||||
|
|
||||||
@ -516,6 +520,25 @@ move完毕后移动的NPC/怪物一定会消失,只不过可以通过immediate
|
|||||||
|
|
||||||
即,在移动的到达点指定一个初始禁用的相同NPC,然后move事件中指定immediateHide使立刻消失,并show该到达点坐标使其立刻显示(看起来就像没有消失),然后就可以触发目标点的事件了。
|
即,在移动的到达点指定一个初始禁用的相同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事件几乎完全相同,除了少了immediateHide选项。
|
||||||
|
|
||||||
|
不过值得注意的是,用这种方式移动勇士的过程中将无视一切地形,无视一切事件,中毒状态也不会扣血。
|
||||||
|
|
||||||
### playSound: 播放音效
|
### playSound: 播放音效
|
||||||
|
|
||||||
使用playSound可以立刻播放一个音效。
|
使用playSound可以立刻播放一个音效。
|
||||||
|
|||||||
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,7 +6,8 @@
|
|||||||
|
|
||||||
所有素材的图片都在`images`目录下。
|
所有素材的图片都在`images`目录下。
|
||||||
- `animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。
|
- `animates.png` 为所有动画效果。主要是星空熔岩,开门,毒网,传送门之类的效果。为四帧。
|
||||||
- `enemys.png` 为所有怪物的图片。地图生成器中对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可)
|
- `autotile.png` 为Autotile块。全塔只支持一种Autotile,其ID为20。
|
||||||
|
- `enemys.png` 为所有怪物的图片。其对应的数字,从上至下依次是会从201开始计算(即,绿色史莱姆为201,小蝙蝠为205,依次类推)。请注意,动画效果为两帧,一般是原始四帧中的1和3。(四帧中12相同,34相同,因此只取1和3即可)
|
||||||
- `heros.png` 为勇士行走图。
|
- `heros.png` 为勇士行走图。
|
||||||
- `items.png` 为所有道具的图标。
|
- `items.png` 为所有道具的图标。
|
||||||
- `npcs.png` 为所有NPC的图标,也是两帧。
|
- `npcs.png` 为所有NPC的图标,也是两帧。
|
||||||
@ -14,6 +15,86 @@
|
|||||||
|
|
||||||
系统会读取`icon.js`文件,并获取每个ID对应的图标所在的位置。
|
系统会读取`icon.js`文件,并获取每个ID对应的图标所在的位置。
|
||||||
|
|
||||||
|
### 使用预定义的素材
|
||||||
|
|
||||||
|
在images目录的“默认素材”下给定了若干预定义的自定义素材。包括野外(草地),星空,木板等等都已经被预先给定。
|
||||||
|
|
||||||
|
如果你需要某个素材已经存在,则可以直接将其覆盖images目录下的同名文件,就能看到效果。
|
||||||
|
|
||||||
|
### 使用便捷PS工具生成素材
|
||||||
|
|
||||||
|
如果我们有更多的素材要求,我们可以使用“便捷PS工具”进行处理。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们可以打开有需求改变的素材,和我们需要被替换的素材,然后简单的Ctrl+C和Ctrl+V操作即可。
|
||||||
|
|
||||||
|
用这种方式,我们能极快地替换或素材,包括需要新增的怪物。
|
||||||
|
|
||||||
|
### 添加素材到游戏
|
||||||
|
|
||||||
|
在使用地图编辑器编辑的过程中,我们有可能会出现“该数字和ID未被定义”的错误提示。
|
||||||
|
|
||||||
|
这是因为,该素材没有被定义,无法被游戏所识别。
|
||||||
|
|
||||||
|
#### 新添加自定义地形(路面、墙壁等)
|
||||||
|
|
||||||
|
如果你在terrains.png中新增了一行:
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 新添加道具
|
||||||
|
|
||||||
|
如果你需要新增一个未被定义的道具:
|
||||||
|
|
||||||
|
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帧**。
|
||||||
|
|
||||||
|
然后执行如下操作:
|
||||||
|
|
||||||
|
1. 指定一个唯一的英文ID,不能和enemys中现有的重复。
|
||||||
|
2. 进入icons.js,在enemys分类下进行添加(对应图标在图片上的位置,即index)
|
||||||
|
3. 在maps.js的getBlock下继续进行添加。请注意其ID为200开始的顺序,即如果新增一行为261,依次类推
|
||||||
|
4. 在items.js中仿照其他道具,来添加道具的信息。
|
||||||
|
|
||||||
|
``` 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指定ID,在icons.js中指定ID-index关系,在maps.js中指定ID-数字的关系,即可。
|
||||||
|
|
||||||
### 地图生成器使用自定义素材
|
### 地图生成器使用自定义素材
|
||||||
|
|
||||||
地图生成器可以将数字和图标一一对应,从数字生成图标或从图标生成数字。
|
地图生成器可以将数字和图标一一对应,从数字生成图标或从图标生成数字。
|
||||||
@ -24,82 +105,6 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 使用自定义地形(路面、墙壁等)
|
|
||||||
|
|
||||||
你需要自行P一张图,里面是你需要的地形素材。然后将其覆盖`terrains.png`文件,请注意所有元件的位置需要完全相同对应。
|
|
||||||
|
|
||||||
岩浆、星空、传送门、箭头、门的开启动画、暗墙的开启动画在`animates.png`中,可能也需要对应进行修改。
|
|
||||||
|
|
||||||
请打开`items.js`中,对比素材的位置。
|
|
||||||
|
|
||||||
另外需要注意的一点是,本样板不支持`Autotile`,换句话说野外地图(草地等)这种自然风可能不会太适合。
|
|
||||||
|
|
||||||
### 使用自定义道具图标
|
|
||||||
|
|
||||||
如果你觉得已有的道具图标不够,想自己添加图标也是可以的。
|
|
||||||
|
|
||||||
如果`items.png`中不存在你需要的图标,则可以自己P一张图,将你需要的图标覆盖到某个用不到的图标上。或者也可以接着后面向下拉伸。
|
|
||||||
|
|
||||||
所有道具必须是`32x32`像素。
|
|
||||||
|
|
||||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
|
||||||
|
|
||||||
要在系统中启用你的图标,你需要自己指定一个道具的ID(不能和任何已有的重名),然后进行如下操作:
|
|
||||||
1. 在`items.js`中道具的定义列表中编辑,修改你的自定义道具的名称,类型,说明文字等。
|
|
||||||
- 即捡即用类道具的cls为items,消耗类道具的cls为tools,永久类道具的cls为constants。
|
|
||||||
2. 在`icon.js`中,找到items一栏,往里面添加你的图标位置。
|
|
||||||
3. 在`maps.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'} // 圣锤
|
|
||||||
// 可以在这里添加自己的数字-道具对应关系
|
|
||||||
```
|
|
||||||
|
|
||||||
有关如何自行实现一个道具的效果,参见[自定义道具效果](#自定义道具效果)。
|
|
||||||
|
|
||||||
### 使用自定义怪物图标
|
|
||||||
|
|
||||||
如果你觉得已有的怪物图标不够,也可以自行向后添加你需要的怪物。
|
|
||||||
|
|
||||||
在`enemys.js`中,直接向后P图,并将你需要的怪物的两帧动画放到正确的位置(注意:普通四帧动画的1和3帧)。
|
|
||||||
|
|
||||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
|
||||||
|
|
||||||
要在系统中启用你的图标,你需要自己指定一个怪物的ID(不能和任何已有的重名),然后进行如下操作:
|
|
||||||
|
|
||||||
1. 在`enemys.js`中,向怪物的定义列表中,添加一个怪物。
|
|
||||||
2. 在`icon.js`中,找到enemys一栏,往里面添加你的图标位置。
|
|
||||||
3. 在`maps.js`中,找到对应位置,往里面添加自己的数字和怪物的一一对应关系。一般对于怪物而言,对应的数字就是`200+`位置。
|
|
||||||
|
|
||||||
有关如何自行实现一个怪物的特殊属性或伤害计算公式,参见[怪物的特殊属性](#怪物的特殊属性)。
|
|
||||||
|
|
||||||
### 使用自定义NPC图标
|
|
||||||
|
|
||||||
同上,你也可以自定义NPC图标。只需要将你NPC的两帧动画接到`npcs.js`中即可。
|
|
||||||
|
|
||||||
P图完毕后,可以在地图生成器中加入对应的数字和图标的对应关系。
|
|
||||||
|
|
||||||
要在系统中启用你的图标,你需要自己指定一个NPC的ID(不能和任何已有的重名),然后进行如下操作:
|
|
||||||
|
|
||||||
1. 在`icon.js`中,找到npcs一栏,往里面添加你的图标位置。
|
|
||||||
2. 在`maps.js`中,找到对应位置,往里面添加自己的数字和NPC的一一对应关系。
|
|
||||||
|
|
||||||
### 使用自定义勇士图标
|
|
||||||
|
|
||||||
我们同样可以使用自定义的勇士图标,比如小可绒之类。
|
|
||||||
|
|
||||||
直接拿一个勇士的行走图覆盖`hero.png`即可。
|
|
||||||
|
|
||||||
**支持任何行走大图,如`32x32`, `48x32`, `64x32`等等。**
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
通过上述这几种方式,我们可以修改素材图片(使用自定义素材),指定数字并放入地图生成器中,然后在系统中进行启用。
|
|
||||||
|
|
||||||
!> **请注意:除了勇士行走图外,其他所有素材强制要求必须是`32x32`的,不然可能会造成不可预料的后果。**
|
|
||||||
|
|
||||||
## 自定义道具效果
|
## 自定义道具效果
|
||||||
|
|
||||||
本节中将继续介绍如何自己编辑一个道具的效果。
|
本节中将继续介绍如何自己编辑一个道具的效果。
|
||||||
@ -167,24 +172,26 @@ if (itemId === 'shield5') {
|
|||||||
``` js
|
``` js
|
||||||
enemys.prototype.getExtraDamage = function (monster) {
|
enemys.prototype.getExtraDamage = function (monster) {
|
||||||
var extra_damage = 0;
|
var extra_damage = 0;
|
||||||
if (monster.special == 11) { // 吸血
|
if (this.hasSpecial(monster.special, 11)) { // 吸血
|
||||||
// 吸血的比例
|
// 吸血的比例
|
||||||
extra_damage = core.status.hero.hp * monster.value;
|
extra_damage = core.status.hero.hp * monster.value;
|
||||||
if (core.hasFlag("shield5")) extra_damage = 0; // 如果存在神圣盾,则免疫吸血
|
if (core.hasFlag("shield5")) extra_damage = 0; // 如果存在神圣盾,则免疫吸血
|
||||||
extra_damage = parseInt(extra_damage);
|
extra_damage = parseInt(extra_damage);
|
||||||
}
|
}
|
||||||
return extra_damage;
|
// ... 下略
|
||||||
}
|
|
||||||
```
|
```
|
||||||
3. 免疫领域、夹击效果:在`events.js`中,找到checkBlock函数,并编辑成如果有神圣盾标记,则直接返回。
|
3. 免疫领域、夹击效果:在`core.js`中,找到updateCheckBlock函数,并编辑成如果有神圣盾标记,则直接返回。
|
||||||
``` js
|
``` js
|
||||||
////// 检查领域、夹击事件 //////
|
// 更新领域、显伤点
|
||||||
events.prototype.checkBlock = function (x,y) {
|
core.prototype.updateCheckBlock = function() {
|
||||||
if (core.hasFlag("shield5")) return; // 如果拥有神圣盾立刻返回,免疫领域和夹击
|
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;
|
var damage = 0;
|
||||||
// 获得四个方向的怪物
|
|
||||||
var directions = [[0,-1],[-1,0],[0,1],[1,0]]; // 上,左,下,右
|
|
||||||
var enemys = [null,null,null,null];
|
|
||||||
// ... 下略
|
// ... 下略
|
||||||
```
|
```
|
||||||
4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。
|
4. 如果有更高的需求,例如想让吸血效果变成一半(如异空间),则还是在上面这些地方进行对应的修改即可。
|
||||||
@ -199,15 +206,28 @@ events.prototype.checkBlock = function (x,y) {
|
|||||||
|
|
||||||
因此无敌属性可以这样设置:
|
因此无敌属性可以这样设置:
|
||||||
|
|
||||||
``` js
|
先给无敌属性指定一个数字,例如18,在getSpecialText中定义
|
||||||
// 我们需要对无敌属性指定一个数字,比如18
|
|
||||||
|
|
||||||
|
``` 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) {
|
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; // 返回无限大
|
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。
|
- 任一款现代浏览器。强烈推荐Chrome。
|
||||||
- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。
|
- 一个很好的文本编辑器。推荐带有高亮染色、错误提示等效果。例如:WebStorm,VSCode,或者至少也要Sublime Text。
|
||||||
([VSCode下载地址](https://code.visualstudio.com/),群里的群文件中也有,强烈推荐之。)
|
([VSCode下载地址](https://code.visualstudio.com/),群里的群文件中也有,强烈推荐之。)
|
||||||
- RPG Maker XP,任一个魔塔样板(推荐魔塔样板7630)
|
|
||||||
|
|
||||||
只要满足了上述条件,你就可以开始做自己的塔啦!
|
只要满足了上述条件,你就可以开始做自己的塔啦!
|
||||||
|
|
||||||
|
## 启动HTTP服务
|
||||||
|
|
||||||
|
在根目录下有一个“启动服务.exe”,运行之。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* “启动游戏”按钮将打开一个网页,你能在里面看到现在游戏的效果。
|
||||||
|
* “地图编辑器”允许你以可视化的方式进行编辑地图。
|
||||||
|
* “便捷PS工具”能让你很方便的对自定义素材进行添加。参见[自定义素材](personalization#自定义素材)。
|
||||||
|
* “地图生成器”能让你从已有的截图(如RMXP项目)中立刻生成可被本样板识别的地图数据。
|
||||||
|
* “JS代码压缩工具”能对JS代码进行压缩,从而减少IO请求数和文件大小。
|
||||||
|
* “伤害和临界值计算器”是一个很便捷的小工具,能对怪物的伤害和临界值进行计算。
|
||||||
|
|
||||||
## 新建剧本
|
## 新建剧本
|
||||||
|
|
||||||
类似于RMXP,本塔每层楼都是一个“剧本”,剧本内主要定义了本层的地图和各种事件。主函数将读取每个剧本,并生成实际的地图供游戏使用。
|
类似于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)。**
|
截图时请注意:**只截取有效游戏空间内数据,并且有效空间内的范围必须是13x13。(如果地图小于13*13,请用星空或墙壁填充到13x13)。**
|
||||||
|
|
||||||
确认地图的图片文件已经复制到剪切板后,我们打开工具中的“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。
|
确认地图的图片文件已经复制到剪切板后,我们打开“地图生成器”,并点“加载图片”。大约1-2秒后,可以得到地图的数据。
|
||||||
|
|
||||||

|

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

|
|
||||||
|
|
||||||
通过这种在RMXP中画图,截图复制,再用地图生成器识别的方式,我们成功将我们需要的地图变成了样板可识别的格式。
|
|
||||||
|
|
||||||
## 录入数据
|
## 录入数据
|
||||||
|
|
||||||
|
|||||||
549
drawMapGUI.html
549
drawMapGUI.html
@ -4,67 +4,176 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<style>
|
<style>
|
||||||
html,body,div,img{margin:0;padding:0;}
|
html,body,div,img{margin:0;padding:0;}
|
||||||
|
body{
|
||||||
|
font-family:Roboto,Helvetica,Arial,sans-serif;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
.main {
|
.main {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
margin: 0 auto;
|
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{
|
#left{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
left: 5px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
width: 416px;
|
width: 430px;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
border: 1px solid rgb(238, 13, 13);
|
/* border: 1px solid rgb(238, 13, 13); */
|
||||||
}
|
}
|
||||||
#left #pout{
|
|
||||||
position: absolute;
|
#left span{
|
||||||
display: block;
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
#left .col{
|
||||||
|
top: 2px;
|
||||||
|
left: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
#left .row{
|
||||||
|
width: 10px;
|
||||||
|
left: 0px;
|
||||||
|
top: 25px;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#editArea{
|
||||||
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 70%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
/* padding: 10px 5px; */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#pout{
|
||||||
|
display: block;
|
||||||
|
width: 408px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 10px;
|
box-sizing: border-box;
|
||||||
top: 10px;
|
margin-left: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
#editTip{
|
||||||
|
position: absolute;
|
||||||
|
width: 50%;
|
||||||
|
height: 80px;
|
||||||
|
bottom:10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
#editTip p{
|
||||||
|
margin: 10px 0;
|
||||||
|
/* display: block; */
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
#editTip .btn{
|
||||||
|
margin-left: 60%;
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
#mid{
|
#mid{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 448px;
|
left: 448px;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 416px;
|
width: 416px;
|
||||||
height: 600px;
|
height: 620px;
|
||||||
border: 1px solid rgb(238, 13, 13);
|
/* border: 1px solid rgb(238, 13, 13); */
|
||||||
}
|
}
|
||||||
#mid .tools{
|
#mid .tools{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 400px;
|
||||||
height: 200px;
|
height: 195px;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 448px;
|
top: 425px;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
padding: 10px 5px;
|
||||||
|
margin-left: 8px;;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
#tip{
|
||||||
|
float: right;
|
||||||
|
width: 50%;
|
||||||
|
height: 95%;
|
||||||
|
padding: 10px;
|
||||||
|
margin-right: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||||
|
}
|
||||||
|
#printOut{
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.btn {
|
.btn {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 30px;
|
border-radius: 2px;
|
||||||
margin: 10px;
|
line-height: 30px;
|
||||||
border-radius: 3px;
|
margin: 0;
|
||||||
/* background-color: green; */
|
min-width: 50px;
|
||||||
|
padding: 0 5px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
font-size: 11px;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
.input{
|
.btn:hover {
|
||||||
width: 100px;
|
background-color: #009688;
|
||||||
margin: 10px;
|
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{
|
#right{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 896px;
|
left: 896px;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 450px;
|
width: 450px;
|
||||||
height: 600px;
|
height: 620px;
|
||||||
border: 1px solid rgb(238, 13, 13);
|
/* border: 1px solid rgb(238, 13, 13); */
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
}
|
}
|
||||||
.gameCanvas {
|
.gameCanvas {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
#dataSelection{
|
#dataSelection{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top:0;
|
top:0;
|
||||||
@ -74,19 +183,43 @@
|
|||||||
|
|
||||||
margin:-2px 0 0 -2px;
|
margin:-2px 0 0 -2px;
|
||||||
padding:0;
|
padding:0;
|
||||||
|
/* display: none; */
|
||||||
|
|
||||||
background-color:rgba(255, 255, 255, 0.0);
|
background-color:rgba(255, 255, 255, 0.0);
|
||||||
border: 2px solid rgb(87, 198, 232);
|
border: 2px solid rgb(87, 198, 232);
|
||||||
box-shadow: 0px 0px 2px rgb(87, 198, 232);
|
box-shadow: 0px 0px 2px rgb(87, 198, 232);
|
||||||
}
|
}
|
||||||
|
.warnText{
|
||||||
|
color: #D50000;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.infoText{
|
||||||
|
color: #2196F3;
|
||||||
|
}
|
||||||
|
.successText{
|
||||||
|
color: #00897B
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for vue dom */
|
||||||
|
[v-cloak] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div id="left">
|
<div id="left">
|
||||||
|
<div id="editArea">
|
||||||
|
<span class="col" id='numMark'></span>
|
||||||
|
<span class="row">0 1 2 3 4 5 6 7 8 9 10 11 12</span>
|
||||||
|
<textarea cols="10" rows="10" id="pout" v-model="mapArr"></textarea>
|
||||||
|
<p class="warnText" v-cloak>{{ errormsg }}</p>
|
||||||
|
</div>
|
||||||
|
<div id="editTip" v-cloak>
|
||||||
|
<input class='btn' type="button" value="copyMap" v-on:click="copyMap"/>
|
||||||
|
<!-- <p v-bind:class="[ hasError ? 'warnText' : 'infoText']" v-show="isShow">{{ message }}</p> -->
|
||||||
|
|
||||||
<p id='pout'>可以在console中通过printf(str)来改变这里的值</p>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="mid">
|
<div id="mid">
|
||||||
@ -97,16 +230,36 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tools">
|
<div class="tools">
|
||||||
<input class='btn' type="button" value="exportMap" onclick="exportMap()" style="top:600px;left:50px"/>
|
<div id="tip" v-cloak >
|
||||||
<input class='btn' type="button" value="read" onclick="readUTF8file(pin.value)" style="top:600px;left:150px"/>
|
<div v-if="isSelectedBlock" >
|
||||||
<input class='btn' type="button" value="write" onclick="writeUTF8file(pin.value,pout.innerText)" style="top:600px;left:200px"/>
|
<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="clearMap" v-on:click="clearMap"/>
|
||||||
|
<input class='btn' type="button" value="exportMap" 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>
|
</div>
|
||||||
<div id="right">
|
<div id="right">
|
||||||
<canvas class='gameCanvas' id='data' width='416' height='416' style='z-index:0'></canvas>
|
<canvas class='gameCanvas' id='data' width='416' height='416' style='z-index:0'></canvas>
|
||||||
<div id='dataSelection'></div>
|
<div id="selectBox">
|
||||||
|
<div id='dataSelection' v-show="isSelected"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -114,35 +267,48 @@
|
|||||||
<script>main={'instance':{}}</script>
|
<script>main={'instance':{}}</script>
|
||||||
<script src='libs/icons.js'></script>
|
<script src='libs/icons.js'></script>
|
||||||
<script src='libs/maps.js'></script>
|
<script src='libs/maps.js'></script>
|
||||||
|
<!-- <script src='ibs/vendor/vue.min.js'></script> -->
|
||||||
|
<script src="https://unpkg.com/vue"></script>
|
||||||
<script>
|
<script>
|
||||||
main.instance.icons.init();//不知道为什么,需要手动init,明明maps是正常的
|
main.instance.icons.init();//不知道为什么,需要手动init,明明maps是正常的
|
||||||
icons=main.instance.icons.getIcons();
|
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++){
|
var point = 0
|
||||||
if('event' in ids[ii]){
|
for(var i=0; i<400; i++){
|
||||||
var id =ids[ii].event.id;
|
var indexBlock = main.instance.maps.getBlock(0,0,i);
|
||||||
var cls =ids[ii].event.cls;
|
indexs[i] = [];
|
||||||
if(id=='autotile'){ids[ii]={'idnum':ii,'id':'autotile','images':'autotile','y':0};continue;}
|
if('event' in indexBlock){
|
||||||
if (id in icons[cls])ids[ii]={'idnum':ii,'id':id,'images':cls,'y':icons[cls][id]};
|
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);
|
var str = ' ';
|
||||||
|
for(var i=0; i<13; i++){
|
||||||
|
var strNum = String(i);
|
||||||
|
str += Array(8-strNum.length).join(' ') + strNum;
|
||||||
|
}
|
||||||
|
numMark.innerHTML = str;
|
||||||
|
|
||||||
// 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);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@ -299,10 +465,9 @@
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
updateMap = function () {
|
updateMap = function () {
|
||||||
//clearGrass();
|
//clearGrass();
|
||||||
|
// console.log(map)
|
||||||
for (var xx = 0; xx <= fullX; xx++) {
|
for (var xx = 0; xx <= fullX; xx++) {
|
||||||
for (var yy = 0; yy <= fullY; yy++) {
|
for (var yy = 0; yy <= fullY; yy++) {
|
||||||
if (!isGrass(xx, yy)) continue;
|
if (!isGrass(xx, yy)) continue;
|
||||||
@ -341,7 +506,12 @@
|
|||||||
for (var yy = 0; yy <= fullY; yy++) {
|
for (var yy = 0; yy <= fullY; yy++) {
|
||||||
if (isGrass(xx, yy)) continue;
|
if (isGrass(xx, yy)) continue;
|
||||||
var mapxy=map[m(xx,yy)];
|
var mapxy=map[m(xx,yy)];
|
||||||
if (typeof(mapxy) == typeof(-1) || typeof(mapxy) == typeof([][0]))continue;
|
if (typeof(mapxy) == typeof(-1))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)
|
//context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
|
||||||
cxt.clearRect(xx*32, yy*32, 32, 32);
|
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);
|
cxt.drawImage(core.material.images[mapxy.images], 0, mapxy.y*32, 32, 32, xx*32, yy*32, 32, 32);
|
||||||
@ -359,7 +529,7 @@
|
|||||||
var prefix='<span class="result">',postfix='</span>';
|
var prefix='<span class="result">',postfix='</span>';
|
||||||
if (weak){prefix='<span class="weakresult">';}
|
if (weak){prefix='<span class="weakresult">';}
|
||||||
if (typeof(str)==="undefined")str='';
|
if (typeof(str)==="undefined")str='';
|
||||||
pout.innerHTML=prefix+String(str)+postfix;
|
printOut.innerHTML=prefix+String(str)+postfix;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawInitData = function(){
|
drawInitData = function(){
|
||||||
@ -444,6 +614,11 @@
|
|||||||
}//用于鼠标移出canvas时的自动清除状态
|
}//用于鼠标移出canvas时的自动清除状态
|
||||||
|
|
||||||
ui.onmousedown = function (e) {
|
ui.onmousedown = function (e) {
|
||||||
|
if(!selectBox.isSelected) {
|
||||||
|
tip.whichShow = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
holdingPath = 1;
|
holdingPath = 1;
|
||||||
mouseOutCheck = 2;
|
mouseOutCheck = 2;
|
||||||
setTimeout(clear1);
|
setTimeout(clear1);
|
||||||
@ -457,6 +632,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui.onmousemove = function (e) {
|
ui.onmousemove = function (e) {
|
||||||
|
if(!selectBox.isSelected) {
|
||||||
|
// tip.whichShow = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (holdingPath == 0) { return; }
|
if (holdingPath == 0) { return; }
|
||||||
mouseOutCheck = 2;
|
mouseOutCheck = 2;
|
||||||
@ -482,15 +661,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
ui.onmouseup = function (e) {
|
ui.onmouseup = function (e) {
|
||||||
|
if(!selectBox.isSelected) {
|
||||||
|
tip.whichShow = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
holdingPath = 0;
|
holdingPath = 0;
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var loc = eToLoc(e);
|
var loc = eToLoc(e);
|
||||||
if (stepPostfix.length) {
|
if (stepPostfix.length) {
|
||||||
console.log(stepPostfix);
|
// console.log(stepPostfix);
|
||||||
for (var ii = 0; ii < stepPostfix.length; ii++)
|
for (var ii = 0; ii < stepPostfix.length; ii++)
|
||||||
map[m(stepPostfix[ii].x, stepPostfix[ii].y)] = info;
|
map[m(stepPostfix[ii].x, stepPostfix[ii].y)] = info;
|
||||||
map[fullX + 1 + fullY * (fullX + 1)] = -2;
|
map[fullX + 1 + fullY * (fullX + 1)] = -2;
|
||||||
console.log(map);
|
// console.log(map);
|
||||||
updateMap();
|
updateMap();
|
||||||
holdingPath = 0;
|
holdingPath = 0;
|
||||||
stepPostfix = [];
|
stepPostfix = [];
|
||||||
@ -498,7 +681,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info=ids[20];//autotile
|
// info=ids[indexs[20][0]];//autotile
|
||||||
data.onmousedown = function (e) {
|
data.onmousedown = function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
var loc = {
|
var loc = {
|
||||||
@ -507,23 +690,26 @@
|
|||||||
'size': 32
|
'size': 32
|
||||||
};
|
};
|
||||||
pos = locToPos(loc);
|
pos = locToPos(loc);
|
||||||
console.log(pos);
|
// console.log(pos);
|
||||||
for (var spriter in widthsX){
|
for (var spriter in widthsX){
|
||||||
if(pos.x>=widthsX[spriter][1] && pos.x<widthsX[spriter][2]){
|
if(pos.x>=widthsX[spriter][1] && pos.x<widthsX[spriter][2]){
|
||||||
pos.x=widthsX[spriter][1];
|
pos.x=widthsX[spriter][1];
|
||||||
pos.images=widthsX[spriter][0];
|
pos.images=widthsX[spriter][0];
|
||||||
if(pos.images=='autotile'){
|
if(pos.images=='autotile'){
|
||||||
pos.y=0;
|
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;
|
}else if((pos.y+1)*32>core.material.images[pos.images].height)
|
||||||
|
pos.y=~~(core.material.images[pos.images].height/32)-1;
|
||||||
|
|
||||||
|
selectBox.isSelected = true;
|
||||||
|
// console.log(pos,core.material.images[pos.images].height)
|
||||||
dataSelection.style.left=pos.x*32+'px';
|
dataSelection.style.left=pos.x*32+'px';
|
||||||
dataSelection.style.top=pos.y*32+'px';
|
dataSelection.style.top=pos.y*32+'px';
|
||||||
info={'images':pos.images,'y':pos.y};
|
info={'images':pos.images,'y':pos.y};
|
||||||
for (var ii=0;ii<ids.length;ii++){
|
for (var ii=0;ii<ids.length;ii++){
|
||||||
if(ids[ii]==null)continue;
|
if(pos.images==ids[ii].images && pos.y==ids[ii].y)
|
||||||
if(pos.images==ids[ii].images && pos.y==ids[ii].y)info=ids[ii];
|
info = JSON.parse(JSON.stringify(ids[ii]));
|
||||||
}
|
}
|
||||||
printf(JSON.stringify(info))
|
tip.infos = info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -592,7 +778,20 @@
|
|||||||
postsomething(JSON.stringify(data),callback);
|
postsomething(JSON.stringify(data),callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportMap(callback){
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
document.body.onmousedown = function(e){
|
||||||
|
selectBox.isSelected = false;
|
||||||
|
info = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var exportM = new Vue({
|
||||||
|
el: '#exportM',
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
exportMap: function(){
|
||||||
|
|
||||||
var filestr='';
|
var filestr='';
|
||||||
for (var yy = 0; yy < 13; yy++){
|
for (var yy = 0; yy < 13; yy++){
|
||||||
filestr+='['
|
filestr+='['
|
||||||
@ -600,18 +799,224 @@
|
|||||||
var mapxy=map[m(xx,yy)];
|
var mapxy=map[m(xx,yy)];
|
||||||
if(typeof(mapxy)==typeof({})){
|
if(typeof(mapxy)==typeof({})){
|
||||||
if ('idnum' in mapxy)mapxy=mapxy.idnum;
|
if ('idnum' in mapxy)mapxy=mapxy.idnum;
|
||||||
else mapxy='!!?';
|
else {
|
||||||
|
// mapxy='!!?';
|
||||||
|
tip.whichShow = 3;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}else if(typeof(mapxy)=='undefined'){
|
||||||
|
tip.whichShow = 3;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
mapxy=String(mapxy);
|
mapxy=String(mapxy);
|
||||||
mapxy=Array(Math.max(4-mapxy.length,0)).join(' ')+mapxy;
|
mapxy=Array(Math.max(4-mapxy.length,0)).join(' ')+mapxy;
|
||||||
filestr+=mapxy+','
|
filestr+=mapxy+(xx==12?'':',')
|
||||||
}
|
}
|
||||||
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>
|
|
||||||
|
|
||||||
|
filestr+=(']'+(yy==12?'':',')+'\n')
|
||||||
|
}
|
||||||
|
pout.value = filestr;
|
||||||
|
|
||||||
|
tip.whichShow = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
var editArea = new Vue({
|
||||||
|
el: '#editArea',
|
||||||
|
data: {
|
||||||
|
mapArr: '',
|
||||||
|
errormsg: '',
|
||||||
|
errors: [ // 编号1,2,3,4
|
||||||
|
"格式错误,请使用正确格式",
|
||||||
|
"当前ID未定义(在地图区域显示红块),请修改ID或者到icons.js和maps.js中进行定义",
|
||||||
|
"编号错误,当前编辑器暂时支持编号小于400,请修改编号!"
|
||||||
|
],
|
||||||
|
error: 0,
|
||||||
|
timer: null,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
mapArr: function () {
|
||||||
|
var self = this;
|
||||||
|
setTimeout(function(){
|
||||||
|
self.drawMap();
|
||||||
|
tip.whichShow = 8
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
error: function(){
|
||||||
|
var that = this;
|
||||||
|
that.errormsg = '';
|
||||||
|
if(that.error)
|
||||||
|
that.errormsg = that.errors[that.error-1];
|
||||||
|
|
||||||
|
clearTimeout(that.timer);
|
||||||
|
that.timer = setTimeout(function() {
|
||||||
|
tip.error = 0;
|
||||||
|
}, 5000); //5秒后自动清除
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
drawMap: function(){
|
||||||
|
var that = this;
|
||||||
|
if(that.formatArr){
|
||||||
|
var mapArray;
|
||||||
|
var arr = that.mapArr.replace(/\s/g, '');
|
||||||
|
var mapJson = '{"mapArr":['+arr+']}';
|
||||||
|
try{
|
||||||
|
mapArray = JSON.parse(mapJson).mapArr;
|
||||||
|
}catch (e) {
|
||||||
|
that.errors[3] = '发生错误!'+e;
|
||||||
|
that.error = 4;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(var y=0; y<mapArray.length; y++)
|
||||||
|
for(var x=0; x<mapArray[y].length; x++){
|
||||||
|
var num = mapArray[y][x];
|
||||||
|
if(num == 0 )
|
||||||
|
map[m(x, y)] = 0;
|
||||||
|
else if(num >= 400){
|
||||||
|
that.error = 3;
|
||||||
|
map[m(x, y)] = 'undefined';
|
||||||
|
}else if(indexs[num][0] == ''){
|
||||||
|
that.error = 2;
|
||||||
|
map[m(x, y)] = 'undefined';
|
||||||
|
}else map[m(x, y)] = ids[[indexs[num][0]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMap();
|
||||||
|
}else{
|
||||||
|
that.error = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatArr: function(){
|
||||||
|
var formatArr = '';
|
||||||
|
var arr = this.mapArr.replace(/\s/g, '').split('],[');
|
||||||
|
|
||||||
|
if(arr.length != 13) return false;
|
||||||
|
else {
|
||||||
|
for(var i =0; i<13; i++){
|
||||||
|
var a = [];
|
||||||
|
formatArr +='[';
|
||||||
|
if(i==0||i==12) a = arr[i].split(/\D/).join(' ').trim().split(' ');
|
||||||
|
else a = arr[i].split(/\D/);
|
||||||
|
if(a.length != 13){
|
||||||
|
formatArr = '';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(var k=0; k<13; k++){
|
||||||
|
formatArr = Array(Math.max(4-mapxy.length,0)).join(' ')+a[k];
|
||||||
|
filestr+=a[k]+(k==12?'':',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
formatArr += (']'+(i==12?'':',')+'\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mapArr = formatArr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var editTip = new Vue({
|
||||||
|
el: '#editTip',
|
||||||
|
data: {
|
||||||
|
|
||||||
|
// message: ''
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
copyMap: function(){
|
||||||
|
this.message = '';
|
||||||
|
tip.whichShow = 0;
|
||||||
|
if(pout.value.trim() != ''){
|
||||||
|
try{
|
||||||
|
pout.select();
|
||||||
|
document.execCommand("Copy");
|
||||||
|
tip.whichShow = 6;
|
||||||
|
}catch(e){
|
||||||
|
this.error= 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);
|
||||||
|
pout.value = '';
|
||||||
|
tip.whichShow = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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;奇数警告,偶数成功
|
||||||
|
"当前未选择任何图块,请先在右边选择要画的图块!",
|
||||||
|
"生成地图成功!可点击复制按钮复制地图数组到剪切板",
|
||||||
|
"生成失败! 地图中有未定义的图块,建议先用其他有效图块覆盖或点击清除地图!",
|
||||||
|
"地图清除成功!",
|
||||||
|
"复制失败!"+editTip.error,
|
||||||
|
"复制成功!可直接粘贴到楼层文件的地图数组中。",
|
||||||
|
"复制失败!当前还没有数据",
|
||||||
|
"修改成功!可点击复制按钮复制地图数组到剪切板"
|
||||||
|
],
|
||||||
|
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 = '';
|
||||||
|
if(that.whichShow)
|
||||||
|
that.mapMsg = that.msgs[that.whichShow-1];
|
||||||
|
|
||||||
|
clearTimeout(that.timer);
|
||||||
|
that.timer = setTimeout(function() {
|
||||||
|
tip.whichShow = 0;
|
||||||
|
}, 5000); //5秒后自动清除
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var selectBox = new Vue({
|
||||||
|
el: '#selectBox',
|
||||||
|
data: {
|
||||||
|
isSelected: false
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isSelected: function(){
|
||||||
|
tip.isSelectedBlock = this.isSelected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -76,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 == 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 == 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 == 14) tmp.event = {'cls': 'animates', 'id': 'curseNet', 'noPass': false, 'trigger': 'passNet'}; // 咒网
|
||||||
|
|
||||||
if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
|
if (id == 15) tmp.event = {'cls': 'animates', 'id': 'water', 'noPass': true}; // 水
|
||||||
|
|
||||||
// autotile: 20
|
// autotile: 20
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user