@sjtdev/koishi-plugin-dota2tracker 1.0.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/lib/index.js +559 -1038
  2. package/package.json +2 -1
  3. package/readme.md +9 -7
  4. package/template/{hero.html → hero/hero_1.ejs} +543 -516
  5. package/template/images/logo_dire.png +0 -0
  6. package/template/images/logo_radiant.png +0 -0
  7. package/template/images/scepter.png +0 -0
  8. package/template/images/scepter_0.png +0 -0
  9. package/template/images/scepter_1.png +0 -0
  10. package/template/images/shard.png +0 -0
  11. package/template/images/shard_0.png +0 -0
  12. package/template/images/shard_1.png +0 -0
  13. package/template/match/match_1.ejs +997 -0
  14. package/template/match/match_2.ejs +440 -0
  15. package/template/player/player_1.ejs +543 -0
  16. package/template/match.html +0 -1027
  17. package/template/player.html +0 -414
  18. /package/{images → template/images}/flag_dire.png +0 -0
  19. /package/{images → template/images}/flag_radiant.png +0 -0
  20. /package/{images → template/images}/hero_badge_1.png +0 -0
  21. /package/{images → template/images}/hero_badge_2.png +0 -0
  22. /package/{images → template/images}/hero_badge_3.png +0 -0
  23. /package/{images → template/images}/hero_badge_4.png +0 -0
  24. /package/{images → template/images}/hero_badge_5.png +0 -0
  25. /package/{images → template/images}/hero_badge_6.png +0 -0
  26. /package/{images → template/images}/medal_0.png +0 -0
  27. /package/{images → template/images}/medal_1.png +0 -0
  28. /package/{images → template/images}/medal_2.png +0 -0
  29. /package/{images → template/images}/medal_3.png +0 -0
  30. /package/{images → template/images}/medal_4.png +0 -0
  31. /package/{images → template/images}/medal_5.png +0 -0
  32. /package/{images → template/images}/medal_6.png +0 -0
  33. /package/{images → template/images}/medal_7.png +0 -0
  34. /package/{images → template/images}/medal_8.png +0 -0
  35. /package/{images → template/images}/medal_8b.png +0 -0
  36. /package/{images → template/images}/medal_8c.png +0 -0
  37. /package/{images → template/images}/star_0.png +0 -0
  38. /package/{images → template/images}/star_1.png +0 -0
  39. /package/{images → template/images}/star_2.png +0 -0
  40. /package/{images → template/images}/star_3.png +0 -0
  41. /package/{images → template/images}/star_4.png +0 -0
  42. /package/{images → template/images}/star_5.png +0 -0
