├── .babelrc
├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── favicon.ico
├── game.gif
├── index.html
├── package.json
├── src
├── api
│ └── login.js
├── app.js
├── assets
│ ├── font
│ │ └── mainfont.otf
│ ├── hero-1.png
│ ├── menu-fight.png
│ ├── menu-forge.png
│ ├── menu-setting.png
│ └── menu-shop.png
├── components
│ ├── App.vue
│ ├── component-item.vue
│ ├── game-config.vue
│ ├── game-fight-drop-list.vue
│ ├── game-fight-event-log.vue
│ ├── game-fight-unit-info.vue
│ ├── game-fight.vue
│ ├── game-home-info.vue
│ ├── game-home-menu.vue
│ ├── game-home.vue
│ ├── game-hot-key-item.vue
│ ├── game-house.vue
│ ├── game-login.vue
│ ├── game-map-active.vue
│ ├── game-map-block.vue
│ ├── game-map.vue
│ ├── game-package.vue
│ ├── game-progress.vue
│ ├── game-range-select.vue
│ ├── game-shop.vue
│ ├── game-skill-item.vue
│ ├── game-smithy-blueprint.vue
│ ├── game-smithy-intensify.vue
│ ├── game-smithy.vue
│ ├── game-state-item.vue
│ └── game-switch-button.vue
├── css
│ ├── animate.css
│ ├── item-tool-tip.css
│ ├── main.css
│ └── map-dialog-modal.css
├── data
│ ├── blueprint-data.js
│ ├── constant.js
│ ├── event-data.js
│ ├── hero-data.js
│ ├── item-data.js
│ ├── map-data.js
│ ├── monster-data.js
│ ├── skill-data.js
│ └── state-data.js
├── directive
│ ├── drop-item.js
│ └── item-tool-tip.js
├── filter.js
├── js
│ ├── astar.js
│ ├── audio.js
│ ├── blueprint.js
│ ├── cool-time-event.js
│ ├── create-hero.js
│ ├── create-monster.js
│ ├── different-item-move-class.js
│ ├── drag-drop.js
│ ├── dungeon-creater.js
│ ├── event-class.js
│ ├── fight-action-class.js
│ ├── fight.js
│ ├── hero
│ │ └── update-attribute.js
│ ├── intensify.js
│ ├── map-hero-move.js
│ ├── map-init.js
│ ├── monster-ai.js
│ ├── public-function.js
│ ├── public-random-range.js
│ ├── public-static-get.js
│ ├── release-skill.js
│ ├── save-load.js
│ ├── skill-available.js
│ └── unit-class.js
├── router.js
├── store.js
└── store
│ ├── config-store.js
│ ├── fight-store.js
│ ├── hero-store.js
│ ├── map-store.js
│ └── smithy-store.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["es2015", { "modules": false }]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | bower_components/
5 | npm-debug.log
6 | .vscode
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // 将设置放入此文件中以覆盖默认值和用户设置。
2 | {
3 | "editor.tabSize": 2,
4 | "files.associations": {
5 | "*.vue": "vue"
6 | },
7 | "vsicons.presets.angular": true
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Endless[文档持续更新中...请往下看]
2 |
3 | > 一个用 Vue.js 开发的冒险类游戏;
4 |
5 | 
6 |
7 | ## 项目安装&启动
8 |
9 | ``` bash
10 | git clone https://github.com/bastarder/Endless.git
11 | cd Endless
12 | npm install
13 | npm run dev
14 | ```
15 |
16 | ## 你好
17 | - 如果你和我一样, 是一名学生, 喜欢前端开发, 苦于没有练手的项目, 或是您已经是独当一面的大牛, 不妨可以与我一起完成这样一个小游戏, 在这个游戏中最复杂的战斗系统已经基本完成, 剩下一些 相对容易 的功能, 能让你得到很好的锻炼。
18 |
19 | - 这是一款 简单的冒险类游戏, 我也希望在编写过程中能得到更好的锻炼;
20 |
21 | - 数据设计, UI设计 方面, 我真的不擅长, 如果你觉得你闲来无事的时候愿意帮忙, 可以联系我;
22 | - `QQ:85257684`,`Wechat: I85257`
23 |
24 | ## 游戏详细文档
25 |
26 | ## 战斗逻辑
27 |
28 | 游戏中的战斗系统采用`技能冷却`的模式,一次向一个目标发送一个技能,计算一次行动的效果为战斗逻辑
29 |
30 | 首先是大致逻辑:(Fight.js)
31 |
32 | ### 1. 判断技能是否可以释放 (skill-available.js)
33 |
34 | 当`技能被释放`后会优先进入这个方法,此方法接受3个参数,`技能对象`,`攻击方`,`被攻击方`,可根据这3者定制,判断此次技能是否能够被释放;
35 |
36 | ### 2. 触发冷却时间 (cool-time-event.js)
37 |
38 | 当技能被判定为能够成功释放以后, 技能将进入冷却时间;
39 |
40 | ### 3. 提取人物状态与敌人状态;
41 |
42 | 从攻击者身上提取 `type : 1` 的状态, 从被攻击者身上提取 `type : 2`的状态;
43 |
44 | `type : 1` 为所有能够影响到此次技能的状态,并且自定义状态时应该按照这一规则创建,否则将不被认可;
45 |
46 | `type : 2` 为所有能够影响此次技能对敌人造成的改变的状态,并且自定义状态时应该按照这一规则创建,否则将不被认可;
47 |
48 | ### 4. 拼接技能,状态效果排序;
49 |
50 | 将技能与状态对象中的事件提取并且合并,按照优先级进行排序;
51 |
52 | ### 5. 行动对象计算(关键);
53 |
54 | 技能行动对象,由所有的状态与技能行动效果进行计算而出,根据权重排序;
55 |
56 | *权重暂定的规则*
57 |
58 | 1. `1-10` Action层, 与 影响本次 `暴击`,`命中`,`闪避` 等前置条件相关的事件,权重值需要定义在这个范围;
59 | 2. `11-89` 普通层, 普通的效果,例如技能造成的伤害,状态添加,移除,更新等,大多数都在普通层;
60 | 3. `>90` 在`90`层中,将会计算本次技能在所有可识别加成下的最终结果(如,造成伤害,恢复血量等等), 如果你的状态需要造成一个 `最终加成`, 例如最终伤害增加50%,那么此事件必须 定义在90层, 在计算结果确定后;
61 | 4. `9999` 最终层, 所有权重都不应该超出9999, 在计算此层时, 行动对象将被转换为可被`unit`接口直接执行的参数,并且不再改变;
62 |
63 | ### 6. 判断行动对像是否有效;
64 | 行动对象生成完毕后, 判定此次攻击是否有效(如必中,miss,等 ps: 必中将优先于100%miss);
65 |
66 | ### 7. 执行变更
67 | 确定有效以后,将行动对象代理给`攻击者`与`被攻击者`处理;
68 |
69 | ### 8. 触发全局冷却
70 | 至此,技能完全释放完毕,每次释放技能为`1s`,也就是说释放一个技能以后所有技能都将获得1秒的冷却(如果技能冷却时间不满1秒,将被强制提升至1秒);
71 |
72 | **一次技能释放结束,如果攻击方和被攻击方,有一方死亡, 则返回 false**
73 |
74 | ## 强化逻辑
75 |
76 | 1. 消耗计算
77 |
78 | 目前采用的公式是: (等级 * 基础金额) * (强化等级 + 1) * 品级参数
79 |
80 | 2. 几率计算
81 |
82 | 几率采用固定值: [1, 1, 1, 0.95, 0.9, 0.8, 0.75, 0.62, 0.54, 0.41, 0.33, 0.28, 0.2, 0.17, 0.13, 0.1, 0.04, 0.03, 0.01, 0.01]
83 |
84 | 3. 属性增幅计算
85 | - **武器计算公式** [等级 + 品级参数A/ 8 ] * 强化参数 * 品级参数B * 位置参数
86 | - **防具计算公式** 基础值 * 强化参数 * 品级参数 * 位置参数
87 | 4. 强化失败后的处理
88 | - 1-3 无效果
89 | - 3-8 随机退级 1-2
90 | - 8-10 掉为0级
91 | - 11以上 装备被破坏
92 |
93 | 大致流程: **判断前置条件 -> 判断强化几率-> 重新计算属性**
94 |
95 |
96 |
97 | ## 如何自定义事件
98 |
99 | 在地图中,我们总可以触发事件,从而获得特定的效果,目前内置的事件的有2种:
100 |
101 | 1. **战斗事件** : 当英雄走入此事件格子以后,将会利用事件对象创建一个战斗事件, 并且跳转到战斗界面,当战斗完毕,在返回地图;
102 |
103 | 2. **对话事件** : 当英雄走入此事件格子以后, 将会利用事件对象创建一个对话事件,与英雄进行一些对话交互,分支事件,物品交换等;
104 |
105 | **事件定义的规则:**
106 |
107 | ```
108 | 1. 事件将在判定允许被移动后,直接执行;
109 | 2. 在执行阶段可捕获的参数有: 当前格子对象,目标格子对象,英雄对象
110 | 3. 事件必须为一个可执行的函数;
111 | 4. 详细可以参考内置事件(event-class.js)
112 | ```
113 |
114 | ## 如何自定义技能(状态)
115 |
116 | 在这里,我们引用实例来解释,技能对象的定义规则;
117 |
118 | *数据(skill-data.js,state-data.js),所有字符串形式将在`战斗技能释放,计算行动对象时`被转换为可执行函数;*
119 |
120 | **范例**
121 |
122 | ```
123 | {
124 | id: 1000002,
125 | name: '净化',
126 | dsc : '净化中毒效果~',
127 | label : ['测试2','伤害2'],
128 | defaultTime : 3000,
129 | restrict : [
130 | "[attacker]{$mp} >= {60}",
131 | "[attacker]{$hp} <= {250}",
132 | "[attacker]{$skills} nothas {1000003,1000001}",
133 | "[attacker]{$status} has {2000001}",
134 | "[skill]{coolTime} > {0}",
135 | function(skill, attacker, enemy){
136 | return true;
137 | }
138 | ],
139 | eventList : [
140 | `[1]enemy@changeHp@attacker.$atk`,
141 | `[2]attacker@changeHp@2`,
142 | `[3]
143 | action@{action.state.isCritical === true};
144 | attacker@{attacker.$hp > (attacker.$hp * 0.5)}
145 | #
146 | enemy@changeState@[{ id: 2000001, state: "ADD" }];
147 | enemy@changeHp@attacker.$atk
148 | `
149 | ,
150 | `[4]action@{action.state.isCritical = true}`,
151 | ],
152 | // 当为状态时还可以添加下面2个属性;
153 | stateEvent : function(hero) {
154 | var self = this;
155 | var duration = 5;
156 | var per = 1;
157 | var current = 1;
158 | self.stateEventTimer = setInterval(function(){
159 | hero.changeHp(-30);
160 | current +=1;
161 | if(current > 5){
162 | clearInterval(self.stateEventTimer);
163 | hero.removeList('$status',self);
164 | }
165 | }, per * 1000);
166 | this.actived = true;
167 | },
168 | powerUp : {
169 | $maxHp : [0,0,0,0],
170 | },
171 | },
172 | ```
173 |
174 | | key | value | dsc |
175 | | ------------- |:-------------:| -----|
176 | | id | Number | 技能的唯一标识 |
177 | | name | String | 技能的名称 |
178 | | label | [String] | 技能的标签 |
179 | | defaultTime | Number|技能冷却时间,单位毫秒|
180 | | restrict | [String or function] | 技能释放的前置条件,当解析为函数时,能访问3个参数,`技能`,`攻击者`,`被攻击者`,当函数返回为true时表示此规则通过,字符串解析规则: `[对象]{属性名1} 标识符 {值}`, 标识符支持 `> < >= <= nothas has`,表达式的最终值将被判定为真与假|
181 | | eventList |[String or function]|事件列表, 可以为一个函数,函数中接受3个参数,`行动对象`,`攻击者`,`被攻击者`,字符串形式解析规则`[权重]参数名@{前置条件语句}#参数名@事件@事件参数`,可多条应该使用`;`分开,并且末尾条目不需要添加分隔符|
182 | | stateEvent |function| 持续状态, 一个状态被添加到对象身上时将被执行此函数,在此函数内可以访问被作用的单位,做一些处理|
183 | | powerUp |object| 状态能带来的属性提升, Array值解析,`[值, 类型: 0:基础值 1:基础百分 2:高级值 3:高级百分]`, 属性计算公式`((默认 + 基础值) * (1 + 基础百分) + 高级值) * (1 + 高级百分)`|
184 |
185 | *恭喜,至此你已经可以制作一个属于自己的技能了*
186 |
187 | ## 如何自定义装备(物品)
188 |
189 | ```
190 | // 装备
191 | {
192 | id: 30000012,
193 | name: '精致的铁剑',
194 | level: 1,
195 | grade: 1,
196 | equipType : 0,
197 | label: [
198 | '武器'
199 | ],
200 | intensify : 1,
201 | intPowerUp : {
202 | $atk: 123
203 | },
204 | equip : {
205 | $def: 2,
206 | $atk: 15,
207 | $maxHp : 5,
208 | $maxMp : 5,
209 | },
210 | dsc : '用野草编制的手镯'
211 | }
212 | // 物品
213 | {
214 | id: 3000001,
215 | name: '野草',
216 | pile : true,
217 | price : 10,
218 | use : {
219 | defaultTime : 1000,
220 | restrict :[
221 | function(){
222 | return this.$hp > 500;
223 | }
224 | ],
225 | effect :[
226 | function(){
227 | this.changeHp(30);
228 | }
229 | ]
230 | },
231 | label : [
232 | '材料'
233 | ],
234 | dsc : '很常见的东西,或许能用来做一些东西'
235 | },
236 | ```
237 | | key | value | dsc |
238 | | ------------- |:-------------:| -----|
239 | | level | Number | 装备的等级 |
240 | | grade |Number| 装备的品级 `白0`,`绿1`,`蓝2`,`紫3`,`橙4`|
241 | | equipType |Number|装备类型 `武器0`, `护肩1`, `鞋子2`, `腰带3`, `上衣4`, `绑腿5`, `戒指6`, `项链7`, `手镯8`,|
242 | | equip |object|装备的属性 参数的值与`状态`的powerUp相同|
243 | | intensify |Number|强化等级|
244 | | intPowerUp |Object|强化带来的增益,武器为`$atk`,其余防具为`$dmgDown`(百分比减伤)|
245 | | pile |Boolean| 是否可以叠加|
246 | | price |Number|物品的价值|
247 | |use|object|可使用物品的效果 `defaultTime :冷却时间`, `restrict 前置条件列表,接受函数为单个条件,this指向使用者`,`effect 造成的效果列表,接受函数为单个事件,this指向使用者`|
248 | *怎么样?是不是发现自己还是不会创建一个属于自己的装备~ 没关系,是我表达的不好~*
249 |
250 | ## 文档未完待续...有空在更新
251 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/favicon.ico
--------------------------------------------------------------------------------
/game.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/game.gif
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
34 |
35 |
36 |
40 |
41 |
游 戏 加 载 中 ...
42 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello",
3 | "description": "A Vue.js project",
4 | "author": "bastarder <85257684@qq.com>",
5 | "private": true,
6 | "scripts": {
7 | "dev": "webpack-dev-server --open --inline --hot --host 0.0.0.0",
8 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
9 | },
10 | "dependencies": {
11 | "axios": "^0.15.3",
12 | "babel-polyfill": "^6.23.0",
13 | "normalize.css": "^6.0.0",
14 | "vue": "^2.0.1",
15 | "vue-router": "^2.0.0",
16 | "vuex": "^2.0.0"
17 | },
18 | "devDependencies": {
19 | "babel-core": "^6.0.0",
20 | "babel-loader": "^6.0.0",
21 | "babel-preset-es2015": "^6.0.0",
22 | "cross-env": "^3.0.0",
23 | "css-loader": "^0.25.0",
24 | "file-loader": "^0.9.0",
25 | "less": "^2.7.2",
26 | "less-loader": "^2.2.3",
27 | "style-loader": "^0.13.1",
28 | "vue-loader": "^9.4.0",
29 | "webpack": "2.1.0-beta.22",
30 | "webpack-dev-server": "2.1.0-beta.9",
31 | "lodash": "4.17.4"
32 | }
33 | }
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 |
3 | const login = function(){
4 | return axios.post('login/signin',{
5 | username: 'aaa',
6 | password: 'aaa'
7 | });
8 | }
9 |
10 | export {
11 | login
12 | };
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | // public css
2 | require("./css/animate.css");
3 | require("./css/main.css");
4 | require("./css/map-dialog-modal.css");
5 | require("normalize.css");
6 |
7 | // require('babel-polyfill');
8 |
9 | // public js
10 | import public_function from './js/public-function.js';
11 |
12 | import Vue from 'vue'
13 | import Vuex from 'vuex'
14 | import router from './router'
15 | import filter from './filter'
16 | import store from './store'
17 | // import axios from 'axios'
18 |
19 | // components
20 | import App from './components/App.vue'
21 | import GamePackage from './components/game-package.vue'
22 | import GameHouse from './components/game-house.vue'
23 | import GameProgress from './components/game-progress.vue'
24 | import GameSkillItem from './components/game-skill-item.vue'
25 | import GameStateItem from './components/game-state-item.vue'
26 | import ComponentItem from './components/component-item.vue'
27 |
28 | Vue.component('App', App)
29 | Vue.component('game-package', GamePackage)
30 | Vue.component('game-house', GameHouse)
31 | Vue.component('game-progress', GameProgress)
32 | Vue.component('game-skill-item', GameSkillItem)
33 | Vue.component('game-state-item', GameStateItem)
34 | Vue.component('component-item', ComponentItem)
35 |
36 | Vue.config.errorHandler = function (err, vm) {
37 | console.warn(err,vm);
38 | router.replace('/');
39 | // router.replace('/login');
40 | }
41 |
42 | // Object.assign(axios.defaults,{
43 | // baseURL : 'http://127.0.0.1:8000',
44 | // })
45 |
46 | const app = new Vue({
47 | store,
48 | router,
49 | template: ``
50 | }).$mount('#app')
51 |
52 |
53 |
--------------------------------------------------------------------------------
/src/assets/font/mainfont.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/font/mainfont.otf
--------------------------------------------------------------------------------
/src/assets/hero-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/hero-1.png
--------------------------------------------------------------------------------
/src/assets/menu-fight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/menu-fight.png
--------------------------------------------------------------------------------
/src/assets/menu-forge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/menu-forge.png
--------------------------------------------------------------------------------
/src/assets/menu-setting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/menu-setting.png
--------------------------------------------------------------------------------
/src/assets/menu-shop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bastarder/vue-endless-h5-game/f0a46f988f502c1df753e4dab5bbaaf8c9e2f80f/src/assets/menu-shop.png
--------------------------------------------------------------------------------
/src/components/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
43 |
44 |
52 |
53 |
77 |
--------------------------------------------------------------------------------
/src/components/component-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{item.name}}
6 |
7 |
8 | {{item.num}}
9 | {{equipCname[item.equipType]}}
10 |
11 |
12 |
13 |
14 |
15 | {{position.$equipments ? equipCname[index]: ''}}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
62 |
63 |
128 |
--------------------------------------------------------------------------------
/src/components/game-config.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 音效
5 |
6 |
7 |
8 |
9 | 音乐
10 |
11 |
12 |
13 |
14 | 技能日志
15 |
16 |
17 |
18 |
19 | 技能快捷键
20 |
21 |
22 |
23 |
24 |
25 |
26 | 物品快捷键
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{tip}}
34 |
35 |
36 |
保存游戏
37 |
38 |
保存配置
39 |
40 |
重置游戏
41 |
42 |
返回
43 |
44 |
45 |
46 |
47 |
97 |
98 |
124 |
--------------------------------------------------------------------------------
/src/components/game-fight-drop-list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
你的背包似乎不能容纳更多的东西了!
17 |
18 |
19 |
20 |
21 |
60 |
61 |
--------------------------------------------------------------------------------
/src/components/game-fight-event-log.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
39 |
40 |
--------------------------------------------------------------------------------
/src/components/game-fight-unit-info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{unit.$nickname}}
6 | {{unit.$showName}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 死亡
23 |
24 |
25 |
26 |
27 |
49 |
50 |
96 |
--------------------------------------------------------------------------------
/src/components/game-fight.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
15 |
16 |
21 |
22 |
23 |
29 |
30 |
31 | 提示 : 默认攻击键为Q,W,E,R
32 |
33 |
34 |
35 |
36 |
37 |
174 |
175 |
205 |
--------------------------------------------------------------------------------
/src/components/game-home-info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
7 |
99
8 |
9 |
Bastarder
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
属性
26 |
攻击力
27 |
{{hero.$r.$atk}}
28 |
29 |
30 |
31 | {{key | heroAttrKey}}
32 | {{hero.$r[key].toFixed(1)}}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
65 |
66 |
208 |
--------------------------------------------------------------------------------
/src/components/game-home-menu.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
32 |
33 |
83 |
--------------------------------------------------------------------------------
/src/components/game-home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
35 |
36 |
56 |
--------------------------------------------------------------------------------
/src/components/game-hot-key-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{String.fromCharCode(value)}}
5 |
6 |
7 |
8 |
9 |
10 |
34 |
35 |
--------------------------------------------------------------------------------
/src/components/game-house.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 仓库
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
43 |
44 |
70 |
--------------------------------------------------------------------------------
/src/components/game-login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | - 目前只是一个测试的登录页面,点击登录按钮就可以直接进入
6 | - 游戏的基本流程已经大致完成
7 | - 如果你有兴趣提交一个有趣的技能,或者装备
8 | - 如果你有兴趣一起参与开发
9 | - 如果你认为我的贴图很丑,想给我提供更好的图片
10 | - 请你们联系QQ: 85257684
11 |
12 |
15 |
16 |
17 |
登录战斗
18 |
账号
19 |
20 |
密码
21 |
22 |
注册账号
23 |
24 |
25 |
26 |
27 |
28 |
29 |
注册成为英雄
30 |
账号
31 |
32 |
密码
33 |
34 |
登录
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
79 |
80 |
153 |
--------------------------------------------------------------------------------
/src/components/game-map-active.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
回家
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | <
16 | >
17 |
18 |
19 |
20 |
21 |
{{map.$opt.name}}
22 |
23 | 战斗
24 | 事件
25 | 英雄
26 |
27 |
28 |
29 |
30 |
41 |
42 |
43 |
44 |
45 |
111 |
112 |
208 |
--------------------------------------------------------------------------------
/src/components/game-map-block.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
68 |
69 |
--------------------------------------------------------------------------------
/src/components/game-map.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ map.name }}
8 |
9 | {{ map.dsc }}
10 |
11 |
12 |
13 |
14 |
15 | 回 家
16 |
17 |
18 |
19 |
20 |
21 |
47 |
48 |
115 |
--------------------------------------------------------------------------------
/src/components/game-package.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 背包
5 |
6 |
7 |
8 | {{item[0]}}
9 | {{hero.$resource[item[1]]}}
10 |
11 |
12 |
13 | 垃圾箱
14 |
15 |
16 |
17 | 整理
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
57 |
58 |
104 |
--------------------------------------------------------------------------------
/src/components/game-progress.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ value + ' / ' + max}}
5 | ∞
6 |
7 |
8 |
9 |
10 |
11 |
32 |
33 |
--------------------------------------------------------------------------------
/src/components/game-range-select.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
76 |
77 |
--------------------------------------------------------------------------------
/src/components/game-shop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 暂未开放
4 |
5 |
6 |
7 |
21 |
22 |
27 |
--------------------------------------------------------------------------------
/src/components/game-skill-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{skill.name.slice(0,1)}}
4 |
5 |
13 |
14 |
15 |
16 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/game-smithy-blueprint.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 暂无
5 |
6 |
7 | 打 造
8 |
9 |
10 |
{{item.name}}
11 |
12 |
13 |
14 | {{tip}}
15 |
16 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
94 |
95 |
147 |
--------------------------------------------------------------------------------
/src/components/game-smithy-intensify.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
68 |
69 |
147 |
--------------------------------------------------------------------------------
/src/components/game-smithy.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
29 |
30 |
45 |
--------------------------------------------------------------------------------
/src/components/game-state-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{state.name.slice(0,1)}}
5 |
6 |
7 | {{state.name.slice(0,1)}}
8 |
9 |
10 |
11 |
12 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/game-switch-button.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
21 |
22 |
--------------------------------------------------------------------------------
/src/css/item-tool-tip.css:
--------------------------------------------------------------------------------
1 | .item-tool-tip-pover .equip{
2 | color:#e7d0d0;
3 | }
4 |
5 | .item-tool-tip-pover .attr-name.skills{
6 | color: yellow;
7 | }
8 |
9 | .item-tool-tip-pover .attr-name.status{
10 | color: green;
11 | }
12 |
13 | .item-tool-tip-pover .attr-name.down,
14 | .item-tool-tip-pover .attr-data.down,
15 | .item-tool-tip-pover .dsc{
16 | color: gray;
17 | }
18 |
19 | .item-tool-tip-pover{
20 | background: rgba(12, 4, 37, 0.8);
21 | border-radius: 4px;
22 | padding: 6px;
23 | color: white;
24 | text-shadow: black 1px 1px;
25 | font-size: 10px;
26 | z-index: 99;
27 | }
--------------------------------------------------------------------------------
/src/css/main.css:
--------------------------------------------------------------------------------
1 | *{
2 | box-sizing: border-box;
3 | user-select: none;
4 | list-style: none;
5 | -ms-box-sizing: border-box; /* IE 9 */
6 | -moz-box-sizing: border-box; /* Firefox */
7 | -webkit-box-sizing: border-box; /* Safari 和 Chrome */
8 | -o-box-sizing: border-box; /* Opera */
9 | }
10 |
11 | ul{
12 | margin:0px;
13 | padding: 0px;
14 | }
15 |
16 | *::-webkit-scrollbar{
17 | display: none;
18 | }
19 |
20 | @font-face {
21 | font-family: 'mainfont';
22 | src: url('../assets/font/mainfont.otf');
23 | font-weight: normal;
24 | font-style: normal;
25 | }
26 |
27 | body{
28 | font-family: mainfont;
29 | padding: 0px;
30 | margin: 0px;
31 | font-weight: 200;
32 | -ms-overflow-style: none;
33 | -moz-overflow-style: none;
34 | -webkit-overflow-style: none;
35 | -o-overflow-style: none;
36 | }
37 |
38 | .btn{
39 | display: inline-block;
40 | text-align: center;
41 | padding: 2px 4px;
42 | background: none;
43 | color: #1997c6;
44 | border: 1px solid #1997c6;
45 | border-radius: 2px;
46 | cursor: pointer;
47 | text-decoration: none;
48 | outline: none;
49 | transition:all 0.3s ease-in-out;
50 | }
51 | .btn:hover{
52 | background: #1997c6;
53 | color: white;
54 | transition:all 0.3s ease-in-out;
55 | }
56 |
57 | .btn.disabled, .btn.disabled:hover{
58 | color: gray;
59 | border-color: gray;
60 | background: none;
61 | cursor: no-drop;
62 | }
63 |
64 | .btn.red{
65 | color: red;
66 | border-color: red;
67 | }
68 |
69 | .btn.red:hover{
70 | background: red;
71 | color: white;
72 | }
73 |
74 | .m-t-10{
75 | margin-top: 10px;
76 | }
77 |
78 | .m-b-10{
79 | margin-bottom: 10px;
80 | }
81 |
82 | .m-b-4{
83 | margin-bottom: 4px;
84 | }
85 |
86 | /*
87 | .m-l-10{
88 | margin-bottom: 10px;
89 | }
90 |
91 | .m-r-10{
92 | margin-bottom: 10px;
93 | }*/
94 |
95 | .text-c{
96 | text-align: center;
97 | }
98 |
99 | .text-l{
100 | text-align: left;
101 | }
102 |
103 | .text-r{
104 | text-align: right;
105 | }
106 |
107 | .f-r{
108 | float: right;
109 | }
110 |
111 | .radius-4{
112 | border-radius: 4px;
113 | }
114 |
115 | .radius-2{
116 | border-radius: 2px;
117 | }
118 |
119 | .back-color{
120 | background: #fff6cb;
121 | }
122 |
123 | .font-min{
124 | font-size: 12px;
125 | }
126 |
127 | .p-abs{
128 | position: absolute;
129 | }
130 |
131 | .p-rel{
132 | position: relative;
133 | }
134 |
135 | .d-ib{
136 | display: inline-block;
137 | }
138 |
139 | .d-b{
140 | display: block;
141 | }
142 |
143 | .color-red{
144 | color: #d44950;
145 | }
146 |
147 | .color-green{
148 | color: #1bc98e;
149 | }
150 |
151 | .color-yellow{
152 | color: #e4d836;
153 | }
154 |
155 | .color-purple{
156 | color: #9f86ff;
157 | }
158 |
159 | .color-darkblue{
160 | color: #1997c6;
161 | }
162 |
163 | .bg-red{
164 | background: #d44950;
165 | }
166 |
167 | .bg-green{
168 | background: #1bc98e;
169 | }
170 |
171 | .bg-yellow{
172 | background: #e4d836;
173 | }
174 |
175 | .bg-purple{
176 | background: #9f86ff;
177 | }
178 |
179 | .bg-darkblue{
180 | background: #1997c6;
181 | }
182 |
183 | .aboutMe{
184 | display: block;
185 | position: fixed;
186 | top: 0;
187 | padding: 0;
188 | }
189 |
190 | .aboutMe a{
191 | display: inline-block;
192 | background: black;
193 | cursor: pointer;
194 | color: white;
195 | text-decoration: none;
196 | width: 100px;
197 | height: 40px;
198 | line-height: 40px;
199 | text-align: center;
200 | margin-bottom: 4px;
201 | }
202 |
203 | .game-main-loading{
204 | display: none;
205 | }
--------------------------------------------------------------------------------
/src/css/map-dialog-modal.css:
--------------------------------------------------------------------------------
1 | .Map-Dialog-modal{
2 | background: white;
3 | border-radius: 2px;
4 | box-shadow: 0px 0px 4px rgba(0,0,0,0.2);
5 | border: 1px solid #eee;
6 | padding: 10px;
7 | font-size: 14px;
8 | letter-spacing: 1px;
9 | line-height: 20px;
10 | }
11 |
12 | .Map-Dialog-modal .close{
13 | display: inline-block;
14 | transform: rotate(45deg);
15 | font-size: 26px;
16 | height: 20px;
17 | float: right;
18 | cursor: pointer;
19 | }
20 |
21 | .Map-Dialog-modal .close:hover{
22 | color: gray;
23 | }
24 |
25 | .Map-Dialog-modal .msg{
26 | padding: 4px 20px 4px 6px;
27 | background: #fcf8e3;
28 | }
29 |
30 | .Map-Dialog-modal .change{
31 | background: #f2dede;
32 | padding: 4px 6px;
33 | }
34 |
35 | .Map-Dialog-modal .change .num{
36 | color: #337ab7;
37 | }
38 |
39 | .Map-Dialog-modal .change .name{
40 | color: #aa6708;
41 | }
42 |
43 | .Map-Dialog-modal .event{
44 | position: absolute;
45 | top: 164px;
46 | width: 278px;
47 | }
48 |
49 | .Map-Dialog-modal .action{
50 | border: none;
51 | background: #d9edf7;
52 | padding: 6px 8px;
53 | cursor: pointer;
54 | margin-left: 4px;
55 | float: right;
56 | }
57 |
58 | .Map-Dialog-modal .action:hover{
59 | box-shadow: 0px 0px 2px;
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/src/data/blueprint-data.js:
--------------------------------------------------------------------------------
1 | const BLUEPRINT_TABLE = [
2 | {
3 | id : 4000001,
4 | name : '测试蓝图',
5 | need : [
6 | [3000001, 22],
7 | [3000002, 1],
8 | [
9 | function(item){
10 | return item && item.id === 3000001;
11 | },
12 | function(){
13 | return 5;
14 | },
15 | '未知的物品',
16 | ]
17 | ],
18 | synthetics : [
19 | 3000003,
20 | [
21 | 3000004,
22 | function(){
23 | return item
24 | }
25 | ]
26 | ]
27 | },
28 | {
29 | id : 4000002,
30 | name : '狂战士之斧',
31 | need : [
32 | [3000001, 2],
33 | [3000002, 1],
34 | ],
35 | synthetics : [
36 | 30000013,
37 | ]
38 | }
39 | ]
40 |
41 | export default BLUEPRINT_TABLE;
--------------------------------------------------------------------------------
/src/data/constant.js:
--------------------------------------------------------------------------------
1 | const CONSTANT = {
2 | MAP_BLOCK_TYPE:{
3 | STICK : '2', // 障碍
4 | ROAD : '0', // 可行走
5 | PATH : '4', // 路径
6 | HERO : '1', // 英雄
7 | END : '3' // 寻路终点
8 | },
9 | EQUIP_ID :[
10 | "武器", "护肩", "鞋子", "腰带", "上衣", "绑腿", "戒指", "项链", "手镯",
11 | ],
12 | UNIT_ATTR_NAME:{
13 | $hp : '当前生命值',
14 | $mp : '当前魔法值',
15 | $maxHp : '生命',
16 | $maxMp : '魔法',
17 | $atk : '攻击力',
18 | $def : '防御',
19 | $str : '力量',
20 | $dex : '敏捷',
21 | $con : '体质',
22 | $int : '智力',
23 | $critical : '暴击',
24 | $dodge : '闪避',
25 | $coolTimePer : '冷却缩短',
26 | $skills : '技能',
27 | $status : '状态',
28 | $dmgDown : '伤害减免',
29 | $critiDmg : '暴击伤害',
30 | },
31 | ITEM_LEVEL: ['ghostwhite','springgreen','skyblue','violet','orange'],
32 | }
33 |
34 | export default CONSTANT
--------------------------------------------------------------------------------
/src/data/event-data.js:
--------------------------------------------------------------------------------
1 | const DIALOG_DATA = [
2 | {
3 | id : 7000001,
4 | type: 'MapDialog',
5 | data: [
6 | {
7 | msg: '你好, 我需要以下材料,给我,我就给你你想要的~',
8 | buttons : [
9 | "[我没有]{1,1}",
10 | "#[我有的]{3,4,2,1}"
11 | ] ,
12 | need : [[3000001,1],[3000002,2]],
13 | get : [[3000003,1]]
14 | },
15 | '没诚意滚蛋!',
16 | '合作愉快~',
17 | '你的背包好像没有我要的东西~',
18 | '你的背包好像放不下了,下次再来吧!'
19 | ]
20 | }
21 | ]
22 |
23 | export {
24 | DIALOG_DATA
25 | };
--------------------------------------------------------------------------------
/src/data/hero-data.js:
--------------------------------------------------------------------------------
1 |
2 | const EXP_TABLE = [
3 | 1, // 1
4 | 10, // 2
5 | 45, // 3
6 | 120, // 4
7 | 300, // 5
8 | 450, // 6
9 | ];
10 |
11 |
12 | export {
13 | EXP_TABLE
14 | };
--------------------------------------------------------------------------------
/src/data/item-data.js:
--------------------------------------------------------------------------------
1 | const ITEM_TYPE = {
2 |
3 | }
4 |
5 | let Material = [
6 | {
7 | id: 3000001,
8 | name: '野草',
9 | pile : true,
10 | price : 10,
11 | use : {
12 | defaultTime : 1000,
13 | restrict :[
14 | function(){
15 | return this.$hp > 500;
16 | }
17 | ],
18 | effect :[
19 | function(){
20 | this.changeHp(30);
21 | }
22 | ]
23 | },
24 | label : [
25 | '材料'
26 | ],
27 | dsc : '很常见的东西,或许能用来做一些东西'
28 | },
29 | {
30 | id: 3000002,
31 | name: '浆果',
32 | pile : true,
33 | use : function(){
34 | return {
35 | defautTime : 1000,
36 | restrict : [],
37 | effect :[]
38 | }
39 | },
40 | label : [
41 | '食物','材料'
42 | ],
43 | dsc : '能卖钱,能吃,数量很多'
44 | },
45 | ];
46 |
47 | let Equipment = [
48 | {
49 | id: 3000003,
50 | level: 1,
51 | name: '铁剑',
52 | equipType : 0,
53 | label: [
54 | '武器'
55 | ],
56 | equip : {
57 | $atk: 5,
58 | $coolTimePer: 50,
59 | $skills : [1000004],
60 | $status : [2000001],
61 | $maxHp : [-10,1],
62 | },
63 | dsc : '最基础的武器.'
64 | },
65 | {
66 | id: 3000004,
67 | name: '破旧的护肩',
68 | level: 1,
69 | equipType : 1,
70 | label: [
71 | '护肩'
72 | ],
73 | equip : {
74 | $def: 2,
75 | $atk: 5,
76 | $skills : [1000002],
77 | $maxHp : 5,
78 | $maxMp : 5,
79 | },
80 | dsc : '用破布做成的护肩,只能挡风'
81 | },
82 | {
83 | id: 3000005,
84 | name: '破旧的马靴',
85 | level: 1,
86 | equipType : 2,
87 | label: [
88 | '鞋子'
89 | ],
90 | equip : {
91 | $def: 2,
92 | $atk: 1,
93 | $maxHp : 5,
94 | $maxMp : 5
95 | },
96 | dsc : '像是垃圾堆捡来的'
97 | },
98 | {
99 | id: 3000006,
100 | name: '破旧的腰带',
101 | level: 1,
102 | equipType : 3,
103 | label: [
104 | '腰带'
105 | ],
106 | equip : {
107 | $def: 2,
108 | $maxHp : 5,
109 | $maxMp : 5,
110 | },
111 | dsc : '随处可见的腰带'
112 | },
113 | {
114 | id: 3000007,
115 | name: '破旧的上衣',
116 | level: 1,
117 | equipType : 4,
118 | label: [
119 | '上衣'
120 | ],
121 | equip : {
122 | $def: 2,
123 | $maxHp : 5,
124 | $maxMp : 5,
125 | },
126 | dsc : '用破布做成的上衣,只能挡风'
127 | },
128 | {
129 | id: 3000008,
130 | name: '破旧的绑腿',
131 | level: 1,
132 | equipType : 5,
133 | label: [
134 | '绑腿'
135 | ],
136 | equip : {
137 | $def: 2,
138 | $maxHp : 5,
139 | $maxMp : 5,
140 | },
141 | dsc : '用破布做成的护肩,只能挡风'
142 | },
143 | {
144 | id: 3000009,
145 | name: '草戒',
146 | level: 1,
147 | equipType : 6,
148 | label: [
149 | '戒指'
150 | ],
151 | equip : {
152 | $def: 2,
153 | $atk: 5,
154 | $maxHp : 5,
155 | $maxMp : 5,
156 | },
157 | dsc : '用野草编制的戒指'
158 | },
159 | {
160 | id: 30000010,
161 | name: '石头挂坠',
162 | equipType : 7,
163 | level: 1,
164 | label: [
165 | '项链'
166 | ],
167 | equip : {
168 | $def: 2,
169 | $atk: 5,
170 | $maxHp : 5,
171 | $maxMp : 5,
172 | },
173 | dsc : '或许这也是一种信仰吧!'
174 | },
175 | {
176 | id: 30000011,
177 | name: '野草手镯',
178 | equipType : 8,
179 | level: 1,
180 | label: [
181 | '手镯'
182 | ],
183 | equip : {
184 | $def: 2,
185 | $atk: 5,
186 | $maxHp : 5,
187 | $maxMp : 5,
188 | },
189 | dsc : '用野草编制的手镯'
190 | },
191 | {
192 | id: 30000012,
193 | name: '精致的铁剑',
194 | level: 1,
195 | grade: 1,
196 | equipType : 0,
197 | label: [
198 | '武器'
199 | ],
200 | equip : {
201 | $def: 2,
202 | $atk: 15,
203 | $maxHp : 5,
204 | $maxMp : 5,
205 | },
206 | dsc : '用野草编制的手镯'
207 | },
208 | {
209 | id: 30000013,
210 | name: '无尽之刃',
211 | grade: 4,
212 | level: 20,
213 | equipType : 0,
214 | label: [
215 | '武器'
216 | ],
217 | equip : {
218 | $con: 100,
219 | $atk: 70,
220 | $critical : 20,
221 | $status : [2000003],
222 | },
223 | dsc : '流传在世间,创世神的武器'
224 | },
225 | {
226 | id: 30000014,
227 | name: '反伤护甲',
228 | grade: 3,
229 | level: 20,
230 | equipType : 4,
231 | label: [
232 | '上衣'
233 | ],
234 | equip : {
235 | $def: 25,
236 | $maxHp : 100,
237 | $maxMp : 25,
238 | $status : [2000004, 2000002],
239 | },
240 | dsc : '哇!这么刺怎么穿上去的!'
241 | },
242 | ]
243 |
244 | const ITEM_TABLE = _.concat(Material,Equipment);
245 |
246 | export {
247 | ITEM_TABLE
248 | };
--------------------------------------------------------------------------------
/src/data/map-data.js:
--------------------------------------------------------------------------------
1 | const MAP_TABLE = [
2 | {
3 | id: 8000001,
4 | name: '村庄',
5 | logo: '',
6 | dsc: '一个几乎毫无危险的地方.',
7 | mapInitOption: {
8 | row : 20,
9 | col : 20,
10 | lines : 10, // 分支量;
11 | inflex : 0.5 // 曲折度;
12 | },
13 | monsterList: [
14 | 5000001, 5000002
15 | ],
16 | eventList: [
17 | 7000001
18 | ], // 特殊事件触发点;
19 | rule: { // 生成规则;
20 | "5000001" : 4,
21 | "5000002" : 5,
22 | "7000001" : 1,
23 | }
24 | },
25 | {
26 | id: 8000002,
27 | name: '森林',
28 | logo: '',
29 | dsc: '这里可能有野兽出没...',
30 | eventList: [],
31 | monsterList: [],
32 | itemList: [
33 |
34 | ],
35 | rule: {
36 |
37 | }
38 |
39 | },
40 | {
41 | id: 8000003,
42 | name: '青木镇',
43 | logo: '',
44 | dsc: '繁华的小镇,却影藏着危机...',
45 | eventList: [],
46 | monsterList: [],
47 | itemList: [
48 |
49 | ],
50 | rule: {
51 |
52 | }
53 |
54 | },
55 | {
56 | id: 8000004,
57 | name: '镇外',
58 | logo: '',
59 | dsc: '发狂的人类?...',
60 | eventList: [],
61 | monsterList: [],
62 | itemList: [
63 |
64 | ],
65 | rule: {
66 |
67 | }
68 |
69 | },
70 | {
71 | id: 8000005,
72 | name: '青木林',
73 | logo: '',
74 | dsc: '发狂的人类?...',
75 | eventList: [],
76 | monsterList: [],
77 | itemList: [
78 |
79 | ],
80 | rule: {
81 |
82 | }
83 |
84 | },
85 | {
86 | id: 8000006,
87 | name: '火焰洞穴',
88 | logo: '',
89 | dsc: '发狂的人类?...',
90 | eventList: [],
91 | monsterList: [],
92 | itemList: [
93 |
94 | ],
95 | rule: {
96 |
97 | }
98 |
99 | },
100 | {
101 | id: 8000007,
102 | name: '冰霜洞穴',
103 | logo: '',
104 | dsc: '发狂的人类?...',
105 | eventList: [],
106 | monsterList: [],
107 | itemList: [
108 |
109 | ],
110 | rule: {
111 |
112 | }
113 |
114 | },
115 | {
116 | id: 8000008,
117 | name: '祭祀台',
118 | logo: '',
119 | dsc: '发狂的人类?...',
120 | eventList: [],
121 | monsterList: [],
122 | itemList: [
123 |
124 | ],
125 | rule: {
126 |
127 | }
128 |
129 | },
130 | {
131 | id: 8000009,
132 | name: '骷髅大厅',
133 | logo: '',
134 | dsc: '发狂的人类?...',
135 | eventList: [],
136 | monsterList: [],
137 | itemList: [
138 |
139 | ],
140 | rule: {
141 |
142 | }
143 |
144 | },
145 | {
146 | id: 80000010,
147 | name: '骷髅王',
148 | logo: '',
149 | dsc: '发狂的人类?...',
150 | eventList: [],
151 | monsterList: [],
152 | itemList: [
153 |
154 | ],
155 | rule: {
156 |
157 | }
158 |
159 | },
160 | {
161 | id: 80000011,
162 | name: '洞穴出口',
163 | logo: '',
164 | dsc: '发狂的人类?...',
165 | eventList: [],
166 | monsterList: [],
167 | itemList: [
168 |
169 | ],
170 | rule: {
171 |
172 | }
173 |
174 | },
175 | {
176 | id: 80000012,
177 | name: '黑色小溪',
178 | logo: '',
179 | dsc: '发狂的人类?...',
180 | eventList: [],
181 | monsterList: [],
182 | itemList: [
183 |
184 | ],
185 | rule: {
186 |
187 | }
188 |
189 | },
190 | ];
191 |
192 |
193 | export default MAP_TABLE;
--------------------------------------------------------------------------------
/src/data/monster-data.js:
--------------------------------------------------------------------------------
1 | const MONSTER = 'Monster';
2 |
3 | const MONSTER_DATA = [
4 | {
5 | $level : 1,
6 | $type : MONSTER,
7 | $showName : '小野猪',
8 | id : 5000001,
9 | $maxHp : 100,
10 | $maxMp : 0,
11 | $atk : 40,
12 | $def : 0,
13 | $status : [2000002, 2000004],
14 | $dropList : [
15 | // 物品ID, 数量范围, 几率
16 | [3000001, [1,3], 1],
17 | [3000002, [1,3], 0.5],
18 | [3000003, 1, 1],
19 | ['gold',[1, 2], 1],
20 | ['exp', 1, 1],
21 | ['gem',1, 1]
22 | ]
23 | },
24 | {
25 | $level : 2,
26 | $type : MONSTER,
27 | $showName : '小牛',
28 | id : 5000002,
29 | $maxHp : 150,
30 | $maxMp : 0,
31 | $atk : 5,
32 | $def : 0,
33 | $status : [2000002],
34 | // $status : [2000002],
35 | // $skills : [1000001]
36 | $dropList : [
37 |
38 | [3000001, [1,3], 1],
39 | [3000002, [1,3], 0.5],
40 | [3000003, 1, 0.1],
41 | ['gold',[5, 10], 1],
42 | ['exp', 5, 1],
43 | ]
44 | }
45 | ]
46 |
47 | export default MONSTER_DATA;
--------------------------------------------------------------------------------
/src/data/skill-data.js:
--------------------------------------------------------------------------------
1 | import { GetRange, GetRandom } from '../js/public-random-range';
2 |
3 | const SKILL_TABLE = [
4 | {
5 | id: 1000001,
6 | name: '斩',
7 | dsc : '简简单单的一击',
8 | label : ['普攻','伤害'],
9 | defaultTime : 1000,
10 | eventList: [
11 | `[11]enemy@beAttack@attacker.$r.$atk`
12 | ]
13 | },
14 | {
15 | id: 1000002,
16 | name: '魔',
17 | dsc : '造成双倍伤害,并恢复等同于伤害的血量,有一定几率使对方进入中毒状态.',
18 | label : ['吸血','加倍'],
19 | defaultTime : 3000,
20 | currentCoolTime : 3000,
21 | coolTime : 0,
22 | restrict : [
23 | // "[attacker]{$mp} >= {60}",
24 | // "[attacker]{$hp} <= {250}",
25 | // "[attacker]{$skills} nothas {1000003,1000001}",
26 | // "[attacker]{$status} has {2000001}",
27 | // "[skill]{coolTime} > {0}",
28 | function(skill, attacker, enemy){
29 | return true;
30 | }
31 | ],
32 | eventList: [
33 | `[11]
34 | enemy@beAttack@ attacker.$r.$atk * 2;
35 | `,{
36 | width: 11,
37 | eventStr : function(action, attacker, enemy){
38 | if(GetRandom(100)){
39 | action.change('enemy_changeState',[
40 | { id: 2000001, state: "ADD" }
41 | ])
42 | }
43 | }
44 | },{
45 | width: 91,
46 | eventStr : function(action, attacker, enemy){
47 | console.log(action)
48 | action.change('attacker_beCure', action.enemy_beAttack)
49 | }
50 | },
51 | ]
52 | // eventList: [
53 | // `[11]attacker@changeState@[{ id: 2000001, state: "REMOVE" }]`
54 | // ]
55 | },
56 | {
57 | id: 1000003,
58 | name: '圣光术',
59 | dsc : '恢复大量生命!',
60 | label : ['恢复'],
61 | defaultTime : 5000,
62 | restrict : [
63 | "[attacker]{$mp} >= {105}",
64 | ],
65 | eventList: [
66 | `[11]
67 | attacker@changeMp@-15;
68 | attacker@beCure@ attacker.$r.$maxHp * 0.5
69 | `
70 | ]
71 | },
72 | {
73 | id: 1000004,
74 | name: '净',
75 | dsc : '净化负面状态!',
76 | label : ['测试2','伤害2'],
77 | defaultTime : 10000,
78 | eventList: [
79 | `[11]attacker@changeState@[{ id: 2000001, state: "REMOVE" }]`
80 | ]
81 | // eventList: [
82 | // `[11]
83 | // enemy@beAttack@ attacker.$r.$atk * 2;
84 | // attacker@beAttack@ attacker.$r.$atk * 0.3
85 | // `
86 | // ]
87 | },
88 | {
89 | id: 1000005,
90 | name: '致死',
91 | logo: '盾',
92 | dsc : '造成大量伤害,有很大的几率一击必杀!',
93 | label : ['测试2','伤害2'],
94 | defaultTime : 10000,
95 | eventList: [
96 | `[11]
97 | action@{Math.random() > 0.98}
98 | #
99 | enemy@beAttack@9999999
100 | `,
101 | ]
102 | },
103 | // {
104 | // id: 1000006,
105 | // name: '测试精简数据',
106 | // logo: '毒',
107 | // dsc : '简简单单的一击',
108 | // label : ['普攻','伤害'],
109 | // defaultTime : 1000,
110 | // currentCoolTime : 1000,
111 | // coolTime : 0,
112 | // eventList : [
113 | // `[1]enemy@changeHp@attacker.$atk`,
114 | // `[2]attacker@changeHp@2`,
115 | // `[3]
116 | // action@{action.state.isCritical === true};
117 | // attacker@{attacker.$hp > (attacker.$hp * 0.5)}
118 | // #
119 | // enemy@changeState@[{ id: 2000001, state: "ADD" }];
120 | // enemy@changeHp@attacker.$atk
121 | // `
122 | // ,
123 | // `[4]action@{action.state.isCritical = true}`,
124 | // ]
125 | // }
126 | ];
127 |
128 | export default SKILL_TABLE
--------------------------------------------------------------------------------
/src/data/state-data.js:
--------------------------------------------------------------------------------
1 | // type: 1 作为攻击方时被提取;
2 | // type: 2 作为被攻击方时被提取;
3 | // type: 3 在判断所受伤害的时候被提取;
4 |
5 | const STATE_TABLE = [
6 | {
7 | id: 2000001,
8 | name: '中毒',
9 | type: '1',
10 | logo: '毒',
11 | color: 'black',
12 | dsc : '你中毒了,每回合将会减少HP!',
13 | label : ['状态'],
14 | stateEvent : function(hero) {
15 | var self = this;
16 | var duration = 5;
17 | var per = 1;
18 | var current = 1;
19 | self.stateEventTimer = setInterval(function(){
20 | hero.changeHp(-10);
21 | current +=1;
22 | if(current > 5){
23 | clearInterval(self.stateEventTimer);
24 | hero.removeList('$status',self);
25 | }
26 | }, per * 1000);
27 | this.actived = true;
28 | }
29 | },
30 | {
31 | id: 2000002,
32 | name: '坚盾',
33 | type: '3',
34 | logo: '盾',
35 | color: 'red',
36 | dsc : '坚守之盾,免疫50%伤害',
37 | label : ['测试','状态'],
38 | powerUp : {
39 | $dmgDown : [50, 1],
40 | },
41 | },
42 | {
43 | id: 2000003,
44 | name: '暴击伤害提升(50%)',
45 | type: '1',
46 | logo: '爆',
47 | color: 'red',
48 | dsc : '暴击伤害提升(50%)',
49 | label : ['状态'],
50 | eventList: [
51 | `[11]
52 | action@{action.state.isCritical === true}
53 | #
54 | action@{action.set('atk_per', 50)}
55 | `
56 | ]
57 | },
58 | {
59 | id: 2000004,
60 | name: '反伤(30%)',
61 | type: '2',
62 | logo: '反',
63 | color: 'red',
64 | dsc : '反弹30%伤害',
65 | label : ['测试','状态'],
66 | eventList: [
67 | `[91]attacker@beAttack@ action.enemy_beAttack * 0.3
68 | `
69 | ]
70 | },
71 | ]
72 |
73 | export default STATE_TABLE
--------------------------------------------------------------------------------
/src/directive/drop-item.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import CONSTANT from '../data/constant'
3 | import PGET from '../js/public-static-get'
4 | import store from '../store';
5 | import dragDrop from '../js/drag-drop';
6 | import moveClass from '../js/different-item-move-class'
7 |
8 | export default function (el, binding){
9 |
10 | let { hero, position } = binding.value;
11 |
12 | let event = {
13 | dragend (event){
14 | event.dataTransfer.clearData("text");
15 | return false;
16 | },
17 | dragover (event){
18 | event.preventDefault();
19 | return true;
20 | },
21 | dragstart (event){
22 | event.dataTransfer.setData("text", position);
23 | try {
24 | event.dataTransfer.setDragImage(!~event.target.className.indexOf("component-item") ? event.target.parentNode : event.target, 20, 20);
25 | } catch (error) {
26 | // pass
27 | }
28 | let itemPover = document.querySelector('.item-tool-tip-pover');
29 | itemPover && itemPover.parentNode.removeChild(itemPover);
30 | },
31 | drop (event){
32 | event.preventDefault();
33 |
34 | dragDrop(
35 | event.dataTransfer.getData("text"),
36 | position,
37 | hero
38 | );
39 |
40 | hero.updateAttribute();
41 |
42 | store.commit('UPDATE');
43 |
44 | }
45 | }
46 |
47 | // 如果是格子是空的则禁用拖动;
48 | {
49 | let block = new moveClass(position).get();
50 |
51 | if(block){
52 | el.setAttribute('draggable','true');
53 | }else{
54 | el.removeAttribute('draggable');
55 | }
56 | block = null;
57 | }
58 |
59 | // 创建事件,并销毁已销毁的事件;
60 | for(let key in event){
61 | let value = event[key],
62 | keyNameInElement = `${key}_EVENT_FUNCTION_ITEM_BLOCK`;
63 | el.removeEventListener(key, el[keyNameInElement]);
64 | el.addEventListener(key, value);
65 | el[keyNameInElement] = value;
66 | }
67 |
68 | };
69 |
--------------------------------------------------------------------------------
/src/directive/item-tool-tip.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import CONSTANT from '../data/constant'
3 | import PGET from '../js/public-static-get'
4 |
5 | require('../css/item-tool-tip.css');
6 |
7 | export default function(el, binding){
8 | let keyName = CONSTANT.UNIT_ATTR_NAME,
9 | itemLevel = CONSTANT.ITEM_LEVEL,
10 | tipClassName = '.item-tool-tip-pover',
11 | item = binding.value;
12 |
13 | let event = {
14 | mouseenter : function(e){
15 |
16 | event.mouseleave();
17 |
18 | let tip = document.createElement('div');
19 |
20 | let {right, top} = e.target.getBoundingClientRect();
21 |
22 | Object.assign(tip.style, {
23 | position : 'absolute',
24 | left : `${right}px`,
25 | top : `${top}px`
26 | })
27 |
28 | Object.assign(tip, {
29 | className : tipClassName.slice(1),
30 | innerHTML : `
31 |
32 | {{this.item.name}}
33 | + {{item.intensify}}
34 |
35 |
36 |
37 | {{v[0]}}
38 | {{v[1]}}
39 |
40 |
41 |
42 | 强化攻击力 + {{item.intPowerUp.$atk}}
43 | 无视伤害 + {{item.intPowerUp.$dmgDown[0] * 100 + '%'}}
44 |
45 | {{item.dsc}}
46 | `
47 | })
48 |
49 | document.body.appendChild(tip);
50 |
51 | new Vue({
52 | created(){
53 | this.item = item;
54 | this.itemColor = {
55 | color : itemLevel[this.item.grade || 0]
56 | }
57 | this.attr = _.map(this.item.equip, (v,k) => {
58 | let record = {v}, i = v;
59 |
60 | if(k === '$status' || k === '$skills'){
61 | return ['附加' + keyName[k], _.map(v, id => Vue.filter('itemKey')(id, 'name')).join(','), false, k.slice(1)];
62 | }
63 |
64 | if(Object.prototype.toString.call(v) === '[object Array]' && v[1] === 1 || v[1] === 3){
65 | record.v = v[0] + '%';
66 | i = v[0];
67 | }
68 |
69 | if(i > 0){
70 | record.v = "+" + record.v;
71 | }else{
72 | record.down = true;
73 | }
74 |
75 | return [keyName[k], record.v, record.down, k.slice(1)];
76 | }).sort(v => {
77 | return (v[3] === 'status' || v[3] === 'skills') ? 1 : 0
78 | })
79 | }
80 | }).$mount(tipClassName);
81 |
82 | },
83 | mouseleave : function(){
84 | let old = document.querySelector(tipClassName);
85 | // 移除已经存在的tip;
86 | if(old){
87 | old.parentNode.removeChild(old);
88 | }
89 | }
90 | }
91 |
92 | for(let key in event){
93 |
94 | let value = event[key],
95 | keyNameInElement = `${key}_EVENT_FUNCTION_ITEM_TOOL_TIP`;
96 |
97 | el.removeEventListener(key, el[keyNameInElement]);
98 |
99 | if(item){
100 | el.addEventListener(key, value);
101 | el[keyNameInElement] = value;
102 | }
103 |
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/filter.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import PGET from './js/public-static-get';
3 | import CONSTANT from './data/constant';
4 |
5 | Vue.filter('itemKey', function (id, key) {
6 | let data = PGET(id);
7 | return data[key] || data;
8 | })
9 |
10 | Vue.filter('heroAttrKey', function (key) {
11 | return CONSTANT.UNIT_ATTR_NAME[key]
12 | })
13 |
--------------------------------------------------------------------------------
/src/js/astar.js:
--------------------------------------------------------------------------------
1 | const Astar = function Astar(map, start, end) {
2 | function block(x, y) {
3 | this.x = x;
4 | this.y = y;
5 | this.parent = null;
6 | this.G = 0;
7 | this.H = null;
8 | this.getF = function() {
9 | return this.G + this.H;
10 | };
11 | }
12 | this.map = _.cloneDeep(map);
13 | this.init = function() {
14 | var opt = {
15 | startBlock : start,
16 | endBlock : end,
17 | stickList : _.filter(_.flattenDeep(this.map.mapData) ,{ block_type: '2'}),
18 | openList : [],
19 | closeList : [],
20 | isInList : function(block, type) {
21 | let index = _.findIndex(this[type],{
22 | x:block.x,
23 | y:block.y
24 | })
25 | return ~index && { index }
26 | }
27 | }
28 | _.assign(this.map, opt)
29 |
30 | this.map.openList.push(this.map.startBlock);
31 |
32 | var line = this.step();
33 | if (!line.find) {
34 | console.info('无法生存路径!');
35 | return [];
36 | }
37 | line = line.endBlock;
38 | let path = [];
39 | while (line.parent.parent) {
40 | line = line.parent;
41 | path.push(line);
42 | }
43 | path.reverse().push(end);
44 | return path;
45 | }
46 | this.step = function() {
47 | this.map.openList = this.map.openList.sort(function(a, b) {
48 | return a.getF() - b.getF();
49 | })
50 | var currentBlock = this.map.openList.shift();
51 | if (!currentBlock) {
52 | return {
53 | find: false
54 | }
55 | };
56 | this.map.closeList.push(currentBlock);
57 | var around = this.around(currentBlock);
58 | for (var i = 0; i < around.length; i++) {
59 | var _block = around[i];
60 | var index = this.map.isInList(_block, 'openList');
61 | _block.parent = currentBlock;
62 | _block.H = this.countH(_block);
63 | _block.G = this.countG(_block) + (currentBlock.G || 0);
64 | if (!index) {
65 | if (_block.x === this.map.endBlock.x && _block.y === this.map.endBlock.y) {
66 | return {
67 | find: true,
68 | endBlock: _block
69 | }
70 | }
71 | this.map.openList.push(_block);
72 | continue;
73 | }
74 | if ((currentBlock.G + this.countG(_block)) < this.map.openList[index.index].G) {
75 | this.map.openList[index.index].parent = currentBlock;
76 | }
77 | };
78 | return this.step();
79 | };
80 | this.around = function(currentBlock) {
81 | var list = [];
82 | for (var i = -1; i <= 1; i++) {
83 | for (var j = -1; j <= 1; j++) {
84 | if (i === 0 && j === 0) {
85 | continue;
86 | };
87 | if (i !== 0 && j !== 0) {
88 | continue;
89 | }
90 | var x = currentBlock.x + i;
91 | var y = currentBlock.y + j;
92 | if (x >= this.map.row || y >= this.map.col || x < 0 || y < 0) {
93 | continue;
94 | }
95 | var record = new block(x, y);
96 | if (this.map.isInList(record, 'closeList')) {
97 | continue;
98 | }
99 | if (this.map.isInList(record, 'stickList')) {
100 | continue;
101 | }
102 | list.push(record)
103 | }
104 | }
105 | return list;
106 | }
107 | this.countH = function(block) {
108 | var x = Math.abs(block.x - this.map.endBlock.x);
109 | var y = Math.abs(block.y - this.map.endBlock.y);
110 | return (x + y) * 10;
111 | }
112 | this.countG = function(block) {
113 | if (block.x !== block.parent.x && block.y !== block.parent.y) {
114 | return 14;
115 | } else {
116 | return 10;
117 | }
118 | }
119 | return this.init();
120 | }
121 |
122 | export default Astar
--------------------------------------------------------------------------------
/src/js/audio.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 |
3 | // let dist = {
4 | // 'backgroundMusic': require('static/audio/bgm.wav'),
5 | // 'fight-attack' : require('static/audio/fight-attack.wav'),
6 | // }
7 |
8 | let dist = {};
9 |
10 | let AudioList = [];
11 | window.AudioList = AudioList;
12 |
13 | let GameAudio = function (opt){
14 | this.$AudioList = AudioList;
15 | this.src = opt.src || dist[opt.key];
16 | this.id = this.src + Date.now();
17 | this.opt = Object.assign({
18 | autoplay : true,
19 | loop : false,
20 | },opt);
21 | // this.start();
22 | }
23 |
24 | GameAudio.prototype = {
25 | start : function(){
26 | let opt = this.opt;
27 | if(this.$el){
28 | this.stop();
29 | }
30 | this.$el = document.createElement('audio');
31 | this.$el.src = this.src;
32 | this.$el.volume = 0.2;
33 | (opt.autoplay) && (this.$el.autoplay = "autoplay");
34 | if(opt.loop){
35 | this.$el.loop = "loop";
36 | }else{
37 | this.$el.addEventListener('ended',() => this.stop(), false);
38 | }
39 | document.body.appendChild(this.$el);
40 | this.$AudioList.push(this.$el);
41 | },
42 | pause : function(){
43 | this.$el.pause();
44 | },
45 | stop : function(){
46 | this.$AudioList.splice(this.$AudioList.findIndex(a => a.id = this.id), 1);
47 | document.body.removeChild(this.$el);
48 | this.$el = null;
49 | }
50 | }
51 |
52 | export default GameAudio;
53 |
54 |
--------------------------------------------------------------------------------
/src/js/blueprint.js:
--------------------------------------------------------------------------------
1 | import store from '../store'
2 | import PGET from '../js/public-static-get';
3 |
4 | const BluePrint = function(opt){
5 | let hero = store.state.HeroStore.hero;
6 | let result = {
7 | success : false,
8 | msg : ''
9 | }
10 |
11 | if(!hero.isEnoughInPackage(opt.need)){
12 | return Object.assign(result, {
13 | success: false,
14 | msg : '材料不足!'
15 | })
16 | }
17 |
18 | let synthetics = opt.synthetics || [];
19 |
20 | synthetics = synthetics.map(v => {
21 | let item;
22 | if(({}).toString.call(v) === "[object Array]"){
23 | item = PGET(v[0]);
24 | console.log('array:',v[0]);
25 | v[1](item);
26 | }else{
27 | item = PGET(v)
28 | }
29 | return [item, item.num || 1];
30 | })
31 |
32 | if(hero.getItem(synthetics).length === 0){
33 | hero.getItem(synthetics, true);
34 | hero.costItem(opt.need);
35 | }else{
36 | return Object.assign(result, {
37 | success: false,
38 | msg : '背包不足!',
39 | })
40 | }
41 |
42 | return Object.assign(result, {
43 | success: true,
44 | msg : '锻造成功!',
45 | synthetics,
46 | })
47 |
48 | }
49 |
50 |
51 | export default BluePrint
--------------------------------------------------------------------------------
/src/js/cool-time-event.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 |
3 | const coolTimeEvent = function(currentCoolTime){
4 |
5 | let hero = store.state.HeroStore.hero;
6 |
7 | currentCoolTime = Math.max(1000, (1 - hero.$r.$coolTimePer / 100) * (currentCoolTime || this.defaultTime));
8 |
9 | this.coolTimeTimer && clearInterval(this.coolTimeTimer);
10 |
11 | this.coolTime = currentCoolTime;
12 |
13 | this.currentCoolTime = currentCoolTime;
14 |
15 | this.coolTimeTimer = setInterval( () => {
16 | this.coolTime -= 30;
17 | if(this.coolTime < 0){
18 | this.coolTime = 0;
19 | clearInterval(this.coolTimeTimer);
20 | }
21 | }, 30);
22 |
23 | }
24 |
25 | export default coolTimeEvent;
26 |
--------------------------------------------------------------------------------
/src/js/create-hero.js:
--------------------------------------------------------------------------------
1 | import { EXP_TABLE } from "../data/hero-data";
2 |
3 | const CreateHero = function(option = {}){
4 |
5 | let opt = _.cloneDeep(option);
6 |
7 | opt.$exp = opt.$exp || 0; // 当前经验
8 |
9 | opt.$level = opt.$level || 1;
10 |
11 | opt.$maxExp = opt.$maxExp || EXP_TABLE[opt.$level - 1]; // 升级经验
12 |
13 | opt.$package = opt.$package || new Array(40);
14 |
15 | opt.$houseList = opt.$houseList || new Array(40);
16 |
17 | opt.$equipments = opt.$equipments || [0,0,0,0,0,0,0,0,0];
18 |
19 | opt.$resource = opt.$resource || {
20 | gold : 50,
21 | gem : 0,
22 | };
23 |
24 | opt.$flashCopy = {
25 | $status: null,
26 | $skills: null,
27 | };
28 |
29 | opt.$attrGrow = {
30 | maxHp : 10,
31 | maxMp : 10,
32 | atk : 5,
33 | def : 2,
34 | str : 1, // 力量
35 | dex : 2, // 敏捷
36 | con : 3, // 体质
37 | int : 4 // 智力
38 | }
39 |
40 | _.assign(this, opt);
41 |
42 | };
43 |
44 | export default CreateHero;
--------------------------------------------------------------------------------
/src/js/create-monster.js:
--------------------------------------------------------------------------------
1 | import PublicStaticGet from './public-static-get'
2 |
3 | const CreateMonster = function(opt = {}){
4 |
5 | let option = _.cloneDeep(opt);
6 |
7 | // 精英
8 | if(!option.$elite){
9 | let rndNum = Math.floor(Math.random() * 100) + 1;
10 | if(rndNum <= 90){
11 | option.$elite = 1;
12 | }else if(rndNum <= 95){
13 | option.$elite = 1.3
14 | }else if(rndNum <= 98){
15 | option.$elite = 1.6
16 | }else if(rndNum <= 100){
17 | option.$elite = 2
18 | }
19 | }
20 | // 名称前缀
21 | if(!option.$nickname){
22 | if(option.$elite <=1){
23 | option.$nickname = "普通的"
24 | }else if(option.$elite <=1.3){
25 | option.$nickname = "威猛的"
26 | }else if(option.$elite <=1.6){
27 | option.$nickname = "发怒的"
28 | }else if(option.$elite <=2){
29 | option.$nickname = "变异的"
30 | }else{
31 | option.$nickname = "???"
32 | }
33 | }
34 |
35 | // 处理增幅;
36 | let amplification = ['$maxHp','$mapMp','$good','$gem','$exp','$atk','$def','$str','$dex','$con','$int']
37 | _.each(amplification,key => {
38 | option[key] && (option[key] = Math.ceil(option[key] * option.$elite));
39 | });
40 |
41 | // 处理初始属性;
42 | option.$hp = option.$maxHp;
43 | option.$mp = option.$maxMp;
44 |
45 | // 对象化 状态,技能;
46 | !option.$skills && (option.$skills = [1000001])
47 | option.$skills = _.map(option.$skills,function(id){
48 | return PublicStaticGet(id);
49 | });
50 |
51 | option.$status = _.map(option.$status,function(id){
52 | return PublicStaticGet(id);
53 | });
54 |
55 | // 删除非必要属性;
56 | delete option.$elite;
57 | delete this.$resource;
58 | delete this.$attrGrow;
59 | delete this.$package;
60 |
61 | // 合并数据;
62 | _.assign(this,option);
63 |
64 | // 删除不必要属性;
65 |
66 |
67 | }
68 |
69 | export default CreateMonster
--------------------------------------------------------------------------------
/src/js/different-item-move-class.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 |
3 | const eventList = {
4 | $intensify : {
5 | get: function(){
6 | return store.state.SmithyStore.intensifyItem;
7 | },
8 | set: function(obj){
9 | store.commit('ChangeIntensifyItem', obj);
10 | }
11 | },
12 | $default : {
13 | get: function(){
14 | let hero = store.state.HeroStore.hero;
15 | return hero && hero[this.position] && hero[this.position][this.index];
16 | },
17 | set: function(obj){
18 | let hero = store.state.HeroStore.hero;
19 | hero[this.position][this.index] = obj;
20 | }
21 | }
22 | }
23 |
24 | const moveClass = function(option){
25 |
26 | let opt = option.split('|');
27 |
28 | this.position = opt[0];
29 |
30 | this.index = Number(opt[1]);
31 |
32 | this.get = function(){
33 | throw new Error('移动物品品种类, item方法必须指定!');
34 | }
35 |
36 | this.set = function(){
37 | throw new Error('移动物品品种类, set方法必须指定!');
38 | }
39 |
40 | Object.assign(this, eventList[this.position] || eventList['$default'])
41 |
42 | }
43 |
44 | export default moveClass;
--------------------------------------------------------------------------------
/src/js/drag-drop.js:
--------------------------------------------------------------------------------
1 | import moveClass from '../js/different-item-move-class'
2 |
3 | const noting = function(from, to){
4 | if(from.position === to.position && from.index === to.index){
5 | return true;
6 | }
7 | }
8 |
9 | const destory = function(from, to){
10 | if(to.position === '$destory'){
11 | from.set();
12 | return true;
13 | }
14 | }
15 |
16 | const equip = function(from, to, hero){
17 | if(to.position === '$equipments'){
18 | hero.equip(from.get(), from.index, from.position);
19 | return true;
20 | }
21 | }
22 |
23 | const demount = function(from, to, hero){
24 | if(from.position === '$equipments'){
25 | if(to.get() && to.get().equipType === from.index){
26 | hero.equip(to.get(), to.index, to.position);
27 | return true;
28 | }
29 | }
30 | }
31 |
32 | const merge = function(from, to){
33 | if(from.get() && to.get() && (from.get().id === to.get().id) && from.get().pile && to.get().pile){
34 | to.get().num += from.get().num;
35 | from.set();
36 | return true;
37 | }
38 | }
39 |
40 | const change = function(from, to){
41 | let T = from.get();
42 | from.set(to.get());
43 | to.set(T);
44 | return true;
45 | }
46 |
47 | const initDrop = function(from, to, hero){
48 |
49 | from = new moveClass(from);
50 |
51 | to = new moveClass(to);
52 |
53 | for(let event of [noting, destory, equip, demount, merge, change]){
54 |
55 | let dropResult = event(from, to, hero);
56 |
57 | if(dropResult){
58 |
59 | console.info('[Move Event] From',from,'To',to);
60 |
61 | return dropResult;
62 |
63 | }
64 |
65 | }
66 |
67 | }
68 |
69 | export default initDrop;
--------------------------------------------------------------------------------
/src/js/dungeon-creater.js:
--------------------------------------------------------------------------------
1 | const DungeonCreater = function(opt) {
2 | // Dict
3 | this.$BLOCK_STICK = '2'; // 障碍
4 | this.$BLOCK_ROAD = '0'; // 可行走
5 | this.$BLOCK_PATH = '4'; // 路径
6 | this.$BLOCK_HERO = '1'; // 英雄
7 | this.$BLOCK_END = '3'; // 寻路终点
8 |
9 | // Create
10 | this.row = opt.row || 20;
11 | this.col = opt.col || 20;
12 | this.lines = opt.lines || 15;
13 | this.inflex = opt.inflex || 0.5;
14 |
15 | this.init = function() {
16 | var result = [];
17 | for (var i = 0; i < this.row; i++) {
18 | var arr = []
19 | for (var j = 0; j < this.col; j++) {
20 | arr.push({
21 | x: i,
22 | y: j,
23 | block_type: this.$BLOCK_STICK
24 | });
25 | }
26 | result.push(arr);
27 | }
28 | this.mapData = result;
29 | var times = 0;
30 | while (true) {
31 | var start = getRandomPosition(this.row, this.col);
32 | var end = getRandomPosition(this.row, this.col);
33 | if (start.x - end.x > (this.row * 0.5) && start.y - end.y > (this.col * 0.5)) {
34 | createLine.call(this, start, end, true);
35 | break;
36 | }
37 | }
38 | for (var i = 0; i < this.lines - 1;) {
39 | createLine.call(this, getRandomPosition(this.row, this.col), getRandomPosition(this.row, this.col)) && i++;
40 | if (times++ > 100) {
41 | break;
42 | }
43 | }
44 | return this.mapData;
45 | };
46 |
47 | function createLine(start, end, first) {
48 | // 生成线路: 采用从A - B的走法,中间随机曲折; _x,_y用于记录当前走到得节点位置,默认为起点;
49 | var _x = start.x;
50 | var _y = start.y;
51 | // 计算2点之间的距离;
52 | var distance_x = Math.abs(start.x - end.x);
53 | var distance_y = Math.abs(start.y - end.y);
54 | // 剩余的移动量;用于确保能够到达目标地点;
55 | var moveLeft = {
56 | x: distance_x,
57 | y: distance_y
58 | }
59 | // 用于存放需要被点亮的点的位置(最终的路径);
60 | var lines = [];
61 | // 循环生成曲折线路,直到走到目标地点;
62 | while (_x !== end.x && _y !== end.y) {
63 | // 进行X轴移动;
64 | var move_x = getRandom(0, distance_x, this.inflex, moveLeft.x);
65 | moveLeft.x -= move_x;
66 | for (var i = 0; i < move_x; i++) {
67 | if (start.x > end.x) {
68 | lines.push([_x - i, _y]);
69 | } else {
70 | lines.push([_x + i, _y]);
71 | }
72 | }
73 | if (start.x > end.x) {
74 | _x -= move_x;
75 | } else {
76 | _x += move_x;
77 | }
78 | // 进行Y轴移动;
79 | if (moveLeft.x === 0) {
80 | // 如果X轴已经走到同一线,则这一步Y轴需要直接走到终点;
81 | var move_y = moveLeft.y;
82 | } else {
83 | var move_y = getRandom(0, distance_y, this.inflex, moveLeft.y);
84 | }
85 | moveLeft.y -= move_y;
86 | for (var j = 0; j < move_y; j++) {
87 | if (start.y > end.y) {
88 | lines.push([_x, _y - j]);
89 | } else {
90 | lines.push([_x, _y + j]);
91 | }
92 | }
93 | if (start.y > end.y) {
94 | _y -= move_y;
95 | } else {
96 | _y += move_y;
97 | }
98 | }
99 | // 判断是否是第一条路径;
100 | // 如果非第一条路径,则需要确认此条路径经过了已有的路径,否则此条路径生成无效,将返回false;
101 | if (!first) {
102 | var isIn = false;
103 | lines.forEach(function(pos) {
104 | this.mapData[pos[0]][pos[1]].block_type === this.$BLOCK_ROAD && (isIn = true);
105 | }, this);
106 | if (!isIn) {
107 | return false;
108 | }
109 | }
110 | // 生成路径成功,将其更新至地图数据;
111 | lines.forEach(function(pos) {
112 | this.mapData[pos[0]][pos[1]].block_type = this.$BLOCK_ROAD;
113 | }, this)
114 | return true;
115 | }
116 |
117 | function getRandom(start, end, per, max) {
118 | var num = Number((Math.random() * (end - start) + start).toFixed(0));
119 | if (per) {
120 | num = Number((num * per).toFixed(0));
121 | if (max && (num > max)) {
122 | num = max;
123 | }
124 | }
125 | return num;
126 | }
127 |
128 | function getRandomPosition(x, y) {
129 | return {
130 | x: getRandom(0, x - 1),
131 | y: getRandom(0, y - 1),
132 | }
133 | }
134 |
135 | this.init();
136 | }
137 |
138 | export default DungeonCreater
--------------------------------------------------------------------------------
/src/js/event-class.js:
--------------------------------------------------------------------------------
1 | import Unit from '../js/unit-class';
2 | import PGET from '../js/public-static-get';
3 | import store from '../store';
4 | import Vue from 'vue';
5 |
6 | const MapDialogElementClassName = '.Map-Dialog-modal';
7 | const ShadowViewClassName = '.shadow-view';
8 |
9 | const MapDialog = function(event, callback){
10 |
11 | let modal = document.createElement('div');
12 | let shadowView = document.createElement('div');
13 | let view = document.querySelector('#router-view');
14 | let opt = {
15 | height : 300,
16 | width : 200,
17 | animated : 'animated zoomIn',
18 | backForce : 0.2,
19 | }
20 |
21 | shadowView.className = ShadowViewClassName.slice(1);
22 |
23 | // 背景层 创建;
24 | Object.assign(shadowView.style, {
25 | position : 'absolute',
26 | background : `rgba(0,0,0,${opt.backForce})`,
27 | width : `100%`,
28 | height : `100%`,
29 | left : `0px`,
30 | top : `0px`,
31 | zIndex : '5'
32 | })
33 |
34 | // 模态框 创建;
35 | Object.assign(modal.style, {
36 | position : 'absolute',
37 | width : `${opt.height}px`,
38 | height : `${opt.width}px`,
39 | left : `${(view.offsetWidth - opt.width)/2}px`,
40 | top : `${(view.offsetHeight - opt.height)/2}px`,
41 | zIndex : '6'
42 | })
43 |
44 | Object.assign(modal, {
45 | className : [ opt.animated, MapDialogElementClassName.slice(1) ].join(' '),
46 | innerHTML : `
47 | +
48 |
49 | {{this.record.msg}}
50 |
51 |
52 | 你愿意使用
53 |
54 | {{item[0] | itemKey('name')}}*{{item[1]}}
55 |
56 | 来交换
57 |
58 | {{item[0] | itemKey('name')}}*{{item[1]}}
59 |
60 | 吗?
61 |
62 |
63 |
66 |
67 |
68 | `
69 | })
70 |
71 | view.appendChild(modal);
72 | view.appendChild(shadowView);
73 |
74 | new Vue({
75 | store,
76 | created(){
77 | this.event = event;
78 | this.$i = 0;
79 | this.isEnd = false;
80 | this.next();
81 | },
82 | methods: {
83 | next(){
84 | if(this.$isEnd){
85 | this.close();
86 | return ;
87 | }
88 | this.record = transformEventObj(this.event.data[this.$i]);
89 | this.isEnd = (this.$i++ === (this.event.data.length-1));
90 | this.$forceUpdate();
91 | },
92 | action(e){
93 | e.call(this);
94 | },
95 | close(){
96 | closeModal();
97 | }
98 | }
99 | }).$mount(MapDialogElementClassName)
100 |
101 | function closeModal(){
102 | let modal = document.querySelector(MapDialogElementClassName),
103 | shadow = document.querySelector(ShadowViewClassName);
104 | modal && modal.parentNode.removeChild(modal);
105 | shadow && shadow.parentNode.removeChild(shadow);
106 | callback && callback();
107 | }
108 |
109 | function transformEventObj(opt){
110 | let record = _.cloneDeep(opt);
111 | if(typeof record === 'string'){
112 | record = {
113 | msg: record
114 | }
115 | }
116 | // "['我没有']{1,1}|['我有的!']{2,1}"
117 | let buttons = _.map(record.buttons,function(str){
118 | if(typeof str === 'object'){
119 | return str;
120 | }
121 | let strc = str;
122 | str = str.match(/\[([^]+)\]\{([^]+)\}/)
123 | let btn = {};
124 | btn.title = str[1];
125 | if(!str[2]){
126 | return btn;
127 | }
128 | if(strc[0] !== '#'){
129 | btn.action = function(){
130 | let [i, isEnd] = str[2].split(',');
131 | this.$i = Number(i);
132 | this.next();
133 | if(Number(isEnd)){
134 | this.isEnd = true;
135 | }
136 | }
137 | }else{
138 | let i = str[2].split(',');
139 | let isEnd = i.unshift();
140 | btn.action = function(){
141 | let need = opt.need || [];
142 | let get = opt.get || [];
143 | let unit = this.$store.state.HeroStore.hero;
144 | let enough = unit.isEnoughInPackage(need);
145 | if(!enough){
146 | this.$i = i[0]
147 | }else{
148 | let left = unit.getItem(get);
149 | if(left.length){
150 | this.$i = i[1]
151 | }else{
152 | unit.getItem(get, true);
153 | unit.costItem(need);
154 | this.$i = i[2]
155 | }
156 | }
157 | this.next();
158 | if(Number(isEnd)){
159 | this.isEnd = true;
160 | }
161 | }
162 | }
163 | return btn;
164 | })
165 | record.buttons = buttons;
166 |
167 | return record;
168 | }
169 |
170 | }
171 |
172 | const MapFight = function(opt){
173 |
174 | let data = {
175 | monsters: [ new Unit(opt) ]
176 | };
177 |
178 | store.state.FightStore.monsters = data.monsters;
179 |
180 | // 跳转到 战斗场景;
181 | location.href = "#/fight"
182 |
183 | }
184 |
185 | export {
186 | MapDialog,
187 | MapFight
188 | };
189 |
190 |
--------------------------------------------------------------------------------
/src/js/fight-action-class.js:
--------------------------------------------------------------------------------
1 | const actionClass = function(attacker, enemy){
2 |
3 | this.state = {
4 | attacker : attacker,
5 | enemy : enemy,
6 | isCritical : false,
7 | isMiss : false,
8 | isMust : false,
9 | holyDmge : 0,
10 | holyDmga : 0,
11 | cure : 0,
12 | cure_per : 0,
13 | atk : 0,
14 | atk_per : 0,
15 | }
16 |
17 | };
18 |
19 | actionClass.prototype.CountFinal = function(){
20 | let s = this.state;
21 | // this.attacker_beAttack this.enemy_beAttack;
22 | if(this.attacker_beAttack){
23 | this.attacker_beAttack = Math.floor(
24 | this.attacker_beAttack * (1 + s.atk_per / 100) + s.atk
25 | );
26 | }
27 | if(this.enemy_beAttack){
28 | this.enemy_beAttack = Math.floor(
29 | this.enemy_beAttack * (1 + s.atk_per / 100) + s.atk
30 | );
31 | if(s.isCritical){
32 | this.enemy_beAttack = Math.floor(
33 | this.enemy_beAttack * s.attacker.$critiDmg
34 | );
35 | }
36 | }
37 | if(this.attacker_beCure){
38 | this.attacker_beCure = Math.floor(
39 | this.attacker_beCure * (1 + s.cure_per / 100) + s.cure
40 | );
41 | }
42 | if(this.enemy_beCure){
43 | this.enemy_beCure = Math.floor(
44 | this.enemy_beCure * (1 + s.cure_per / 100) + s.cure
45 | );
46 | }
47 | }
48 |
49 | actionClass.prototype.init = function(){
50 | let s = this.state;
51 | let [v,per] = s.attacker.$r.$dmgDown;
52 | let atk = ((this.attacker_beAttack || 0) - v - s.attacker.$r.$def) * (1 - per / 100);
53 | atk = atk > 0 ? atk : 0;
54 | console.log(this)
55 | this.attacker_changeHp = (this.attacker_beCure || 0) - atk - s.holyDmga;
56 |
57 | [v,per] = s.enemy.$r.$dmgDown;
58 | atk = ((this.enemy_beAttack || 0) - v - s.enemy.$r.$def) * (1 - per / 100);
59 | atk = atk > 0 ? atk : 0;
60 |
61 | this.enemy_changeHp = (this.enemy_beCure || 0) - atk - s.holyDmge;
62 | this.state.isCritical && console.info('暴击!');
63 | this.state.isMiss && console.info('闪避!');
64 | this.state.isMust && console.info('必中!');
65 | delete this.attacker_beCure
66 | delete this.attacker_beAttack
67 | delete this.enemy_beCure
68 | delete this.enemy_beAttack
69 | // delete this.state
70 | }
71 |
72 | actionClass.prototype.change = function(key, value, cover) {
73 | if(this[key]){
74 | if(cover){
75 | this[key] = value;
76 | }else{
77 | if(this[key] instanceof Array){
78 | this[key] = this[key].concat(value);
79 | }else{
80 | this[key] += value;
81 | }
82 | }
83 | }else{
84 | this[key] = value;
85 | }
86 | }
87 |
88 | actionClass.prototype.set = function(key, value, index){
89 | if(index === undefined){
90 | this.state[key] += value
91 | }else{
92 | this.state[key][index] += value
93 | }
94 | }
95 |
96 | export default actionClass;
--------------------------------------------------------------------------------
/src/js/fight.js:
--------------------------------------------------------------------------------
1 | import coolTimeEvent from './cool-time-event'
2 | import actionClass from './fight-action-class'
3 | import SkillAvailable from './skill-available'
4 | import { GetRange, GetRandom } from './public-random-range';
5 | import GameAudio from './audio'
6 | import store from '../store';
7 |
8 | const Fight = (attacker, enemy, skill) => {
9 |
10 |
11 | // 判断是否可以行动;
12 | if(!SkillAvailable(skill, attacker, enemy)){
13 | store.commit('FightEventLogPush',`未满足施法条件!`);
14 | return ;
15 | }
16 |
17 | // 触发冷却;
18 | coolTimeEvent.call(skill);
19 |
20 | // 开始计算打击事件;
21 | let event = _.concat(
22 | _.filter(attacker.$status, { type: '1' }),
23 | _.filter(enemy.$status, { type: '2' }),
24 | [ skill ]
25 | );
26 |
27 | // 提取事件;
28 | var eventList = [];
29 |
30 | // 拼接父技能;
31 | _.each(event, skill => {
32 | _.each(skill.eventList, item => {
33 | if(item.width){
34 | eventList.push(item);
35 | return;
36 | }
37 | eventList.push({
38 | father : skill,
39 | eventStr : item,
40 | weight : Number(item.match(/\[(\d+)\]/)[1])
41 | })
42 | })
43 | });
44 |
45 | // 排序;
46 | eventList = _.sortBy(eventList, ['weight']);
47 |
48 | // 初始化行动对象;
49 | var action = new actionClass(attacker, enemy);
50 |
51 | let opt = {
52 | attacker, enemy, action
53 | };
54 |
55 | // 插入规则中间缓冲
56 | eventList.push(
57 | // action 层
58 | {
59 | width: 10,
60 | father : skill,
61 | eventStr : function(action, attacker, enemy){
62 | action.state.isCritical = GetRandom(attacker.$r.$critical / 100);
63 | action.state.isMiss = GetRandom(enemy.$r.$dodge / 100);
64 | }
65 | },
66 | // CountFinal 层 , 需要最终伤害为基准的事件之前;
67 | {
68 | width: 90,
69 | father : skill,
70 | eventStr : function(action, attacker, enemy){
71 | action.CountFinal();
72 | }
73 | },
74 | // 最终层 , 永远在事件列队的最后;
75 | {
76 | width: 9999,
77 | father : skill,
78 | eventStr : function(action, attacker, enemy){
79 | action.init();
80 | }
81 | }
82 | )
83 |
84 | // 计算最终行动对象;
85 | _.each(eventList, item => {
86 | let father = item.father;
87 | var eventStr,ruleStr;
88 | if(typeof item.eventStr === 'function'){
89 | item.eventStr.apply(father, [action, attacker, enemy]);
90 | return;
91 | }
92 | var str = item.eventStr.replace(/[\r\n\s]/g,"").replace(/\[(\d+)\]/,"").split('#');
93 | if(str.length > 1){
94 | ruleStr = str[0];
95 | eventStr = str[1];
96 | }else{
97 | eventStr = str[0];
98 | }
99 |
100 | if(ruleStr){
101 | ruleStr = ruleStr.split(';');
102 | for(let i = 0; i{
112 | let e = estr.match(/(\w+)@(\w+)@([^]+)/);
113 | if(!e){
114 | e = estr.match(/action@\{([^]+)\}/);
115 | if(!e){
116 | console.warn('不能识别的技能效果码!', item);
117 | return
118 | };
119 | funcStr += `${e[1]};`;
120 | }else{
121 | let [, target,func,parm] = e;
122 | funcStr += `action.change('${target + '_' + func}',${parm.replace('@',',')});`;
123 | }
124 | })
125 |
126 | new Function('action','attacker','enemy', funcStr ).apply(father, [action, attacker, enemy]);
127 |
128 | })
129 |
130 | let attackerLogName = attacker.$type === 'Hero' ? '你' : attacker.$showName;
131 | let enemyLogName = enemy.$type === 'Hero' ? '你' : enemy.$showName;
132 | let skillLogName = `${skill.name}`;
133 |
134 | attackerLogName = `${attackerLogName}`;
135 | enemyLogName = `${enemyLogName}`;
136 |
137 |
138 | let msg = `${attackerLogName} 释放了 ${skillLogName}`;
139 |
140 | if(!(action.state.isMiss && !action.state.isMust)){
141 | if(action.attacker_changeHp){
142 | let word = action.attacker_changeHp > 0 ? '恢复': '减少';
143 | let v = `${Math.abs(action.attacker_changeHp)}`
144 | msg +=`,${attackerLogName}${word}了${v}点HP`;
145 | }
146 |
147 | if(action.enemy_changeHp){
148 | let v = `${-action.enemy_changeHp}`
149 | msg +=`,对${enemyLogName}造成${v}点伤害`;
150 | }
151 |
152 | if(action.state.isCritical){
153 | msg = `[暴击]` + msg;
154 | }
155 | }
156 |
157 | store.commit('FightEventLogPush',msg);
158 |
159 | // 判断闪避
160 | if(action.state.isMiss && !action.state.isMust){
161 | store.commit('FightEventLogPush',`[Miss]${enemyLogName} 闪避了 ${attackerLogName} 的攻击!`);
162 | return ;
163 | }
164 |
165 | new GameAudio({
166 | key : 'fight-attack'
167 | });
168 |
169 | // 获取双方变更行动对象;
170 | var actionList = {
171 | attacker: {},
172 | enemy: {}
173 | };
174 |
175 | for(var item in action){
176 | item = item.match(/^([\w]+)_([\w]+)$/);
177 | if(item){
178 | actionList[item[1]][item[2]] = action[item[0]];
179 | }
180 | }
181 |
182 | // ======== 处理 ========
183 |
184 | // attacker
185 | for(var item in actionList.attacker){
186 | if(attacker[item]){
187 | attacker[item](actionList.attacker[item]);
188 | }else{
189 | console.warn('战斗中发现未知事件:' , item , actionList.attacker[item]);
190 | }
191 | }
192 |
193 | // enemy
194 | for(var item in actionList.enemy){
195 | if(attacker[item]){
196 | enemy[item] && enemy[item](actionList.enemy[item]);
197 | }else{
198 | console.warn('战斗中发现未知事件:' , item , actionList.enemy[item]);
199 | }
200 | }
201 |
202 | // 全局冷却 1秒;
203 | _.each(attacker.$skills, skill => {
204 | if(skill.coolTime < 300){
205 | coolTimeEvent.call(skill, 1000)
206 | }
207 | })
208 |
209 | // 返回是否存活; 目前无任何实际用途;
210 | return attacker.$alive && enemy.$alive;
211 |
212 | }
213 |
214 | export default Fight;
--------------------------------------------------------------------------------
/src/js/hero/update-attribute.js:
--------------------------------------------------------------------------------
1 | export default function updateAttribute(){
2 |
3 | // 记录当前血量,魔量百分比;
4 | let hp_per = Math.min(this.$hp / (this.$r.$maxHp || this.$maxHp), 1);
5 | let mp_per = Math.min(this.$mp / (this.$r.$maxMp || this.$maxMp), 1);
6 |
7 | let promote = {
8 | // 基础值 基础百分 高级值 高级百分
9 | // ((默认 + 基础值) * (1 + 基础百分) + 高级值) * (1 + 高级百分)
10 | $maxHp : [0,0,0,0], // 血量最大值
11 | $maxMp : [0,0,0,0], // 魔法最大值
12 | $atk : [0,0,0,0], // 攻击
13 | $def : [0,0,0,0], // 防御
14 | $str : [0,0,0,0], // 力量
15 | $dex : [0,0,0,0], // 敏捷
16 | $con : [0,0,0,0], // 体质
17 | $int : [0,0,0,0], // 智力
18 | $critical : [0,0,0,0], // 暴击几率 90 => 暴击率90%
19 | $dodge : [0,0,0,0], // 闪避几率 90 => 闪避率90%
20 | $coolTimePer : [0,0,0,0], // 冷却时间减少 10 => 冷却时间减少10%
21 | $critiDmg : [0,0,0,0], // 暴击伤害倍数 1.5 => 1.5倍
22 | $dmgDown : [0,0], // 伤害减少 [5,10], 免伤 5 + 10%
23 | }
24 |
25 | let data = (this.$equipments || []).concat(this.$status || []);
26 |
27 | let action = {
28 | $default : function(key,v){
29 | this.$r[key] = Math.floor(((this[key] + v[0]) * (1 + v[1] / 100) + v[2]) * (1 + v[3] / 100));
30 | },
31 | $dmgDown : function(key,v){
32 | this.$r[key] = v;
33 | }
34 | }
35 |
36 | // 计算基础属性
37 | for(let item of data){
38 |
39 | if(!item){
40 | continue;
41 | }
42 |
43 | let opt = [item.equip, item.powerUp, item.intPowerUp];
44 |
45 | for(let obj of opt){
46 | if(!obj){
47 | continue;
48 | }
49 | for(let key in obj){
50 | let v = obj[key];
51 |
52 | if(!promote[key]){
53 | continue;
54 | }
55 |
56 | let index = 0,up = v;
57 |
58 | if(v instanceof Array){
59 | index = v[1];
60 | up = v[0];
61 | }
62 |
63 | promote[key][index] += up;
64 | }
65 | }
66 |
67 | };
68 |
69 | for(let key in promote){
70 | action[action[key] ? key : '$default'].apply(this, [key, promote[key]]);
71 | }
72 |
73 | // 计算属性对属性的影响;
74 |
75 | action = [
76 | function $str(){
77 | this.$r.$atk += (this.$r.$str * (this.$r.$atk * 0.004));
78 | },
79 | function $int(){
80 | this.$r.$maxMp += (this.$r.$int * (this.$r.$maxMp * 0.004));
81 | },
82 | function $con(){
83 | this.$r.$maxHp += (this.$r.$con * (this.$r.$maxHp * 0.004));
84 | },
85 | function $dex(){
86 | this.$r.$critical += (this.$r.$critical * 0.005)
87 | this.$r.$dodge += (this.$r.$critical * 0.001)
88 | }
89 | ]
90 |
91 | for(let act of action){
92 | act.call(this);
93 | }
94 |
95 | this.$hp = Math.floor(hp_per * this.$r.$maxHp) || 0;
96 | this.$mp = Math.floor(mp_per * this.$r.$maxMp) || 0;
97 |
98 | }
--------------------------------------------------------------------------------
/src/js/intensify.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 | import { GetRange, GetRandom } from './public-random-range';
3 | import GameAudio from './audio'
4 |
5 | let audio = function(){
6 | let data = {
7 | // success : require('static/audio/intensify-success.ogg'),
8 | // fail : require('static/audio/intensify-fail.ogg'),
9 | // zero : require('static/audio/intensify-zero.ogg'),
10 | // broken : require('static/audio/intensify-broken.ogg')
11 | }
12 | return function(key){
13 | new GameAudio({
14 | src : data[key]
15 | });
16 | }
17 | }();
18 |
19 | const excuteSuccess = {
20 | type : function(){
21 | if(!this.equip){
22 | return {
23 | success: false,
24 | msg : '此物品无法被强化!'
25 | };
26 | }
27 | },
28 | good : function(hero){
29 | let grade = [1, 1.1, 1.2, 1.5, 2], // 品级系数;
30 | base = 100,
31 | // 计算花费
32 | cost = (() => {
33 | let g = grade[this.grade || 0];
34 | let l = (this.level || 1) * base;
35 | let i = this.intensify || 0;
36 | return (l + l * i) * g;
37 | })();
38 | if(!hero.cost(cost)){
39 | return {
40 | success: false,
41 | msg: '金币不足!'
42 | }
43 | }
44 | },
45 | }
46 |
47 | const isSuccess = function(item){
48 | let probability = [1, 1, 1, 0.95, 0.9, 0.8, 0.75, 0.62, 0.54, 0.41, 0.33, 0.28, 0.2, 0.17, 0.13, 0.1, 0.04, 0.03, 0.01, 0.01]
49 | let intensify = item.intensify || 0;
50 | return GetRandom(probability[intensify]);
51 | }
52 |
53 | const failureHandle = function(item){
54 | let intensify = item.intensify || 0;
55 | if(intensify < 3){
56 | // noop
57 | audio('fail');
58 | }else if(intensify < 8){
59 | // 退级 1-3之间;
60 | audio('fail');
61 | intensify -= GetRange([1,2]);
62 | item.intensify = Math.min(intensify, 3);
63 | }else if(intensify < 10){
64 | item.intensify = 0;
65 | audio('zero');
66 | }else{
67 | // 摧毁
68 | audio('broken');
69 | store.commit('ChangeIntensifyItem', null);
70 | }
71 | return item;
72 | }
73 |
74 | const excutePowerUp = function(item){
75 | let excute = {
76 | weapon : function(item){
77 | // 强化(武器)
78 | // '白','绿','蓝','紫','橙装'
79 | let coeffA = [8, 8, 10, 11, 12, 13][item.grade || 0];
80 | let coeffB = [0.4, 0.7, 1, 1.25, 1.4][item.grade || 0];
81 | let coefficient = [1, 2, 3, 4, 6, 8, 10, 12, 14, 17, 33, 50, 67, 108, 150, 192, 267, 360, 417, 500][item.intensify - 1];
82 | let positionCoeff = [1, 1, 1, 1, 1, 1, 1, 1, 1][item.equipType];
83 | let level = item.level || 1;
84 | item.intPowerUp = {
85 | $atk : Math.floor([level + coeffA/ 8 ] * coefficient * coeffB * positionCoeff)
86 | }
87 | // 伤害计算公式: [level + coeffA/ 8 ] * coefficient * coeffB * positionCoeff
88 | },
89 | armor : function(item){
90 | // 强化(防具)
91 | let coeffA = [1, 1.75, 2.5, 3.25, 3.75][item.grade || 0];
92 | let coefficient = [1, 2, 3, 5, 7, 9, 12, 14, 16, 20, 40, 60, 80, 130, 180, 230, 320, 410, 500][item.intensify - 1]
93 | let positionCoeff = [1, 1, 1, 1, 1, 1, 1, 1, 1][item.equipType];
94 | let base = 0.04 / 100;
95 | item.intPowerUp = {
96 | $dmgDown : [+(base * coefficient * coeffA * positionCoeff).toFixed(2), 1]
97 | }
98 | }
99 | };
100 | return item.equipType === 0 ? excute.weapon(item) : excute.armor(item);
101 | }
102 |
103 | const Intensify = function(item){
104 |
105 | let result = {
106 | success: true,
107 | msg : '',
108 | }
109 |
110 | // 计算是否符合强化要求;
111 | let validate = {
112 | type : [],
113 | good : [store.state.HeroStore.hero],
114 | };
115 |
116 | for(let key in validate){
117 | let re = excuteSuccess[key].apply(item, validate[key]);
118 | if(re){
119 | audio('fail');
120 | return re;
121 | }
122 | }
123 |
124 | // 计算是否强化成功
125 | if(!isSuccess(item)){
126 | failureHandle(item);
127 | Object.assign(result, {
128 | success: false,
129 | msg : "强化失败!"
130 | })
131 | }else{
132 | audio('success');
133 | item.intensify ? (item.intensify++) : (item.intensify = 1);
134 | Object.assign(result, {
135 | success: true,
136 | msg : "强化成功!"
137 | })
138 | }
139 |
140 | // 重新计算强化数据
141 | excutePowerUp(item);
142 |
143 | return result;
144 | }
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | export default Intensify;
--------------------------------------------------------------------------------
/src/js/map-hero-move.js:
--------------------------------------------------------------------------------
1 | import CONSTANT from '../data/constant';
2 | import { MapDialog, MapFight } from '../js/event-class';
3 |
4 | const HeroMoveEvent = function(map, $VueScope){
5 | let key_up = 38,
6 | key_down = 40,
7 | key_left = 37,
8 | key_right = 39,
9 | move_delay = 300,
10 | can_move = true,
11 | block_type = CONSTANT.MAP_BLOCK_TYPE;
12 |
13 | this.start = function(){
14 | this.keyUpFunc = event => {
15 | if(!event.keyCode){
16 | return ;
17 | }
18 | this.autoMoveTimer && clearInterval(this.autoMoveTimer);
19 | this.move(event.keyCode);
20 | };
21 | document.addEventListener('keyup', this.keyUpFunc);
22 | };
23 |
24 | this.stop = function(){
25 | document.removeEventListener('keyup', this.keyUpFunc)
26 | clearInterval(this.autoMoveTimer);
27 | };
28 |
29 | this.move = function(direction){
30 | if(!can_move){
31 | return ;
32 | }
33 |
34 | can_move = false;
35 |
36 | setTimeout(()=>{
37 | can_move = true;
38 | },move_delay)
39 |
40 | let next_block,
41 | x = map.hero.x,
42 | y = map.hero.y
43 |
44 | switch(direction){
45 | case key_up :
46 | x--;
47 | break;
48 | case key_down :
49 | x++;
50 | break;
51 | case key_left :
52 | y--;
53 | break;
54 | case key_right :
55 | y++;
56 | break;
57 | }
58 |
59 | try{
60 | next_block = map.$data.mapData[x][y];
61 | }catch(e){
62 | /*Pass*/
63 | }
64 |
65 | if(!next_block || next_block.block_type != block_type['ROAD']){
66 | return false;
67 | }
68 |
69 | // 将当前格子设置为 Road;
70 | map.hero.block_type = block_type['ROAD'];
71 | // 将目标格子标识为 Hero;
72 | next_block.block_type = block_type['HERO'];
73 |
74 | // 更新视图;
75 | $VueScope.$forceUpdate();
76 |
77 | let {event_type, event} = next_block.event || {};
78 |
79 | // 战斗事件不应该被保留;
80 | if(event_type === 'Monster'){
81 | delete next_block.event;
82 | }
83 |
84 | // 设置新的英雄位置;
85 | map.hero = next_block;
86 |
87 | //判断 初始化 执行事件
88 | switch(event_type){
89 | case 'Monster':
90 | MapFight(event)
91 | break;
92 | case 'MapDialog':
93 | MapDialog(event, () => this.start())
94 | break;
95 | }
96 |
97 | }
98 |
99 | this.autoMoveTimer = null;
100 |
101 | this.autoMove = function(path){
102 | this.autoMoveTimer && clearInterval(this.autoMoveTimer);
103 | let _path = _.cloneDeep(path);
104 | this.autoMoveTimer = setInterval(()=>{
105 |
106 | let next = _path.splice(0,1)[0],
107 | x = map.hero.x,
108 | y = map.hero.y,
109 | direction;
110 |
111 | if(!next){
112 | this.autoMoveTimer && clearInterval(this.autoMoveTimer);
113 | return ;
114 | }
115 |
116 | switch(true){
117 | case next.x < x:
118 | direction = key_up
119 | break;
120 | case next.x > x:
121 | direction = key_down
122 | break;
123 | case next.y < y:
124 | direction = key_left
125 | break;
126 | case next.y > y:
127 | direction = key_right
128 | break;
129 | }
130 |
131 | this.move(direction);
132 |
133 | if(_path.length < 1){
134 | clearInterval(this.autoMoveTimer);
135 | };
136 |
137 | }, move_delay + 100);
138 | }
139 |
140 | this.start();
141 |
142 | return this;
143 | }
144 |
145 | export default HeroMoveEvent;
--------------------------------------------------------------------------------
/src/js/map-init.js:
--------------------------------------------------------------------------------
1 | import DungeonCreater from '../js/dungeon-creater';
2 | import CONSTANT from '../data/constant';
3 | import PGET from '../js/public-static-get';
4 |
5 | let blockType = CONSTANT.MAP_BLOCK_TYPE;
6 |
7 | const Map = function(o){
8 |
9 | let opt = this.$opt = _.cloneDeep(o),
10 | getBlankRandomBlock = size => {
11 | return _.sampleSize(
12 | _.filter( _.flattenDeep(this.$data.mapData), item => {
13 | if (item.block_type != blockType['ROAD'] || item.event){
14 | return false;
15 | }
16 | return true;
17 | }),
18 | size
19 | );
20 | }
21 |
22 | this.$data = opt.mapData || new DungeonCreater(opt.mapInitOption || {});
23 |
24 | this.hero = (() => {
25 | let hero = getBlankRandomBlock(1)[0];
26 | hero.block_type = blockType['HERO'];
27 | return hero;
28 | })();
29 |
30 |
31 | for(let id of (opt.monsterList || []).concat(opt.eventList || [])){
32 |
33 | let event = PGET(id);
34 |
35 | if(!id){
36 | console.warn('No Such Map Event Object:', id, opt);
37 | continue;
38 | }
39 |
40 | for(let block of getBlankRandomBlock(opt.rule[String(id)])){
41 | block.event = {
42 | event_type: event.type || event.$type || 'unknown',
43 | event,
44 | };
45 | }
46 |
47 | }
48 |
49 |
50 | // monsterList: [
51 | // 5000001, 5000002
52 | // ],
53 | // eventList: [
54 | // 7000001
55 | // ], // 特殊事件触发点;
56 | // rule: { // 生成规则;
57 | // "5000001" : 4,
58 | // "5000003" : 5,
59 | // "7000001" : 1,
60 | // }
61 | }
62 |
63 |
64 | export default Map;
65 |
--------------------------------------------------------------------------------
/src/js/monster-ai.js:
--------------------------------------------------------------------------------
1 | import Fight from './fight';
2 | import SkillAvailable from './skill-available';
3 |
4 | const MonsterAI = function(hero, monster){
5 |
6 | this.monster = monster;
7 |
8 | this.hero = hero;
9 |
10 | this.startTimer = null;
11 |
12 | this.start = function(){
13 | let delay = 1100;
14 | this.startTimer = setInterval(() => {
15 | let skill = this.monster.EventAI && this.monster.EventAI() || this.normalAi();
16 | skill && Fight(this.monster, this.hero, skill) && console.info('AI自动释放:' , skill.name);
17 | },delay)
18 | };
19 |
20 | this.end = function(){
21 | this.startTimer && clearInterval(this.startTimer);
22 | };
23 |
24 | this.normalAi = function (){
25 | let skills = this.monster.$skills;
26 | for(let i = 0; i< skills.length; i++){
27 | if(SkillAvailable(skills[i], this.monster, this.hero)){
28 | return skills[i];
29 | }
30 | };
31 | return false;
32 | }
33 |
34 | this.start();
35 |
36 | }
37 |
38 | export default MonsterAI;
--------------------------------------------------------------------------------
/src/js/public-function.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | window._ = {
4 | cloneDeep : require('lodash/cloneDeep'),
5 | each : require('lodash/each'),
6 | concat : require('lodash/concat'),
7 | map : require('lodash/map'),
8 | range : require('lodash/range'),
9 | sortBy : require('lodash/sortBy'),
10 | sampleSize : require('lodash/sampleSize'),
11 | filter : require('lodash/filter'),
12 | find : require('lodash/find'),
13 | findIndex : require('lodash/findIndex'),
14 | flattenDeep : require('lodash/flattenDeep'),
15 | random : require('lodash/random'),
16 | every : require('lodash/every'),
17 | assign : Object.assign,
18 | shuffle : require('lodash/shuffle'),
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/src/js/public-random-range.js:
--------------------------------------------------------------------------------
1 | const GetRange = function(opt){
2 | if (({}).toString.call(opt) === "[object Array]"){
3 | return _.random.apply(null,opt);
4 | }
5 | return opt || 0;
6 | }
7 |
8 | const GetRandom = function(opt){
9 | return Math.random() < opt;
10 | }
11 |
12 | export {
13 | GetRange,
14 | GetRandom
15 | };
--------------------------------------------------------------------------------
/src/js/public-static-get.js:
--------------------------------------------------------------------------------
1 | import { DIALOG_DATA } from '../data/event-data'
2 | import { EXP_TABLE } from '../data/hero-data'
3 | import { ITEM_TABLE } from '../data/item-data'
4 | import MAP_TABLE from '../data/map-data'
5 | import SKILL_TABLE from '../data/skill-data'
6 | import STATE_TABLE from '../data/state-data'
7 | import MONSTER_DATA from '../data/monster-data'
8 | import BLUEPRINT_TABLE from '../data/blueprint-data'
9 |
10 | const Data = {
11 | '0' : EXP_TABLE,
12 | '1' : SKILL_TABLE,
13 | '2' : STATE_TABLE,
14 | '3' : ITEM_TABLE,
15 | '4' : BLUEPRINT_TABLE,
16 | '5' : MONSTER_DATA,
17 | // '6' : MISSION_TABLE,
18 | '7' : DIALOG_DATA,
19 | '8' : MAP_TABLE,
20 | // '9' : ACHIEVEMENT_TABLE,
21 | }
22 |
23 | const PublicStaticGet = function(key){
24 | let Head = key.toString()[0];
25 | let record = Data[Head];
26 | let result = _.cloneDeep(_.find(record, {
27 | id: key
28 | })) || key;
29 |
30 | if(result.defaultTime){
31 | result.coolTime = 0;
32 | result.currentCoolTime = result.defaultTime;
33 | }
34 |
35 | return result;
36 | }
37 |
38 | export default PublicStaticGet;
--------------------------------------------------------------------------------
/src/js/release-skill.js:
--------------------------------------------------------------------------------
1 | import coolTimeEvent from './cool-time-event'
2 | import Fight from '../js/fight'
3 | import store from '../store'
4 |
5 | const SkillEvent = function(hero, monster){
6 | this.hero = hero;
7 | this.monster = monster;
8 | this.keyDownFunc = null;
9 | this.start();
10 | }
11 |
12 | SkillEvent.prototype = {
13 | start (){
14 | this.end();
15 | this.keyDownFunc = event => {
16 | let index;
17 | // 技能监听;
18 | let skillHotKey = store.state.ConfigStore.skillHotKey;
19 | index = skillHotKey.indexOf(event.keyCode);
20 | if(~index){
21 | let skill = this.hero.$skills[index];
22 | skill && Fight(this.hero, this.monster, skill);
23 | }
24 | // 物品监听;
25 | let itemHotKey = store.state.ConfigStore.itemHotKey;
26 | index = itemHotKey.indexOf(event.keyCode);
27 | if(~index){
28 | console.log('Use: $package',index);
29 | this.hero.use({
30 | position: '$package',
31 | index
32 | });
33 | }
34 | };
35 | document.addEventListener('keydown', this.keyDownFunc);
36 | },
37 | end (){
38 | document.removeEventListener('keydown',this.keyDownFunc);
39 | }
40 | }
41 |
42 | export default SkillEvent;
43 |
44 |
--------------------------------------------------------------------------------
/src/js/save-load.js:
--------------------------------------------------------------------------------
1 | import Unit from '../js/unit-class'
2 | import MAP_TABLE from '../data/map-data'
3 | import MONSTER_DATA from '../data/monster-data';
4 | import SKILL_TABLE from "../data/skill-data";
5 | import STATE_TABLE from "../data/state-data";
6 | import {ITEM_TABLE} from '../data/item-data';
7 | import PGET from '../js/public-static-get';
8 | import store from '../store';
9 | import Vue from 'vue';
10 |
11 | let SaveKey = 'ENDLESS_QWHIDHIASDYQWD';
12 |
13 | const SaveGame = function(){
14 | // hero
15 | // config
16 | // store.state.HeroStore.hero = hero;
17 |
18 | let hero = _.cloneDeep(store.state.HeroStore.hero);
19 | let config = _.cloneDeep(store.state.ConfigStore);
20 |
21 | hero.__proto__ = {};
22 |
23 | delete hero.$r;
24 | delete hero.$flashCopy;
25 | delete hero.$attrGrow;
26 |
27 | // 处理特殊物品,技能,状态;
28 | // for(let key of ['$status','$skills','$package','$houseList','$equipments']){
29 | // if(!hero[key]) continue;
30 | // hero[key] = hero[key].map(v => {
31 | // return pack(v);
32 | // })
33 | // }
34 |
35 | let saveString = JSON.stringify({
36 | hero,
37 | config,
38 | });
39 |
40 | localStorage.setItem(SaveKey,saveString);
41 |
42 | }
43 |
44 | function pack(v){
45 |
46 | if(!v){
47 | return 0;
48 | }
49 | if(v.equip && v.intensify){
50 | return {
51 | id: v.id,
52 | intensify : v.intensify,
53 | intPowerUp : v.intPowerUp,
54 | };
55 | }
56 | return v.id
57 | }
58 |
59 | function unpack(v){
60 | if(!v){
61 | return undefined;
62 | }
63 | if(typeof v === 'number'){
64 | return PGET(v)
65 | }else{
66 | return Object.assign(PGET(v.id), v);
67 | }
68 | }
69 |
70 | const LoadGame = function(){
71 |
72 | let saveString = localStorage.getItem(SaveKey);
73 |
74 | // console.log(saveString);
75 | saveString = '';
76 |
77 | if(saveString){
78 | let {hero, config} = _.cloneDeep(JSON.parse(saveString));
79 |
80 | for(let key of ['$status','$skills','$package','$houseList','$equipments']){
81 | if(!hero[key]) continue;
82 | hero[key] = hero[key].map(v => {
83 | // console.log(unpack(v));
84 | return unpack(v);
85 | })
86 | }
87 |
88 | Vue.set(store.state.HeroStore,'hero', new Unit(hero));
89 | store.commit('ConfigUpdate', config);
90 | }else{
91 | // 生成默认测试英雄
92 | let test1 = PGET(3000001);
93 | test1.num = 10;
94 | let test2 = PGET(3000002);
95 | test2.num = 5;
96 |
97 | var hero = new Unit(
98 | {
99 | $showName : 'Bastarder',
100 | $type : 'Hero',
101 | $skills : [PGET(1000001),PGET(1000002),PGET(1000003),PGET(1000004)],
102 | $status : [],
103 | $resource : {
104 | gold: 999999999,
105 | gem : 899999
106 | },
107 | $blueprint: [
108 | 4000001, 4000002
109 | ],
110 | $package : [test1,test2].concat(_.cloneDeep(ITEM_TABLE).slice(2)).concat(new Array(26))
111 | }
112 | );
113 | Vue.set(store.state.HeroStore,'hero', hero);
114 | }
115 |
116 | SaveGame();
117 |
118 | }
119 |
120 |
121 | export {
122 | SaveGame,
123 | LoadGame
124 | }
--------------------------------------------------------------------------------
/src/js/skill-available.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 |
3 | const SkillAvailable = function(skill, attacker, enemy){
4 |
5 | /**
6 | * 全局条件
7 | */
8 |
9 | // 冷却;
10 | if(skill.coolTime > 0){
11 | store.commit('FightEventLogPush',`技能冷却中!`);
12 | return false;
13 | }
14 |
15 | // 人物状态,是否可行动 等等 暂留;
16 |
17 | /**
18 | * 附加条件
19 | */
20 |
21 | if(!skill.restrict){
22 | return true;
23 | }
24 |
25 | let _length = _.filter(skill.restrict, function(rule){
26 |
27 | // 如果是高级规则,则直接判断返回值;
28 | if(typeof rule === "function"){
29 | return rule(skill, attacker, enemy);
30 | }
31 |
32 | // 简易规则,计算结果;
33 | let _rule = rule.replace(/\s/g,'').match(/^\[([\w$]+)\]\{([\w$]+)\}([\w\W]+)\{([\w\W]+)\}/);
34 |
35 | if(!_rule){
36 | return false;
37 | }
38 |
39 | let [ , type, key, condition, targetValue] = _rule;
40 |
41 | var originObject = '';
42 |
43 | // 判断对象;
44 | switch(type){
45 | case 'attacker':
46 | originObject = attacker;
47 | break;
48 | case 'skill' :
49 | originObject = skill;
50 | break;
51 | case 'enemy':
52 | originObject = enemy;
53 | break;
54 | }
55 |
56 | originObject = originObject[key];
57 |
58 | switch(condition){
59 | case 'has':
60 | targetValue = targetValue.split(',');
61 | for(let i = 0; i' :
76 | return originObject > parseInt(targetValue);
77 | case '<' :
78 | return originObject < parseInt(targetValue);
79 | case '>=' :
80 | return originObject >= parseInt(targetValue);
81 | case '<=' :
82 | return originObject <= parseInt(targetValue);
83 | }
84 | // "[attacker]{$mp} > {100}",
85 | // "[attacker]{$hp} < {300}",
86 | // "[skill]{coolTime} > {0}",
87 | // "[attacker]{$skills} has {}",
88 | // "[attacker]{$status} nothas {}",
89 | }).length;
90 |
91 | return skill.restrict.length === _length;
92 | }
93 |
94 | export default SkillAvailable;
--------------------------------------------------------------------------------
/src/js/unit-class.js:
--------------------------------------------------------------------------------
1 | import { EXP_TABLE } from "../data/hero-data";
2 | import SKILL_TABLE from "../data/skill-data";
3 | import STATE_TABLE from "../data/state-data";
4 | import { ITEM_TABLE } from "../data/item-data";
5 | import store from '../store';
6 | import CreateMonster from './create-monster';
7 | import CreateHero from './create-hero';
8 | import { GetRange, GetRandom } from './public-random-range';
9 | import PGET from '../js/public-static-get';
10 | import coolTimeEvent from './cool-time-event';
11 | import Vue from 'vue';
12 |
13 | // prototype
14 | import updateAttribute from './hero/update-attribute'
15 |
16 |
17 |
18 | function Unit(obj = {}){
19 | this.id = 1000 + (Math.random()* 1000).toFixed(0) // 编号
20 | this.$type = 'Hero'; // 单位类型
21 | this.$showName = 'unit' // 展示名称
22 | this.$level = 0; // 等级
23 | this.$alive = true;
24 | this.$status = [];
25 | this.$skills = []; // 技能列表
26 |
27 | // 属性方面
28 | this.$hp = 600; // 当前生命值
29 | this.$mp = 400; // 当前魔法值
30 | this.$maxHp = 600; // 生命最大值
31 | this.$maxMp = 400; // 魔法最大值
32 | this.$atk = 10; // 攻击
33 | this.$def = 0; // 防御
34 | this.$str = 10; // 力量
35 | this.$dex = 10; // 敏捷
36 | this.$con = 10; // 体质
37 | this.$int = 10; // 智力
38 | this.$critical = 3; // 暴击几率
39 | this.$dodge = 5; // 闪避几率
40 | this.$coolTimePer = 0; // 冷却缩短
41 | this.$critiDmg = 1.5; // 暴击伤害倍数;
42 | this.$dmgDown = [0,0]; // 伤害减免;
43 | this.$r = {};
44 |
45 | switch(obj.$type){
46 | case 'Monster' :
47 | CreateMonster.call(this, obj);
48 | break;
49 | case 'Hero' :
50 | CreateHero.call(this, obj);
51 | break;
52 | }
53 |
54 | this.updateAttribute();
55 | }
56 |
57 | Unit.prototype = {
58 | startFight,
59 | endFight,
60 | updateAttribute,
61 | changeMp,
62 | changeHp,
63 | startFight,
64 | endFight,
65 | updateAttribute,
66 | changeMp,
67 | changeHp,
68 | getExp,
69 | getList,
70 | removeList,
71 | changeState,
72 | reset,
73 | dieDrop,
74 | itemSort,
75 | getItem,
76 | equip,
77 | demount,
78 | isEnoughInPackage,
79 | use,
80 | cost,
81 | costItem,
82 | }
83 |
84 | /* --------------- */
85 |
86 | function startFight(){
87 |
88 | for(let key in this.$flashCopy){
89 | this.$flashCopy[key] = _.cloneDeep(this[key]);
90 | }
91 |
92 | for(let state of (this.$status || [])){
93 | state.stateEvent && state.stateEvent(this);
94 | }
95 |
96 | this.updateAttribute();
97 | }
98 |
99 | function endFight(){
100 |
101 | for(let state of (this.$status || [])){
102 | state.stateEventTimer && clearInterval(state.stateEventTimer);
103 | }
104 |
105 | Object.assign(this, this.$flashCopy);
106 |
107 | !this.$alive && this.reset();
108 |
109 | this.updateAttribute();
110 | }
111 |
112 | function changeMp(value) {
113 |
114 | let v = parseInt(value);
115 |
116 | if(!v){
117 | return false;
118 | }
119 |
120 | let mp = Math.min(this.$mp + v, this.$r.$maxMp);
121 |
122 | this.$mp = mp < 0 ? 0 : mp;
123 |
124 | return true;
125 | }
126 |
127 | function changeHp(value) {
128 |
129 | let v = parseInt(value);
130 |
131 | if(!v || this.$hp <= 0){
132 | return false;
133 | }
134 |
135 | let hp = Math.min(this.$hp + value, this.$r.$maxHp); // 更新Hp的值;
136 |
137 | if(hp <= 0){
138 | this.$hp = 0;
139 | this.$alive = false;
140 | }else{
141 | this.$hp = hp;
142 | }
143 |
144 | return true;
145 | }
146 |
147 | function getExp(value) {
148 |
149 | let ExpTable = EXP_TABLE,
150 | v = parseInt(value);
151 |
152 | if(!v || !this.$maxExp){
153 | return false;
154 | }
155 |
156 | let exp = this.$exp + v;
157 |
158 | if(exp >= this.$maxExp){
159 |
160 | this.$exp = parseInt(exp % this.$maxExp);
161 |
162 | this.$level += parseInt(exp / this.$maxExp);
163 |
164 | this.$maxExp = ExpTable[this.$level - 1] || 0;
165 |
166 | for(var key in this.$attrGrow){
167 |
168 | this[key] += this.$attrGrow[key];
169 |
170 | }
171 |
172 | this.updateAttribute();
173 |
174 | }else{
175 |
176 | this.$exp = exp;
177 |
178 | }
179 |
180 | return true;
181 |
182 | }
183 |
184 | function getList(key, opt, isIndex){
185 | let list = this[key];
186 |
187 | if(!list){
188 | return false;
189 | }
190 |
191 | let condition;
192 |
193 | if(typeof opt === 'number'){
194 | condition = i => i && (i.id === opt);
195 | }
196 |
197 | if(typeof opt === 'object'){
198 | condition = i => i && (i.id === opt.id);
199 | }
200 |
201 | if(typeof opt === 'function'){
202 | condition = opt;
203 | }
204 |
205 | if(!condition){
206 | return false;
207 | }
208 |
209 | return isIndex ? list.findIndex(condition) : list.find(condition);
210 | }
211 |
212 | function removeList(key, opt) {
213 | let index,
214 | list = this[key];
215 |
216 | if(typeof opt === 'object'){
217 | index = list.findIndex(i => i.id === opt.id);
218 | }else{
219 | index = opt;
220 | }
221 |
222 | if(!~index || !list){
223 | return false;
224 | }
225 |
226 | if(key === '$status'){
227 | list[index].stateEventTimer && clearInterval(list[index].stateEventTimer);
228 | }
229 |
230 | let remove = list.splice(index,1);
231 |
232 | this.updateAttribute();
233 |
234 | return remove;
235 | }
236 |
237 | function changeState(changeList) {
238 |
239 | if(!changeList || !changeList.length){
240 | return ;
241 | }
242 |
243 | changeList.forEach(item => {
244 | let id = item.id;
245 | let state = this.getList('$status', {id});
246 | switch (item.state){
247 | case 'ADD':
248 |
249 | if(state){
250 | break;
251 | }
252 |
253 | state = Object.assign( PGET(id), item.action || {} );
254 |
255 | state.stateEvent && state.stateEvent(this);
256 |
257 | this.$status.push(state);
258 |
259 | break;
260 | case 'REMOVE':
261 |
262 | this.removeList('$status', {id});
263 |
264 | break;
265 | case 'CHANGE':
266 |
267 | if(state){
268 |
269 | state = Object.assign( state, item.action );
270 |
271 | }
272 |
273 | break;
274 | }
275 | })
276 |
277 | this.updateAttribute();
278 |
279 | }
280 |
281 | function reset(){
282 |
283 | this.$alive = true;
284 |
285 | this.$hp = this.$maxHp;
286 |
287 | this.$mp = this.$mp;
288 |
289 | }
290 |
291 | function dieDrop(){
292 | // 数据范例
293 | // $dropList : [
294 | // // 物品ID, 数量范围, 几率
295 | // [3000001, [3, 10], 1],
296 | // [3000002, 5, 0.3],
297 | // [3000003, 1, 0.5],
298 | // ['gold',[1, 80], 1],
299 | // ['exp', 1, 1]
300 | // ]
301 | let data = this.$dropList || [],
302 | consequence = [];
303 |
304 | data.forEach(item => {
305 |
306 | let num = GetRange(item[1]),
307 | odds = GetRandom(item[2]);
308 |
309 | if(!num || !odds || !item[0] || !item[1] || !item[2]){
310 | return ;
311 | }
312 |
313 | consequence.push([
314 | item[0], num
315 | ])
316 |
317 | });
318 |
319 | return consequence;
320 | }
321 |
322 | function itemSort(type){
323 | let list = this[type];
324 |
325 | if(!list || !list.length){
326 | return false;
327 | }
328 |
329 | list.sort(
330 | (a, b) => ((a.id || Infinity) - (b.id || Infinity))
331 | );
332 |
333 | store.commit('UPDATE');
334 |
335 | return true;
336 | }
337 |
338 | function getItem(data, force, type = '$package'){
339 |
340 | let container = force ? this[type] : _.cloneDeep(this[type]),
341 | surplus = [];
342 |
343 | if(!container || !data || !data.length){
344 | console.warn('[unit.getItem Error]:', data, force, t, this);
345 | return surplus;
346 | }
347 |
348 | data.forEach(i => {
349 | let item,
350 | num = i[1];
351 |
352 | if(typeof i[0] === 'object'){
353 | item = i[0];
354 | }else{
355 | item = PGET(i[0]);
356 | }
357 |
358 | switch(item){
359 | case "gold":
360 | force && (this.$resource.gold += num);
361 | return;
362 | case 'gem':
363 | force && (this.$resource.gem += num);
364 | return;
365 | case 'exp':
366 | force && (this.getExp(num));
367 | return;
368 | }
369 |
370 | let itemInPackage = this.getList(type, { id: item.id }),
371 | nextBlankPlace = container.findIndex(item => !item);
372 |
373 | item.pile && (item.num = num);
374 |
375 | // 可堆叠
376 | if(itemInPackage && item.pile){
377 | // 存在
378 | itemInPackage.num = (itemInPackage.num || 0) + num;
379 | }else{
380 | // 不存在
381 | if(~nextBlankPlace){
382 | // 有空位
383 | container[nextBlankPlace] = item;
384 | }else{
385 | // 没空位
386 | surplus.push(i);
387 | }
388 | }
389 |
390 | })
391 |
392 | store.commit('UPDATE');
393 |
394 | return surplus;
395 | }
396 |
397 | function equip(item, index, type = '$package'){
398 | //0武器 1护肩 2鞋子 3腰带 4上衣 5绑腿 6戒指 7护腕 8项链
399 | let container = this[type],
400 | $equipments = this.$equipments,
401 | equip = item.equip;
402 |
403 | if(!equip || !container || !$equipments){
404 | return false;
405 | }
406 |
407 | // 删除包裹中的装备, 如果已有装备, 卸载装备;
408 | container[index] = undefined;
409 |
410 | if($equipments[item.equipType]){
411 | this.demount(item.equipType, index, type);
412 | }
413 |
414 | $equipments[item.equipType] = item;
415 |
416 | // 更新附加状态;
417 | let status = equip.$status || [];
418 |
419 | status.forEach(id => {
420 | this.changeState({
421 | id,
422 | state : 'ADD',
423 | action : { notShow: true }
424 | })
425 | })
426 |
427 | this.updateAttribute();
428 |
429 | store.commit('UPDATE');
430 |
431 | return true;
432 | }
433 |
434 | function demount(equipType, index, type = '$package'){
435 | let container = this[type],
436 | equipItem = this.$equipments[equipType],
437 | $equipments = this.$equipments;
438 |
439 | $equipments[equipType] = 0;
440 |
441 | if(!equipItem){
442 | return ;
443 | }
444 |
445 | index = index || container.findIndex(i => !i);
446 |
447 | if(~index){
448 | container[index] = equipItem;
449 | }else{
450 | $equipments[equipType] = equipItem;
451 | return false;
452 | }
453 |
454 | for(let key in (equipItem.equip || {})){
455 | if(~['$skills', '$status'].indexOf(key)){
456 | equipItem.equip[key].forEach( id => this.removeList(key, {id}) );
457 | }
458 | }
459 |
460 | this.updateAttribute();
461 |
462 | store.commit('UPDATE');
463 |
464 | }
465 |
466 | function costItem(list, type = '$package'){
467 | let container = this[type];
468 |
469 | for(let opt of list){
470 | let index = this.getList(type, opt[0], true);
471 |
472 | let num = (()=>{
473 | if(typeof opt[1] === 'function'){
474 | return opt[1].call(this);
475 | }
476 | return opt[1];
477 | })();
478 |
479 | container[index].num -= num;
480 |
481 | if(!container[index].num){
482 | container[index] = undefined;
483 | }
484 | }
485 |
486 | }
487 |
488 | function isEnoughInPackage(list, type = '$package'){
489 | // [
490 | // [200001, 5]
491 | // [function(item){ return item.id === 200001}, function(item){ return 10; }, '测试物品1','10']
492 | // ]
493 | let container = this[type];
494 |
495 | if(!container || !list || !list.length ){
496 | return false;
497 | }
498 |
499 | for(let opt of list){
500 | let item;
501 |
502 | item = this.getList(type, opt[0]);
503 |
504 | let num = (()=>{
505 | if(typeof opt[1] === 'function'){
506 | return opt[1].call(this);
507 | }
508 | return opt[1];
509 | })();
510 |
511 | if(!item || (item.num || 1) < num){
512 | return false;
513 | }
514 | }
515 |
516 | return true;
517 | }
518 |
519 | function use(option){
520 | let container = this[option.position || '$package'],
521 | item = container[option.index];
522 |
523 | // 暂时不支持战斗时更换装备,所以只判断消耗品;
524 | if(!item || !item.use){
525 | return false;
526 | }
527 |
528 | let use = typeof item.use === 'function' ? item.use.call(this) : item.use;
529 |
530 | if(use.defaultTime){
531 | if(!item.hasOwnProperty('coolTime')){
532 | Vue.set(item, 'defaultTime', use.defaultTime);
533 | Vue.set(item, 'coolTime', 0);
534 | Vue.set(item, 'currentCoolTime', use.defaultTime);
535 | }
536 | }else{
537 | Vue.set(item, 'coolTime', 0);
538 | }
539 |
540 | if(item.coolTime > 0){
541 | console.log('冷却中!');
542 | return false;
543 | }
544 |
545 | if(!item.num){
546 | console.log('物品数量不足!');
547 | return false;
548 | }
549 |
550 | for(let restrict of (use.restrict || [])){
551 | if(!restrict.call(this)){
552 | return false;
553 | }
554 | }
555 |
556 | for(let effect of (use.effect || [])){
557 | effect.call(this);
558 | }
559 |
560 | if(--item.num < 1){
561 | container[option.index] = undefined;
562 | }else{
563 | coolTimeEvent.call(item);
564 | }
565 |
566 | }
567 |
568 | function cost(num, type = "gold"){
569 |
570 | let costNum = Math.ceil(Number(num)) || 0,
571 | container = this.$resource;
572 |
573 | if(!costNum || costNum > container[type]){
574 | return false;
575 | }else{
576 | container[type] -= costNum;
577 | return true;
578 | }
579 |
580 | }
581 |
582 | export default Unit;
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from "vue-router";
3 |
4 | import GameHome from './components/game-home.vue'
5 | import GameFight from './components/game-fight.vue'
6 | import GameMap from './components/game-map.vue'
7 | import GameMapActive from './components/game-map-active.vue'
8 | import GameLogin from './components/game-login.vue'
9 | import GameConfig from './components/game-config.vue'
10 | import GameSmithy from './components/game-smithy.vue'
11 | import GameShop from './components/game-shop.vue'
12 |
13 | Vue.use(VueRouter)
14 |
15 | const routes = [
16 | {
17 | path: '/login',
18 | name: 'login',
19 | component: GameLogin
20 | },{
21 | path: '/',
22 | name: 'home',
23 | component: GameHome
24 | },{
25 | path: '/fight',
26 | name: 'fight',
27 | component: GameFight
28 | },{
29 | path: '/map',
30 | name: 'map',
31 | component: GameMap
32 | },{
33 | path: '/map-active',
34 | name: 'mapActive',
35 | component: GameMapActive
36 | },{
37 | path: '/config',
38 | name: 'config',
39 | component: GameConfig
40 | },{
41 | path: '/smithy',
42 | name: 'smithy',
43 | component: GameSmithy
44 | },{
45 | path: '/shop',
46 | name: 'shop',
47 | component: GameShop
48 | }
49 | ]
50 |
51 | const router = new VueRouter({
52 | routes ,
53 | })
54 |
55 | router.beforeEach((to, from, next) => {
56 | // if(from.path === to.path){
57 | // location.reload();
58 | // }
59 | next();
60 | });
61 |
62 | export default router;
63 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | import FightStore from './store/fight-store';
5 | import HeroStore from './store/hero-store';
6 | import MapStore from './store/map-store';
7 | import ConfigStore from './store/config-store';
8 | import SmithyStore from './store/smithy-store';
9 |
10 | Vue.use(Vuex)
11 |
12 | const store = new Vuex.Store({
13 | modules: {
14 | FightStore,
15 | HeroStore,
16 | MapStore,
17 | ConfigStore,
18 | SmithyStore,
19 | },
20 | state: {
21 | UPDATE: 1,
22 | },
23 | mutations: {
24 | UPDATE(state){
25 | Vue.set(state,'UPDATE', Math.random() + Date.now());
26 | },
27 | }
28 | })
29 |
30 | export default store;
31 |
--------------------------------------------------------------------------------
/src/store/config-store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const state = {
4 | volumeEffectMusic : 0.5,
5 | volumeBackgroundMusic : 0.5,
6 | skillAvailableFightTip : true,
7 | skillHotKey : [81, 87, 69, 82],
8 | itemHotKey : [49, 50, 51, 52],
9 | }
10 |
11 | const mutations = {
12 | ConfigUpdate (state, option){
13 |
14 | // 修改音量
15 | for(let el of window.AudioList){
16 | el.volume = option.volumeBackgroundMusic;
17 | }
18 |
19 | Object.assign(state, option);
20 |
21 | }
22 | }
23 |
24 | export default {
25 | state,
26 | mutations
27 | }
--------------------------------------------------------------------------------
/src/store/fight-store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Unit from '../js/unit-class'
3 | import MONSTER_DATA from '../data/monster-data';
4 |
5 | const state = {
6 | eventLogs : [],
7 | monsters : [
8 | new Unit(_.cloneDeep(MONSTER_DATA[0])),
9 | new Unit(_.cloneDeep(MONSTER_DATA[1]))
10 | ]
11 | }
12 |
13 | // mutations
14 | const mutations = {
15 | // FightEvent;
16 | FightEventLogPush(state, log){
17 | if(!log){
18 | return ;
19 | }
20 | let now = new Date().toString().slice(16, 16 + 8);
21 | state.eventLogs.push(
22 | `[${now}] ${log}.`
23 | );
24 | },
25 | FightEventLogClear(state){
26 | Vue.set(state, 'eventLogs', []);
27 | }
28 | }
29 |
30 | export default {
31 | state,
32 | mutations
33 | }
--------------------------------------------------------------------------------
/src/store/hero-store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | const state = {
5 | hero : null,
6 | }
7 |
8 | const mutations = {
9 |
10 | }
11 |
12 | export default {
13 | state,
14 | mutations
15 | }
--------------------------------------------------------------------------------
/src/store/map-store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import MAP_TABLE from '../data/map-data'
3 |
4 | const state = {
5 | mapList: _.cloneDeep(MAP_TABLE),
6 | map: null,
7 | }
8 |
9 | const mutations = {
10 |
11 | }
12 |
13 | export default {
14 | state,
15 | mutations
16 | }
--------------------------------------------------------------------------------
/src/store/smithy-store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 |
3 | const state = {
4 | intensifyItem : 0,
5 | }
6 |
7 | const mutations = {
8 | ChangeIntensifyItem (state, newItem){
9 | Vue.set(state, 'intensifyItem', newItem);
10 | }
11 | }
12 |
13 | export default {
14 | state,
15 | mutations
16 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var webpack = require('webpack')
3 |
4 | module.exports = {
5 | entry: './src/app.js',
6 | output: {
7 | path: path.resolve(__dirname, './dist'),
8 | publicPath: 'dist/',
9 | filename: 'build.js'
10 | },
11 | resolveLoader: {
12 | root: path.join(__dirname, 'node_modules'),
13 | },
14 | resolve: {
15 | alias:{
16 | vue: 'vue/dist/vue.js',
17 | static : '../assets'
18 | }
19 | },
20 | module: {
21 | loaders: [
22 | {
23 | test: /\.css$/,
24 | loader: 'style!css'
25 | },
26 | {
27 | test: /\.vue$/,
28 | loader: 'vue'
29 | },
30 | {
31 | test: /\.js$/,
32 | loader: 'babel',
33 | exclude: /node_modules/
34 | },
35 | {
36 | test: /\.(png|jpg|gif|svg|wav|ogg|wma|otf)$/,
37 | loader: 'file',
38 | query: {
39 | name: '[name].[ext]?[hash]'
40 | }
41 | }
42 | ]
43 | },
44 | devServer: {
45 | historyApiFallback: false,
46 | noInfo: true
47 | },
48 | devtool: '#eval-source-map'
49 | }
50 |
51 | if (process.env.NODE_ENV === 'production') {
52 | module.exports.devtool = '#source-map'
53 | module.exports.plugins = (module.exports.plugins || []).concat([
54 | new webpack.DefinePlugin({
55 | 'process.env': {
56 | NODE_ENV: '"production"'
57 | }
58 | }),
59 | new webpack.optimize.UglifyJsPlugin({
60 | compress: {
61 | warnings: false
62 | }
63 | })
64 | ])
65 | }
66 |
--------------------------------------------------------------------------------