@sjtdev/koishi-plugin-dota2tracker 1.1.5 → 1.1.7

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.
package/lib/index.js CHANGED
@@ -254,60 +254,60 @@ function PLAYER_INFO_WITH_25_MATCHES(steamAccountId, heroId) {
254
254
  {
255
255
  player(steamAccountId: ${steamAccountId}) {
256
256
  steamAccount {
257
- avatar
258
- name
259
- seasonRank
260
- seasonLeaderboardRank
261
- id
257
+ avatar
258
+ name
259
+ seasonRank
260
+ seasonLeaderboardRank
261
+ id
262
262
  }
263
263
  guildMember {
264
- guild {
265
- tag
266
- }
264
+ guild {
265
+ tag
266
+ }
267
267
  }
268
268
  matchCount
269
269
  winCount
270
270
  performance {
271
- imp
271
+ imp
272
272
  }
273
273
  heroesPerformance(take: 25, request: {matchGroupOrderBy: WIN_COUNT take: 25 ${heroId ? "heroIds:" + heroId : ""}}) {
274
- hero {
275
- id
276
- shortName
277
- }
278
- imp
279
- winCount
280
- matchCount
274
+ hero {
275
+ id
276
+ shortName
277
+ }
278
+ imp
279
+ winCount
280
+ matchCount
281
281
  }
282
282
  matches(request: {take: 25 ${heroId ? "heroIds:" + heroId : ""}}) {
283
- id
284
- rank
285
- lobbyType
286
- gameMode
287
- startDateTime
288
- durationSeconds
289
- didRadiantWin
290
- topLaneOutcome
291
- midLaneOutcome
292
- bottomLaneOutcome
293
- radiantKills
294
- direKills
295
- players(steamAccountId: ${steamAccountId}) {
296
- isRadiant
297
- lane
298
- kills
299
- deaths
300
- assists
301
- position
302
- award
303
- imp
304
- hero {
305
- id
306
- shortName
307
- }
308
- }
309
- }
310
- }
283
+ id
284
+ rank
285
+ lobbyType
286
+ gameMode
287
+ startDateTime
288
+ durationSeconds
289
+ didRadiantWin
290
+ topLaneOutcome
291
+ midLaneOutcome
292
+ bottomLaneOutcome
293
+ radiantKills
294
+ direKills
295
+ players(steamAccountId: ${steamAccountId}) {
296
+ isRadiant
297
+ lane
298
+ kills
299
+ deaths
300
+ assists
301
+ position
302
+ award
303
+ imp
304
+ hero {
305
+ id
306
+ shortName
307
+ }
308
+ }
309
+ }
310
+ }
311
311
 
312
312
  }
313
313
 
