1
0
mirror of https://github.com/lexogrine/dota2-react-hud.git synced 2025-12-10 01:52:49 +01:00

Added combat log support

This commit is contained in:
Hubert Walczak 2023-05-16 17:39:31 +02:00
parent a475763d62
commit 44eb8896c5
No known key found for this signature in database
GPG Key ID: 17BB1C9355357860
3 changed files with 220 additions and 3 deletions

View File

@ -9,7 +9,7 @@
"@types/node": "18.15.11",
"@types/react": "18.0.35",
"@types/react-dom": "18.0.11",
"dotagsi": "^1.1.3",
"dotagsi": "github:Macronic/dota2gsi",
"buffer": "^6.0.3",
"query-string": "^6.12.1",
"react": "^18.2.0",

View File

@ -9,6 +9,7 @@ import { Match } from './api/interfaces';
import "./HUD/GameHUD/gamehud.scss";
import { exampleData } from './example';
import { initiateConnection } from './HUD/Camera/mediaStream';
import { GameSummary } from './summaries';
const DOTA2 = new DOTA2GSI();
export const socket = io(isDev ? `localhost:${port}` : '/');
@ -46,14 +47,22 @@ const dataLoader: DataLoader = {
match: null
}
class App extends React.Component<any, { game: Dota2 | null, steamids: string[], match: Match | null, checked: boolean }> {
class App extends React.Component<any, { game: Dota2 | null,summary: GameSummary; steamids: string[], match: Match | null, checked: boolean }> {
constructor(props: any) {
super(props);
this.state = {
game: null,
steamids: [],
match: null,
checked: false
checked: false,
summary: {
players: {},
tickSummary: {
creepsKilled: [],
abilitiesHit: [],
abilitiesUsed: []
}
}
}
}
@ -131,6 +140,10 @@ class App extends React.Component<any, { game: Dota2 | null, steamids: string[],
window.top && window.top.location.reload();
});
socket.on("combatLogUpdate", (summary: GameSummary) => {
this.setState({ summary });
});
DOTA2.on('data', data => {
if (!this.state.game || this.state.steamids.length) this.verifyPlayers(data);
this.setState({ game: data });

204
src/summaries.ts Normal file
View File

@ -0,0 +1,204 @@
import { Player } from "dotagsi";
export const BOUNTY_RUNES_REASON_ID = 17;
const CUSTOM_GOLD_REASON_MIDAS = -1;
const GOLD_REASON_CREEP_KILL = 13;
const MIDAS_GOLD_PER_USE = 160;
const RuneTypes = [
"haste",
"arcane",
"bounty",
"double_damage",
"illusion",
"invisibility",
"regeneration",
"water",
] as const;
type RuneType = (typeof RuneTypes)[number];
export enum GoldReason {
Unspecified = 0,
Death = 1,
Buyback = 2,
PurchaseConsumable = 3,
PurchaseItem = 4,
AbandonedRedistribute = 5,
SellItem = 6,
AbilityCost = 7,
CheatCommand = 8,
SelectionPenalty = 9,
GameTick = 10,
Building = 11,
HeroKill = 12,
CreepKill = 13,
RoshanKill = 14,
CourierKill = 15,
SharedGold = 16,
BountyRune = 17,
Midas = CUSTOM_GOLD_REASON_MIDAS,
}
type GoldReasonSummary = { [goldReason: number]: number };
type EnemyType = "melee" | "ranged" | "siege" | "summon" | "neutral" | "other";
type GoldEnemyTypeSummary = {
[enemy in EnemyType]: number;
};
const summonEnemyNameChecks = [
"_beastmaster_boar",
"_death_ward",
"_brewmaster_earth",
"_eidolon",
"_visage_familiar",
"_brewmaster_fire",
"_invoker_forged_spirit",
"_furion_treant",
"_scout_hawk",
"_lycan_wolf",
"_lycan_wolf",
"_necromonicon",
"_necromonicon",
"_necromonicon",
"_necromonicon",
"_dark_troll_warlord_skeleton_warrior",
"_broodmother_spiderite",
"_broodmother_spiderling",
"_broodmother_web",
"_lone_druid_bear",
"_brewmaster_storm",
"_furion_treant",
"_undying_zombie",
"_warlock_golem",
"_elder_titan_ancestral_spirit",
];
const enemyNameToType = (name: string): EnemyType => {
if (name.includes("_neutral")) {
return "neutral";
}
if (name.includes("_creep")) {
if (name.includes("_melee")) {
return "melee";
}
if (name.includes("_ranged")) {
return "ranged";
}
}
if (name.includes("_siege")) {
return "siege";
}
for (const summonNameCheck of summonEnemyNameChecks) {
if (name.includes(summonNameCheck)) {
return "summon";
}
}
return "other";
};
type Channelling = {
lastTimestamp: number;
lastRealTimeTimestamp: string;
isCurrentlyChannelling: boolean;
channellingTimes: number;
};
type EnemyTypeInProgress = {
killedEnemy?: string;
killedAt?: number;
};
type AbilityStats = {
[skillName: string]: {
hitCount: number;
useCount: number;
};
};
export type PlayerSummary = {
goldReasonSummary: GoldReasonSummary;
goldEnemyTypeSummary: GoldEnemyTypeSummary;
activeRunes: RuneType[];
bountyRuneTimeline: BountyRuneData[];
enemyTypeInProgress: EnemyTypeInProgress;
chanellings: Channelling;
abilityStats: AbilityStats;
towerDamage: number;
};
export type PlayerSummaries = { [hero: string]: PlayerSummary };
export type CreepKill = {
playerName: string;
creepName: string;
};
export type AbilityUse = {
playerName: string;
abilityName: string;
abilityLevel: number;
};
export type AbilityHit = {
playerName: string;
abilityName: string;
targetName: string;
};
export type TickSummary = {
creepsKilled: CreepKill[];
abilitiesUsed: AbilityUse[];
abilitiesHit: AbilityHit[];
};
export type BountyRuneData = {
time: number;
gold: number;
};
export type GameSummary = {
players: PlayerSummaries;
tickSummary: TickSummary;
};
export const findPlayerSummary = (
summary: GameSummary | null | undefined,
player: Player
) => {
return summary &&
summary.players &&
player.hero &&
player.hero.name &&
player.hero.name in summary.players
? summary.players[player.hero.name]
: null;
};
export type EnrichedPlayerData = Player & { summary: PlayerSummary | null };
export const enrichPossiblePlayerData = (
summary: GameSummary | null | undefined,
player: Player | null | undefined
) => {
return player
? { ...player, summary: findPlayerSummary(summary, player) }
: null;
};
export const enrichPlayerData = (
summary: GameSummary | null | undefined,
player: Player
) => {
return { ...player, summary: findPlayerSummary(summary, player) };
};