@@ -0,0 +1,440 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Document</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" />
8
+ <style>
9
+ p {
10
+ margin: 0;
11
+ }
12
+
13
+ img {
14
+ width: 100%;
15
+ vertical-align: middle;
16
+ }
17
+
18
+ html,
19
+ body {
20
+ width: 800px;
21
+ }
22
+
23
+ nav {
24
+ font-size: 14px;
25
+ line-height: 1.8;
26
+ height: 72px;
27
+ color: #fff;
28
+ background-color: #000;
29
+ display: flex;
30
+ flex-direction: row;
31
+ justify-content: space-around;
32
+ align-items: center;
33
+ }
34
+
35
+ nav > div > p:first-of-type {
36
+ font-weight: bold;
37
+ }
38
+
39
+ nav .match_id p:first-of-type {
40
+ font-size: 16px;
41
+ }
42
+
43
+ nav > div.match_id > p.success::after {
44
+ content: "※录像分析成功";
45
+ color: #7ba334;
46
+ }
47
+ nav > div.match_id > p.fail::after {
48
+ content: "※分析结果不完整";
49
+ color: #ffb400;
50
+ }
51
+
52
+ .radiant {
53
+ color: #3c9028;
54
+ }
55
+
56
+ .dire {
57
+ color: #9c3628;
58
+ }
59
+
60
+ .match_result {
61
+ font-weight: bold;
62
+ height: 49px;
63
+ display: flex;
64
+ justify-content: center;
65
+ align-items: center;
66
+ }
67
+
68
+ .match_result .win {
69
+ margin: 0 25px;
70
+ }
71
+
72
+ .match_result .win.radiant::after {
73
+ content: "天辉获胜";
74
+ }
75
+
76
+ .match_result .win.dire::after {
77
+ content: "夜魇获胜";
78
+ }
79
+
80
+ .players {
81
+ display: flex;
82
+ flex-direction: column;
83
+ }
84
+
85
+ .panel,
86
+ .player {
87
+ padding: 0 10px;
88
+ }
89
+
90
+ .panel {
91
+ padding-top: 4px;
92
+ height: 40px;
93
+ border-top: 3px solid #fff;
94
+ font-size: 13.3px;
95
+ display: grid;
96
+ grid-template-columns: 32px 56px 378px repeat(4,1fr);
97
+ }
98
+ .panel.radiant {
99
+ border-color: #3c9028;
100
+ }
101
+ .panel.dire {
102
+ border-color: #9c3628;
103
+ }
104
+ .panel p{
105
+ line-height: 16px;
106
+ margin-left: 8px;
107
+ }
108
+ .panel .win{
109
+ font-size: 20px;
110
+ line-height: 32px;
111
+ }
112
+ .panel .data{
113
+ color: #aaa;
114
+ }
115
+
116
+ .player:not(:last-child) {
117
+ border-bottom: 1px solid #e1e1e1; /* 示例边框样式 */
118
+ }
119
+
120
+ .player {
121
+ color: #000;
122
+ width: 100%;
123
+ display: grid;
124
+ grid-template-columns: 64px 48px 88px 160px 112px 252px 36px 20px;
125
+ grid-template-rows: 19px 14px 14px 14px;
126
+ padding-bottom: 3px;
127
+ font-size: 12px;
128
+ line-height: 14px;
129
+ overflow: hidden;
130
+ }
131
+
132
+ .player > .row-1 {
133
+ margin-top: 6px;
134
+ }
135
+
136
+ .player > .hero_avatar {
137
+ margin-bottom: 5px;
138
+ width: 64px;
139
+ grid-row: 1 / span 3;
140
+ grid-column: 1;
141
+ position: relative;
142
+ }
143
+ .player > .hero_avatar > p {
144
+ width: 20px;
145
+ height: 15px;
146
+ background-color: #323232;
147
+ position: absolute;
148
+ bottom: 0;
149
+ right: 0;
150
+ font-size: 12px;
151
+ line-height: 15px;
152
+ color: #fff;
153
+ text-align: center;
154
+ }
155
+
156
+ .player > .rank {
157
+ position: relative;
158
+ grid-row: 1 / span 3;
159
+ grid-column: 2;
160
+ width: 48px;
161
+ height: 48px;
162
+ }
163
+ .player > .rank > img {
164
+ position: absolute;
165
+ }
166
+ .player > .rank > p {
167
+ position: absolute;
168
+ width: 100%;
169
+ bottom: 3px;
170
+ text-align: center;
171
+ font-size: 9px;
172
+ color: #fff;
173
+ text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; /* 文字描边 */
174
+ }
175
+
176
+ .player > .titles {
177
+ grid-row: 4;
178
+ grid-column: 1 / span 3;
179
+ text-shadow: 1px 1px 0 #333; /* 设置阴影颜色及偏移 */
180
+ }
181
+
182
+ .player > .player_name {
183
+ grid-row: 1;
184
+ grid-column: 3 / span 2;
185
+ }
186
+ .player > .player_name > .rank {
187
+ color: #aaa;
188
+ }
189
+ .player.radiant > .player_name > .name {
190
+ color: #3c9028;
191
+ }
192
+ .player.dire > .player_name > .name {
193
+ color: #9c3628;
194
+ }
195
+
196
+ .player .pick {
197
+ grid-column: 3;
198
+ }
199
+
200
+ .player .networth {
201
+ grid-column: 3;
202
+ }
203
+ .player .networth .gold {
204
+ color: rgb(203, 176, 42);
205
+ text-shadow: 1px 1px 0 #000; /* 设置阴影颜色及偏移 */
206
+ }
207
+
208
+ .player .hero_damage {
209
+ grid-row: 2;
210
+ grid-column: 4;
211
+ }
212
+ .player .damage_received {
213
+ grid-row: 3;
214
+ grid-column: 4;
215
+ }
216
+ .player .tower_damage {
217
+ grid-row: 4;
218
+ grid-column: 4;
219
+ }
220
+ .player .kda {
221
+ grid-row: 1;
222
+ grid-column: 5;
223
+ }
224
+ .player .kill_contribution {
225
+ grid-row: 2;
226
+ grid-column: 5;
227
+ }
228
+ .player .stun_duration {
229
+ grid-row: 3;
230
+ grid-column: 5;
231
+ }
232
+ .player .heal {
233
+ grid-row: 4;
234
+ grid-column: 5;
235
+ }
236
+
237
+ .player .items {
238
+ grid-row: 1 / span 4;
239
+ grid-column: 6;
240
+ display: grid;
241
+ grid-template-columns: 24px 192px auto;
242
+ grid-template-rows: 32px 24px;
243
+ }
244
+ .player .items > div {
245
+ display: flex;
246
+ background-color: #c0c0c0;
247
+ }
248
+ .player .items .normal {
249
+ height: 32px;
250
+ grid-column: 1/-1;
251
+ grid-row: 1;
252
+ }
253
+ .player .items .backpack {
254
+ height: 24px;
255
+ width: 96px;
256
+ grid-row: 2;
257
+ /* grid-column: 1; */
258
+ }
259
+ .player .items .normal .item {
260
+ width: 40px;
261
+ height: 30px;
262
+ margin: 1px;
263
+ position: relative;
264
+ }
265
+ .player .items .time {
266
+ position: absolute;
267
+ width: 100%;
268
+ text-align: center;
269
+ bottom: 0;
270
+ height: 11px;
271
+ line-height: 11px;
272
+ color: #ccc;
273
+ background-color: #323232;
274
+ }
275
+ .player .items .backpack{
276
+ filter: grayscale(100%);
277
+ }
278
+ .player .items .backpack .item,
279
+ .player .items .bear .item {
280
+ width: 30px;
281
+ height: 22px;
282
+ margin: 1px;
283
+ position: relative;
284
+ }
285
+ .player .neutral_item {
286
+ grid-row: 1 / span 3;
287
+ grid-column: 7;
288
+ overflow: hidden;
289
+ height: 32px;
290
+ width: 32px;
291
+ border-radius: 50%;
292
+ background-size: auto 100%;
293
+ background-position: center;
294
+ margin-left: 2px;
295
+ }
296
+ .player .items .item.recipe {
297
+ background-image: url("https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/items/recipe.png");
298
+ background-size: 100%;
299
+ }
300
+
301
+ .player .ahgs {
302
+ grid-row: 1 / span 3;
303
+ grid-column: 8;
304
+ }
305
+ .player .items .bear{
306
+ height: 24px;
307
+ width: 192px;
308
+ grid-row: 2;
309
+ grid-column: 2;
310
+ }
311
+ .player.bear .items .bear .time{
312
+ font-size: 10px;
313
+ }
314
+ .player.bear .items .bear_icon{
315
+ grid-row: 2;
316
+ grid-column: 1;
317
+ }
318
+ .player.bear .items .neutral_item{
319
+ height: 24px;
320
+ width: 24px;
321
+ grid-row: 2;
322
+ grid-column: 3;
323
+ }
324
+ /* .player .items .backpack .item img{
325
+ height: 22px;
326
+ width: auto;
327
+ } */
328
+ </style>
329
+ </head>
330
+ <body>
331
+ <% const match = data; %>
332
+ <nav>
333
+ <div class="match_id">
334
+ <p>比赛 <%-match.id%></p>
335
+ <p class="<%-match.parsedDateTime?'success':'fail'%>"><!--伪类赋值:类名输入success时此处为※录像分析成功,fail为※分析结果不完整,同时自动应用字体颜色样式。--></p>
336
+ </div>
337
+ <div class="start_time">
338
+ <p>开始时间</p>
339
+ <p><%-moment(new Date(match.startDateTime * 1000)).format("YYYY-MM-DD HH:mm:ss").slice(2)%></p>
340
+ </div>
341
+ <div class="duration">
342
+ <p>持续时间</p>
343
+ <p><%-utils.sec2time(match.durationSeconds)%></p>
344
+ </div>
345
+ <div class="region">
346
+ <p>地区</p>
347
+ <p><%-d2a.region[match.regionId]%></p>
348
+ </div>
349
+ <div class="mode">
350
+ <p>模式</p>
351
+ <p><%-d2a.lobbyTypes[match.lobbyType] || match.lobbyType%>/<%-d2a.gameMode[match.gameMode] || match.gameMode%></p>
352
+ </div>
353
+ <div class="rank">
354
+ <img width="48" height="48" src="<%-utils.getImageUrl('medal_' + match.rank?.toString().split('')[0])%>" alt="" />
355
+ </div>
356
+ </nav>
357
+ <section class="match_result">
358
+ <span class="kills radiant"><%-match.radiant.killsCount%></span>
359
+ <span class="win <%-match.didRadiantWin?'radiant':'dire'%>"><!--伪类赋值:类名输入radiant此处为天辉获胜,dire为夜魇获胜,同时自动应用字体颜色样式。--></span>
360
+ <span class="kills dire"><%-match.dire.killsCount%></span>
361
+ </section>
362
+ <section class="players">
363
+ <%- ['radiant','dire'].map(team=>`
364
+ <section class="panel ${team}" style="order: ${team==="radiant"?0:50}">
365
+ <img src="${utils.getImageUrl("logo_"+team)}" alt="">
366
+ <p>${(team==="radiant"?["Radiant","天辉"]:["Dire","夜魇"]).join("<br>")}</p>
367
+ ${((team==="radiant")===match.didRadiantWin)?`<p class="win">胜利</p>`:"<p></p>"}
368
+ <p class="data">击杀<br>${match[team].killsCount}</p>
369
+ <p class="data">总伤害<br>${match[team].heroDamage}</p>
370
+ <p class="data">总经济<br>${match[team].networth}</p>
371
+ <p class="data">总经验<br>${match[team].experience}</p><!--此处无有效API,仅能通过每分钟经验推算-->
372
+ </section>`).join("")
373
+ %>
374
+ <%- match.players.map(player=> `
375
+ <div class="player ${player.team}${player.hero.id==80?" bear":""}" style="order: ${player.team==="radiant"?1:100};">
376
+ <div class="hero_avatar row-1">
377
+ <img src="${utils.getImageUrl(player.hero.shortName, ImageType.Heroes)}" />
378
+ <p class="level">${player.level}</p>
379
+ </div>
380
+ <div class="rank">
381
+ <img
382
+ src="${utils.getImageUrl('medal_' +(player.inTop100??player.rank.medal))}"
383
+ class="medal"
384
+ />
385
+ <img src="${utils.getImageUrl('star_' + player.rank.star)}" class="stars" />
386
+ <p>${player.steamAccount.seasonLeaderboardRank??""}</p>
387
+ </div>
388
+ <div class="titles">${player.titles.map((item) => `<span style="color: ${item.color};">${item.name}</span>`).join('&nbsp;')}</div>
389
+ <div class="player_name row-1">
390
+ <span class="rank">${`[${d2a.rank[player.rank.medal]}${player.rank.star||""}]`}</span>
391
+ <span class="name">${player.steamAccount.name}</span>
392
+ </div>
393
+ <p class="pick">${player.isRandom?'随机':`第${player.order == null ? "-" : player.order + 1}手`} ${d2a.position[player.position?.slice(-1)]??''}</p>
394
+ <p class="networth">
395
+ <span class="gold">${utils.formatNumber(player.networth)}</span>
396
+ (${(player.heroDamage / player.networth)?.toFixed(2)})
397
+ </p>
398
+ <p class="hero_damage">造成伤害:${player.heroDamage} (${(player.heroDamage/match[player.team].heroDamage*100).toFixed(2)}%)</p>
399
+ <p class="damage_received">承受伤害:${player.damageReceived} (${(player.damageReceived/match[player.team].damageReceived*100).toFixed(2)}%)</p>
400
+ <p class="tower_damage">建筑伤害:${player.towerDamage}</p>
401
+ <p class="kda row-1">${player.kills}/${player.deaths}/${player.assists} (${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)})</p>
402
+ <p class="kill_contribution">参战率:${(player.killContribution * 100).toFixed(2)}%</p>
403
+ <p class="stun_duration">控制时间:${(player.stats.heroDamageReport.dealtTotal.stunDuration / 100).toFixed(2)}s</p>
404
+ <p class="heal">治疗量:${player.heroHealing}</p>
405
+ <div class="items row-1">
406
+ <div class="normal">
407
+ ${player.items.map((item) =>`
408
+ <div class="item${item?.isRecipe ? " recipe" : ""}" data-id="${item?.id??0}">
409
+ <img src="${item ? utils.getImageUrl(item.name, ImageType.Items) : ""}" alt="" />
410
+ ${item ? `<p class="time">${utils.sec2time(item?.time) ?? ""}</p>` : ""}
411
+ </div>`).join("")}
412
+ </div>
413
+ ${player.hero.id != 80?`
414
+ <div class="backpack">
415
+ ${player.backpacks.map((item) =>`
416
+ <div class="item${item?.isRecipe ? " recipe" : ""}">
417
+ <img src="${item ? utils.getImageUrl(item.name, ImageType.Items) : ""}" alt="" />
418
+ </div>`).join("")}
419
+ </div>`:`
420
+ <img class="bear_icon" src="https://cdn.cloudflare.steamstatic.com/apps/dota2/images/dota_react/abilities/lone_druid_spirit_bear.png" alt="">
421
+ <div class="bear">
422
+ ${player.unitItems.map((item) =>`
423
+ <div class="item${item?.isRecipe ? " recipe" : ""}" data-id="${item?.id??0}">
424
+ <img src="${item ? utils.getImageUrl(item.name, ImageType.Items) : ""}" alt="" />
425
+ ${item ? `<p class="time">${utils.sec2time(item?.time) ?? ""}</p>` : ""}
426
+ </div>`).join("")}
427
+ </div>
428
+ <div class="neutral_item" style="background-image: url(${utils.getImageUrl(dotaconstants.item_ids[player.additionalUnit.neutral0Id], ImageType.Items)})"></div>`
429
+ }
430
+ </div>
431
+ <div class="neutral_item row-1" style="background-image: url(${utils.getImageUrl(dotaconstants.item_ids[player.neutral0Id], ImageType.Items)})"></div>
432
+ <div class="ahgs row-1">
433
+ <img src="${utils.getImageUrl("scepter_"+((player.items.concat(player.backpacks).find(item=>item?.id==108)||player.stats.matchPlayerBuffEvent.find(buff=>buff.itemId==108))?1:0))}" alt="" />
434
+ <img src="${utils.getImageUrl("shard_"+(player.stats.matchPlayerBuffEvent.find(buff=>buff.itemId==609)?1:0))}" alt="" />
435
+ </div>
436
+ </div>
437
+ `).join("") %>
438
+ </section>
439
+ </body>
440
+ </html>