@@ -500,6 +500,7 @@ async function testGetHtml(URL) {
500
500
  __name(testGetHtml, "testGetHtml");
501
501
  var ImageType = /* @__PURE__ */ ((ImageType2) => {
502
502
  ImageType2["Icons"] = "icons";
503
+ ImageType2["IconsFacets"] = "icons/facets";
503
504
  ImageType2["Heroes"] = "heroes";
504
505
  ImageType2["HeroIcons"] = "heroes/icons";
505
506
  ImageType2["Items"] = "items";
@@ -525,11 +526,21 @@ function getFormattedMatchData(match) {
525
526
  ["radiant", "dire"].forEach((team) => {
526
527
  match[team] = { killsCount: match[team + "Kills"]?.reduce((acc, cva) => acc + cva, 0) ?? 0, damageReceived: 0, heroDamage: 0, networth: 0, experience: 0 };
527
528
  });
529
+ if (!match.parsedDateTime) {
530
+ match.players.reduce((acc, player) => {
531
+ if (player.isRadiant) {
532
+ acc.radiant.killsCount += player.kills;
533
+ } else {
534
+ acc.dire.killsCount += player.kills;
535
+ }
536
+ return acc;
537
+ }, match);
538
+ }
528
539
  match.party = {};
529
540
  let party_index = 0;
530
541
  const party_mark = ["I", "II", "III", "IV"];
531
542
  let heroOrderList = {};
532
- for (let hero of match.pickBans) {
543
+ for (let hero of match.pickBans ?? []) {
533
544
  if (hero.isPick)
534
545
  heroOrderList[hero.heroId] = hero.order;
535
546
  }
@@ -561,27 +572,29 @@ function getFormattedMatchData(match) {
561
572
  };
562
573
  player.killContribution = (player.kills + player.assists) / match[player.team].killsCount;
563
574
  player.deathContribution = player.deaths / match[player.team === "radiant" ? "dire" : player.team].killsCount;
564
- player.damageReceived = player.stats?.heroDamageReport?.receivedTotal.physicalDamage + player.stats?.heroDamageReport?.receivedTotal.magicalDamage + player.stats?.heroDamageReport?.receivedTotal.pureDamage;
575
+ player.damageReceived = (player.stats?.heroDamageReport?.receivedTotal?.physicalDamage ?? 0) + (player.stats?.heroDamageReport?.receivedTotal?.magicalDamage ?? 0) + (player.stats?.heroDamageReport?.receivedTotal?.pureDamage ?? 0);
565
576
  match[player.team].heroDamage = (match[player.team].heroDamage ?? 0) + player.heroDamage;
566
577
  match[player.team].damageReceived = (match[player.team].damageReceived ?? 0) + player.damageReceived;
567
578
  match[player.team].networth += player.networth;
568
579
  match[player.team].experience += Math.floor(player.experiencePerMinute / 60 * match.durationSeconds);
569
580
  player.titles = [];
570
581
  player.mvpScore = // 计算MVP分数
571
- player.kills * 5 + player.assists * 3 + player.stats.heroDamageReport.dealtTotal.stunDuration / 100 * 0.1 + player.stats.heroDamageReport.dealtTotal.disableDuration / 100 * 0.05 + player.stats.heroDamageReport.dealtTotal.slowDuration / 100 * 0.025 + player.heroDamage * 1e-3 + player.towerDamage * 0.01 + player.heroHealing * 2e-3 + player.imp * 0.25;
582
+ player.kills * 5 + player.assists * 3 + (player.stats.heroDamageReport?.dealtTotal.stunDuration ?? 0) / 100 * 0.1 + (player.stats.heroDamageReport?.dealtTotal.disableDuration ?? 0) / 100 * 0.05 + (player.stats.heroDamageReport?.dealtTotal.slowDuration ?? 0) / 100 * 0.025 + player.heroDamage * 1e-3 + player.towerDamage * 0.01 + player.heroHealing * 2e-3 + player.imp * 0.25;
572
583
  player.order = heroOrderList[player.hero.id];
573
584
  if (player.partyId != null) {
574
585
  if (!match.party[player.partyId])
575
586
  match.party[player.partyId] = party_mark[party_index++];
576
587
  }
577
- const maxStackCountsByAbilityOrItem = player.stats.matchPlayerBuffEvent.reduce((acc, event) => {
578
- const key = event.abilityId !== null ? `ability-${event.abilityId}` : `item-${event.itemId}`;
579
- if (!acc[key] || event.stackCount > acc[key].stackCount) {
580
- acc[key] = event;
581
- }
582
- return acc;
583
- }, {});
584
- player.stats.matchPlayerBuffEvent.splice(0, player.stats.matchPlayerBuffEvent.length, ...Object.values(maxStackCountsByAbilityOrItem));
588
+ if (match.parsedDateTime) {
589
+ const maxStackCountsByAbilityOrItem = player.stats.matchPlayerBuffEvent.reduce((acc, event) => {
590
+ const key = event.abilityId !== null ? `ability-${event.abilityId}` : `item-${event.itemId}`;
591
+ if (!acc[key] || event.stackCount > acc[key].stackCount) {
592
+ acc[key] = event;
593
+ }
594
+ return acc;
595
+ }, {});
596
+ player.stats.matchPlayerBuffEvent.splice(0, player.stats.matchPlayerBuffEvent.length, ...Object.values(maxStackCountsByAbilityOrItem));
597
+ }
585
598
  switch (player.lane) {
586
599
  case "SAFE_LANE":
587
600
  player.laneResult = laneResult[player.isRadiant ? "bottom" : "top"][player.team];
@@ -726,18 +739,20 @@ function getFormattedMatchData(match) {
726
739
  ).titles.push({ name: "魂", color: "#6cf" });
727
740
  findMaxByProperty("networth").titles.push({ name: "富", color: "#FFD700" });
728
741
  findMaxByProperty("experiencePerMinute").titles.push({ name: "睿", color: "#8888FF" });
729
- match.players.reduce(
730
- (max, player) => player.stats.heroDamageReport.dealtTotal.stunDuration + player.stats.heroDamageReport.dealtTotal.disableDuration / 2 + player.stats.heroDamageReport.dealtTotal.slowDuration / 4 > max.stats.heroDamageReport.dealtTotal.stunDuration + max.stats.heroDamageReport.dealtTotal.disableDuration / 2 + max.stats.heroDamageReport.dealtTotal.slowDuration / 4 ? player : max
731
- ).titles.push({ name: "控", color: "#FF00FF" });
742
+ if (match.parsedDateTime) {
743
+ match.players.reduce(
744
+ (max, player) => player.stats.heroDamageReport.dealtTotal.stunDuration + player.stats.heroDamageReport.dealtTotal.disableDuration / 2 + player.stats.heroDamageReport.dealtTotal.slowDuration / 4 > max.stats.heroDamageReport.dealtTotal.stunDuration + max.stats.heroDamageReport.dealtTotal.disableDuration / 2 + max.stats.heroDamageReport.dealtTotal.slowDuration / 4 ? player : max
745
+ ).titles.push({ name: "控", color: "#FF00FF" });
746
+ match.players.reduce(
747
+ (max, player) => player.stats.heroDamageReport.receivedTotal.physicalDamage + player.stats.heroDamageReport.receivedTotal.magicalDamage + player.stats.heroDamageReport.receivedTotal.pureDamage > max.stats.heroDamageReport.receivedTotal.physicalDamage + max.stats.heroDamageReport.receivedTotal.magicalDamage + max.stats.heroDamageReport.receivedTotal.pureDamage ? player : max
748
+ ).titles.push({ name: "耐", color: "#84A1C7" });
749
+ }
732
750
  findMaxByProperty("heroDamage").titles.push({ name: "爆", color: "#CC0088" });
733
751
  findMaxByProperty("kills", "heroDamage").titles.push({ name: "破", color: "#DD0000" });
734
752
  findMaxByProperty("deaths", "networth", void 0, void 0, "min" /* Min */).titles.push({ name: "鬼", color: "#CCCCCC" });
735
753
  findMaxByProperty("assists", "heroDamage").titles.push({ name: "助", color: "#006400" });
736
754
  findMaxByProperty("towerDamage", "heroDamage").titles.push({ name: "拆", color: "#FEDCBA" });
737
755
  findMaxByProperty("heroHealing").titles.push({ name: "奶", color: "#00FF00" });
738
- match.players.reduce(
739
- (max, player) => player.stats.heroDamageReport.receivedTotal.physicalDamage + player.stats.heroDamageReport.receivedTotal.magicalDamage + player.stats.heroDamageReport.receivedTotal.pureDamage > max.stats.heroDamageReport.receivedTotal.physicalDamage + max.stats.heroDamageReport.receivedTotal.magicalDamage + max.stats.heroDamageReport.receivedTotal.pureDamage ? player : max
740
- ).titles.push({ name: "耐", color: "#84A1C7" });
741
756
  match.players.reduce((lowest, player) => {
742
757
  const currentContribution = (player.kills + player.assists) / match[player.team].KillsCount;
743
758
  const lowestContribution = (lowest.kills + lowest.assists) / match[lowest.team].KillsCount;
@@ -1094,7 +1109,7 @@ var ejs = __toESM(require("ejs"));
1094
1109
  var name = "dota2tracker";
1095
1110
  var usage = `
1096
1111
  DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑定),以及一系列查询功能。
1097
- **更多信息请进入插件主页查看。**`;
1112
+ **更多信息请进入插件主页(github本项目仓库)查看。**`;
1098
1113
  var inject = ["database", "puppeteer", "cron"];
1099
1114
  var Config = import_koishi.Schema.intersect([
1100
1115
  import_koishi.Schema.object({
@@ -1249,9 +1264,10 @@ async function apply(ctx, config) {
1249
1264
  match = getFormattedMatchData(queryRes.data.data.match);
1250
1265
  }
1251
1266
  }
1252
- if (match && match.parsedDateTime) {
1267
+ if (match && (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore((0, import_moment.default)().subtract(1, "hours")))) {
1253
1268
  session.send(await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */)));
1254
- ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1269
+ if (match.parsedDateTime)
1270
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1255
1271
  } else {
1256
1272
  pendingMatches.push({ matchId, guilds: [{ platform: session.event.platform, guildId: session.event.guild.id, players: [] }] });
1257
1273
  session.send("比赛尚未解析,将在解析完成后发布。");
@@ -1477,7 +1493,7 @@ async function apply(ctx, config) {
1477
1493
  });
1478
1494
  });
1479
1495
  let heroes3 = Array.from(mergedMap.values());
1480
- return heroes3.find((hero) => hero.names_cn.includes(input) || hero.shortName === input.toLowerCase() || hero.id == input);
1496
+ return heroes3.find((hero) => hero.names_cn.some((cn) => cn.toLowerCase() == input.toLowerCase()) || hero.shortName === input.toLowerCase() || hero.id == input);
1481
1497
  }
1482
1498
  __name(findingHero, "findingHero");
1483
1499
  ctx.command("7.36 <input_data>", "查询7.36改动").option("refresh", "-r 重新获取数据").usage("可查询英雄改动并生成图片返回").example("7.36 小松许").action(async ({ session, options }, input_data) => {
@@ -1517,7 +1533,6 @@ async function apply(ctx, config) {
1517
1533
  });
1518
1534
  heroes3.push({ id: 0, data: result.remainingContent });
1519
1535
  await ctx.database.upsert("dt_7_36", (row) => heroes3);
1520
- import_fs2.default.writeFileSync("./node_modules/@sjtdev/koishi-plugin-dota2tracker/remainingContent.html", result.remainingContent);
1521
1536
  await session.send("数据获取完成。");
1522
1537
  await page.close();
1523
1538
  }
@@ -1574,9 +1589,6 @@ async function apply(ctx, config) {
1574
1589
  } else
1575
1590
  session.send("https://www.dota2.com/patches/7.36");
1576
1591
  });
1577
- ctx.command("test <input_data>").option("a", "a").action(async ({ session, options }, input_data) => {
1578
- console.log((await ctx.database.get("dt_7_36", [0]))[0].data);
1579
- });
1580
1592
  ctx.on("ready", async () => {
1581
1593
  const tables = await ctx.database.tables;
1582
1594
  if (!("dt_subscribed_guilds" in tables)) {
@@ -1713,10 +1725,10 @@ async function apply(ctx, config) {
1713
1725
  } else {
1714
1726
  let queryRes = await query(MATCH_INFO(pendingMatch.matchId));
1715
1727
  if (queryRes.status == 200) {
1716
- match = queryRes.data.data.match.parsedDateTime ? getFormattedMatchData(queryRes.data.data.match) : queryRes.data.data.match;
1728
+ match = getFormattedMatchData(queryRes.data.data.match);
1717
1729
  }
1718
1730
  }
1719
- if (match.parsedDateTime || import_moment.default.unix(match.startDateTime).isBefore((0, import_moment.default)().subtract(1, "years"))) {
1731
+ if (match.parsedDateTime || import_moment.default.unix(match.endDateTime).isBefore((0, import_moment.default)().subtract(1, "hours"))) {
1720
1732
  pendingMatches = pendingMatches.filter((item) => item.matchId != match.id);
1721
1733
  const img = await ctx.puppeteer.render(genImageHTML(match, config.template_match, "match" /* Match */));
1722
1734
  for (let commingGuild of pendingMatch.guilds) {
@@ -1741,9 +1753,10 @@ KDA:${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)} [${
1741
1753
  broadMatchMessage += broadPlayerMessage + "\n";
1742
1754
  }
1743
1755
  await ctx.broadcast([`${commingGuild.platform}:${commingGuild.guildId}`], broadMatchMessage + img);
1744
- ctx.logger.info(`已解析${match.id}并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
1756
+ ctx.logger.info(`${match.id}${match.parsedDateTime ? "已解析," : "已结束超过1小时仍未被解析,放弃解析直接"}生成图片并发布于${commingGuild.platform}:${commingGuild.guildId}。`);
1745
1757
  }
1746
- ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1758
+ if (match.parsedDateTime)
1759
+ ctx.database.upsert("dt_previous_query_results", (row) => [{ matchId: match.id, data: match, queryTime: /* @__PURE__ */ new Date() }]);
1747
1760
  ctx.database.create("dt_sended_match_id", { matchId: match.id, sendTime: /* @__PURE__ */ new Date() });
1748
1761
  } else
1749
1762
  ctx.logger.info("比赛 %d 尚未解析完成,继续等待。", match.id);
@@ -1769,7 +1782,7 @@ function genImageHTML(data, template, type) {
1769
1782
  moment: import_moment.default
1770
1783
  };
1771
1784
  let result = "";
1772
- ejs.renderFile(templatePath, templateData, (err, html) => {
1785
+ ejs.renderFile(templatePath, templateData, { strict: false }, (err, html) => {
1773
1786
  if (err)
1774
1787
  throw err;
1775
1788
  else
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sjtdev/koishi-plugin-dota2tracker",
3
3
  "description": "",
4
- "version": "1.1.5",
4
+ "version": "1.1.7",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
package/readme.md CHANGED
@@ -17,27 +17,35 @@ DOTA2Bot插件-提供自动追踪群友的最新对局的功能(需群友绑
17
17
  指令 <必填参数> [可选参数]
18
18
  ##### 订阅
19
19
  (bot仅向已订阅群组推送信息)
20
- * <input type="checkbox" checked>`订阅本群`
21
- * <input type="checkbox" checked>`取消订阅`
20
+ * `订阅本群`
21
+ * `取消订阅`
22
22
  ##### 绑定
23
23
  (bot会追踪每位绑定玩家的最新对局)
24
- * <input type="checkbox" checked>`绑定 <玩家SteamID> [玩家别名]`
25
- * <input type="checkbox" checked>`取消绑定`
26
- * <input type="checkbox" checked>`改名 <新玩家别名>`
24
+ * `绑定 <玩家SteamID> [玩家别名]`
25
+ * `取消绑定`
26
+ * `改名 <新玩家别名>`
27
27
  ##### 查询
28
- * <input type="checkbox" checked>`查询玩家 [SteamID|别名] [<--hero|-o> <英雄ID|英雄名|英雄常用别名>]`
28
+ * `查询玩家 [SteamID|别名] [<--hero|-o> <英雄ID|英雄名|英雄常用别名>]`
29
29
  返回一张图片,包含玩家各类信息。(缺省参数时并且调用者已绑定将自查)(输入--hero或-o并跟上查询英雄的参数时,将查询玩家指定英雄)
30
- * <input type="checkbox" checked>`查询比赛 <比赛ID>`
30
+ * `查询比赛 <比赛ID>`
31
31
  返回一张图片,包含比赛对战信息。
32
- * <input type="checkbox" checked>`查询最近比赛 [SteamID|别名]`
32
+ * `查询最近比赛 [SteamID|别名]`
33
33
  查询指定玩家的最近一场比赛,效果同上。(缺省参数时并且调用者已绑定将自查)
34
- * <input type="checkbox" checked>`查询英雄 <英雄ID|英雄名|英雄常用别名>`
34
+ * `查询英雄 <英雄ID|英雄名|英雄常用别名>`
35
35
  返回一张图片,包含英雄属性与技能详情。(此处英雄名为中文名)
36
- * <input type="checkbox" checked><del>`查询英雄对战 <英雄ID|英雄名|英雄常用别名>`</del>
36
+ * <del>`查询英雄对战 <英雄ID|英雄名|英雄常用别名>`</del>
37
37
  好像不是很实用
38
+ * `7.36 [英雄ID|英雄名|英雄常用别名] [--refresh|-r]`
39
+ 查询官网7.36更新日志中指定英雄的改动信息
40
+ 无英雄参数时直接返回官网7.36更新日志网址
41
+ 首次使用时将缓存更新日志网页,若读取失败或出错,可添加`--refresh`或`-r`指令重新缓存
42
+
43
+ ### 英雄ID|英雄名|英雄常用别名 列表
44
+ [dotaconstants_add.json](https://github.com/sjtdev/koishi-plugin-dota2tracker/blob/master/src/dotaconstants_add.json#L102-L226)
45
+ 补充或纠错请提issue
38
46
 
39
47
  ### 图片模板列表
40
- 展示见[wiki](./wiki)
48
+ 展示见[wiki](https://github.com/sjtdev/koishi-plugin-dota2tracker/wiki)
41
49
  生成图片已使用ejs模板实现,所有模板都在[template]文件夹下,若是有大佬想自己设计模板欢迎联系我完善数据接口。(当前有很多在模板中后处理的数据,不是很友好)
42
50
 
43
51
  ### 其他问题
@@ -718,7 +718,7 @@
718
718
  <img alt="" src="${utils.getImageUrl(player.hero.shortName, ImageType.Heroes)}" />
719
719
  <p class="party_line${player.partyId != null ? " party_" + match.party[player.partyId] : ""}"></p>
720
720
  <p class="party_mark${player.partyId != null ? " party_" + match.party[player.partyId] : ""}"></p>
721
- <p class="position p${Math.floor(player.order / 4) + 1}">${player.isRandom ? "随机" : `第<span>${player.order == null ? "-" : player.order + 1}</span>手`}<br/>${d2a.position[player.position?.slice(-1)]}</p>
721
+ <p class="position p${Math.floor(player.order / 4) + 1}">${player.isRandom ? "随机" : `第<span>${player.order == null ? "-" : player.order + 1}</span>手`}<br/>${d2a.position[player.position?.slice(-1)]??""}</p>
722
722
  <p class="level">${player.level}</p>
723
723
  </div>
724
724
  <div class="player_info">
@@ -804,7 +804,7 @@
804
804
  <p>${buff.stackCount ?? ""}</p>
805
805
  </div>`
806
806
  )
807
- .join("")}
807
+ .join("")??""}
808
808
  </section>
809
809
  <section>
810
810
  <div class="support_item"${player.supportItemsCount[30] > 0 ? "" : ' style="display:none"'}>
@@ -946,19 +946,19 @@
946
946
  <section>英雄伤害:<span class="hero_damage">${player.heroDamage}</span></section>
947
947
  <section>建筑伤害:<span class="building_damage">${player.towerDamage}</span></section>
948
948
  <section>受到伤害(减免后):<span class="tak">${
949
- player.stats?.heroDamageReport?.receivedTotal.physicalDamage + player.stats?.heroDamageReport?.receivedTotal.magicalDamage + player.stats?.heroDamageReport?.receivedTotal.pureDamage
949
+ (player.stats?.heroDamageReport?.receivedTotal.physicalDamage??0) + (player.stats?.heroDamageReport?.receivedTotal.magicalDamage??0) + (player.stats?.heroDamageReport?.receivedTotal.pureDamage??0)
950
950
  }</span></section>
951
951
  <section>补刀:<span class="lh">${player.numLastHits}</span>/<span class="dn">${player.numDenies}</span></section>
952
952
  <section>GPM/XPM:<span class="gpm">${player.goldPerMinute}</span>/<span class="xpm">${player.experiencePerMinute}</span></section>
953
953
  <section>治疗量:<span class="heal">${player.heroHealing}</span></section>
954
- <section>控制时间:<span class="building_damage">${(player.stats?.heroDamageReport?.dealtTotal.stunDuration / 100).toFixed(2)}/${(player.stats?.heroDamageReport?.dealtTotal.slowDuration / 100).toFixed(2)}/${(
955
- player.stats?.heroDamageReport?.dealtTotal.disableDuration / 100
954
+ <section>控制时间:<span class="building_damage">${((player.stats?.heroDamageReport?.dealtTotal.stunDuration ?? 0) / 100).toFixed(2)}/${((player.stats?.heroDamageReport?.dealtTotal.slowDuration ?? 0) / 100).toFixed(2)}/${(
955
+ (player.stats?.heroDamageReport?.dealtTotal.disableDuration ?? 0) / 100
956
956
  ).toFixed(2)}</span>s</section>
957
957
  </div>
958
958
  </div>`).join("") %>
959
959
  </div>
960
960
  <div class="ban_list">
961
- <%- match.pickBans
961
+ <%- (match.pickBans??[])
962
962
  .filter((hero) => !hero.isPick)
963
963
  .map((hero) => `<div class="ban_hero"><img src="${utils.getImageUrl(/^npc_dota_hero_(?<name>.+)$/.exec(dotaconstants.heroes[hero.bannedHeroId].name)[1], ImageType.Heroes)}" alt="" /></div>`)
964
964
  .join("") %>
@@ -396,11 +396,11 @@
396
396
  (${(player.heroDamage / player.networth)?.toFixed(2)})
397
397
  </p>
398
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>
399
+ <p class="damage_received">承受伤害:${player.damageReceived} (${match[player.team].damageReceived>0?((player.damageReceived/match[player.team].damageReceived*100).toFixed(2)):"0.00"}%)</p>
400
400
  <p class="tower_damage">建筑伤害:${player.towerDamage}</p>
401
401
  <p class="kda row-1">${player.kills}/${player.deaths}/${player.assists} (${((player.kills + player.assists) / (player.deaths || 1)).toFixed(2)})</p>
402
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>
403
+ <p class="stun_duration">控制时间:${((player.stats.heroDamageReport?.dealtTotal.stunDuration ?? 0)/ 100).toFixed(2)}s</p>
404
404
  <p class="heal">治疗量:${player.heroHealing}</p>
405
405
  <div class="items row-1">
406
406
  <div class="normal">
@@ -430,8 +430,8 @@
430
430
  </div>
431
431
  <div class="neutral_item row-1" style="background-image: url(${utils.getImageUrl(dotaconstants.item_ids[player.neutral0Id], ImageType.Items)})"></div>
432
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="" />
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
435
  </div>
436
436
  </div>
437
437
  `).join("") %>
@@ -385,13 +385,13 @@
385
385
 
386
386
  player.positionPerformance=[];
387
387
  // 瞎j8定义的各位置代表物品,以后看情况调整
388
- const positionItems=["greater_crit","dagon_5","heart","hand_of_midas","ward_observer"];
388
+ const positionIcons = ["damage","nuke","armor","speed","healing"];
389
389
  for (let index = 0; index < 5; index++) {
390
390
  let currentPositionMatches = player.matches.filter(match=>match.players[0].position == ("POSITION_"+(index+1)))
391
391
  let winCount = currentPositionMatches.filter(match=>match.didRadiantWin == match.players[0].isRadiant).length;
392
392
  player.positionPerformance.push({
393
393
  position : (index + 1),
394
- icon : positionItems[index],
394
+ icon : positionIcons[index],
395
395
  matchCount : currentPositionMatches.length,
396
396
  winCount : winCount,
397
397
  loseCount : currentPositionMatches.length - winCount,
@@ -468,7 +468,7 @@
468
468
  <span class="tip lose_count" style="justify-self: start; margin-left: 2px">败场</span>
469
469
  <%- player.positionPerformance
470
470
  .map((position) => `
471
- <span><img src="${utils.getImageUrl(position.icon,ImageType.Items)}"></span>
471
+ <span><img src="${utils.getImageUrl(position.icon,ImageType.IconsFacets)}"></span>
472
472
  <span class="count">${position.matchCount}</span>
473
473
  <span class="win_rate">${position.matchCount>0?(((position.winCount / position.matchCount) * 100).toFixed(0)):"-"}%</span>
474
474
  <span class="imp">${(position.imp > 0 ? "+" : "") + position.imp}</span>