├── .gitignore ├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── README.md ├── gameConfig ├── gamestate_integration_observerspectator.cfg └── observer.cfg ├── index.html ├── jestconfig.json ├── lint.tsconfig.json ├── package-lock.json ├── package.json ├── src ├── config │ ├── hudSettings.ts │ ├── playerInfo.ts │ └── teamInfo.ts ├── container │ └── container.tsx ├── dataTypes │ └── index.ts ├── index.tsx ├── redux │ ├── modules │ │ ├── actions.ts │ │ ├── defuseType │ │ │ └── defuseType.ts │ │ ├── gsi │ │ │ ├── __tests__ │ │ │ │ └── gst-test.ts │ │ │ └── gsi.ts │ │ ├── hudVisibility │ │ │ └── hudVisibility.ts │ │ ├── index.ts │ │ ├── paused │ │ │ └── paused.ts │ │ ├── players │ │ │ ├── __tests__ │ │ │ │ └── players-test.ts │ │ │ └── players.ts │ │ ├── roundPhase │ │ │ └── roundPhase.ts │ │ ├── roundWinner │ │ │ └── roundWinner.ts │ │ ├── score │ │ │ └── score.ts │ │ ├── slotSide │ │ │ └── slotSide.ts │ │ ├── spectatingPlayer │ │ │ └── spectatingPlayer.ts │ │ ├── teamInfo │ │ │ └── teamInfo.ts │ │ └── teamMoney │ │ │ ├── __tests__ │ │ │ └── teamMoney-test.ts │ │ │ └── teamMoney.ts │ └── store.ts ├── resources │ ├── armors │ │ ├── armor.png │ │ └── helmet.png │ ├── miscs │ │ ├── bomb.png │ │ ├── death.png │ │ ├── defuse.png │ │ ├── eq.png │ │ ├── hp.png │ │ └── team.png │ ├── players │ │ ├── noimage.png │ │ └── shroud.jpg │ ├── teams │ │ └── magixgod.png │ └── weapons │ │ ├── weapon_decoy.png │ │ ├── weapon_flashbang.png │ │ ├── weapon_hegrenade.png │ │ ├── weapon_incgrenade.png │ │ ├── weapon_molotov.png │ │ └── weapon_smokegrenade.png ├── server.ts ├── shortcut │ └── shortcut.ts ├── util │ ├── __tests__ │ │ └── slotSideResolver-test.ts │ ├── armorIconResolver.ts │ ├── createAction.ts │ ├── miscIconResolver.ts │ ├── playerImageResolver.ts │ ├── slotSideResolver.ts │ ├── teamLogoResolver.ts │ └── weaponIconResolver.ts └── views │ ├── blinkingC4Icon │ ├── BlinkingC4Icon.tsx │ ├── Story.tsx │ └── blinking_c4_icon.scss │ ├── index.ts │ ├── kda │ ├── Kda.tsx │ ├── Story.tsx │ └── kda.scss │ ├── percentageTimer │ ├── PercentageTimer.tsx │ ├── Story.tsx │ └── percentage_timer.scss │ ├── player │ ├── Player.tsx │ ├── Story.tsx │ └── player.scss │ ├── roundCounter │ ├── RoundCounter.tsx │ ├── Story.tsx │ └── round_counter.scss │ ├── spectatingPlayer │ ├── SpectatingPlayer.tsx │ ├── Story.tsx │ └── spectating_player.scss │ ├── teamMoney │ ├── Story.tsx │ ├── TeamMoney.tsx │ └── team_money.scss │ ├── teamStats │ ├── Story.tsx │ ├── TeamStats.tsx │ └── team_stats.scss │ ├── template │ ├── Story.tsx │ └── Template.tsx │ ├── theme │ ├── color.scss │ └── common.scss │ ├── timer │ ├── Story.tsx │ ├── Timer.tsx │ └── timer.scss │ ├── topBar │ ├── Story.tsx │ ├── TopBar.tsx │ └── top_bar.scss │ ├── util │ └── baseComponent.tsx │ └── winnerTeamAnnounce │ ├── Story.tsx │ ├── WinnerTeamAnnounce.tsx │ └── winner_team_announce.scss ├── test ├── fixtures │ ├── c4Planted.json │ └── c4Refusing.json └── util │ └── loadFixture.ts ├── tsconfig.json ├── tsconfig.server.json ├── tslint.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | ### project specific settings 2 | 3 | # build target 4 | build/ 5 | server/ 6 | 7 | ### https://raw.github.com/github/gitignore/ead0f8e25c9cc58ca70594d557d0dec8a9c08a1d/Node.gitignore 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules 40 | jspm_packages 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional REPL history 46 | .node_repl_history 47 | 48 | 49 | ### https://raw.github.com/github/gitignore/fceac113a3a20e00718d6317e468eec27f6e2d99/Global/VisualStudioCode.gitignore 50 | 51 | .vscode 52 | 53 | 54 | ### https://raw.github.com/github/gitignore/fceac113a3a20e00718d6317e468eec27f6e2d99/Global/JetBrains.gitignore 55 | 56 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 57 | 58 | *.iml 59 | 60 | ## Directory-based project format: 61 | .idea/ 62 | # if you remove the above rule, at least ignore the following: 63 | 64 | # User-specific stuff: 65 | # .idea/workspace.xml 66 | # .idea/tasks.xml 67 | # .idea/dictionaries 68 | # .idea/shelf 69 | 70 | # Sensitive or high-churn files: 71 | # .idea/dataSources.ids 72 | # .idea/dataSources.xml 73 | # .idea/sqlDataSources.xml 74 | # .idea/dynamic.xml 75 | # .idea/uiDesigner.xml 76 | 77 | # Gradle: 78 | # .idea/gradle.xml 79 | # .idea/libraries 80 | 81 | # Mongo Explorer plugin: 82 | # .idea/mongoSettings.xml 83 | 84 | ## File-based project format: 85 | *.ipr 86 | *.iws 87 | 88 | ## Plugin-specific files: 89 | 90 | # IntelliJ 91 | /out/ 92 | 93 | # mpeltonen/sbt-idea plugin 94 | .idea_modules/ 95 | 96 | # JIRA plugin 97 | atlassian-ide-plugin.xml 98 | 99 | # Crashlytics plugin (for Android Studio and IntelliJ) 100 | com_crashlytics_export_strings.xml 101 | crashlytics.properties 102 | crashlytics-build.properties 103 | fabric.properties 104 | 105 | .DS_Store 106 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | // To get our default addons (actions and links) 2 | import "@storybook/addons"; 3 | // To add the knobs addon 4 | import "@storybook/addon-knobs/register" 5 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { configure } from "@storybook/react"; 3 | 4 | 5 | const req = require.context("../src/views", true, /Story\.tsx$/); 6 | 7 | function loadStories() { 8 | req.keys().sort().forEach(req) 9 | } 10 | 11 | configure(loadStories, module); 12 | 13 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const config = require("../webpack.config.js"); 2 | const path = require("path"); 3 | 4 | config.entry = undefined; 5 | config.plugins = undefined; 6 | config.externals = undefined; 7 | config.output = undefined; 8 | config.target = undefined; 9 | 10 | config.module.rules.push( 11 | { 12 | test: /\.ts(x?)$/, 13 | enforce: "pre", 14 | use: "tslint-loader", 15 | include: { 16 | include: [path.join(__dirname, "../src/views")] 17 | }, 18 | } 19 | ); 20 | 21 | module.exports = config; 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | csgo-custom-hud 2 | ======= 3 | 4 | react based cs:go custom hud. 5 | 6 | # How to use 7 | ## before running app. 8 | - `gameConfig/gamestate_integration_observerspectator.cfg` needs to be placed in cfg folder in CS:GO location 9 | - `gameConfig/observer.cfg` needs to be placed in cfg folder in CS:GO location. 10 | 11 | ## run app 12 | ```sh 13 | npm i 14 | npm run build 15 | npm start 16 | ``` 17 | ## configuration 18 | ## hud settings 19 | you may edit `/src/config/hudSettings.ts`. 20 | For example, 21 | ```js 22 | export const hudSettings: HudSettings = { 23 | showTeamMoney: true, // show team money when round is freeze time. 24 | }; 25 | ``` 26 | 27 | ### team config 28 | you may edit `/src/config/teamInfo.ts`. 29 | For example, 30 | ```js 31 | export const team1: TeamInfo = { 32 | name: "NiP", 33 | logo: "nip.png", // You need to place the file in `/src/resources/teams` 34 | }; 35 | 36 | export const team2: TeamInfo = { 37 | name: "fnatic", 38 | logo: "fnatic.png", // You need to place the file in `/src/resources/teams` 39 | }; 40 | ``` 41 | ### player config 42 | you may edit `/src/config/playerInfo`. 43 | For example, 44 | ```js 45 | export const playerInfoList: PlayerInfoList = { 46 | // `76561198005627722` is Steam ID. 47 | "76561198005627722": { 48 | twitterId: "@thiry_sk", 49 | image: "shroud.jpg", 50 | }, 51 | }; 52 | ``` 53 | ### shortcuts 54 | - `Alt+Left` => swap team information. 55 | - `Alt+Up` => Toggle show/hide HUD. 56 | 57 | # For developer 58 | ## how to enable Chrome dev tools 59 | ```sh 60 | npm run install:sdk 61 | npm run build 62 | npm start 63 | ``` 64 | Now you may press `F12`, then you can use Chrome dev tools. 65 | ## how to start storybook 66 | ```sh 67 | npm i 68 | npm run start:storybook 69 | ``` 70 | access [http://localhost:3000/](http://localhost:3000/). 71 | Story subjects are currently written in Japanese. 72 | I will change to English whenever I feel like. -------------------------------------------------------------------------------- /gameConfig/gamestate_integration_observerspectator.cfg: -------------------------------------------------------------------------------- 1 | "Observer All Players v.1" 2 | { 3 | "uri" "http://127.0.0.1:3000" 4 | "timeout" "5.0" 5 | "buffer" "0.1" 6 | "throttle" "0.1" 7 | "heartbeat" "0.1" 8 | "data" 9 | { 10 | "provider" "1" 11 | "map" "1" 12 | "round" "1" 13 | "player_id" "1" 14 | "allplayers_id" "1" // Same as 'player_id' but for all players. 'allplayers' versions are only valid for HLTV and observers 15 | "player_state" "1" 16 | "allplayers_state" "1" 17 | "allplayers_match_stats" "1" 18 | "allplayers_weapons" "1" 19 | "allplayers_position" "1" // output the player world positions, only valid for HLTV or spectators. 20 | "phase_countdowns" "1" // countdowns of each second remaining for game phases, eg round time left, time until bomb explode, freezetime. Only valid for HLTV or spectators. 21 | } 22 | } -------------------------------------------------------------------------------- /gameConfig/observer.cfg: -------------------------------------------------------------------------------- 1 | cl_draw_only_deathnotices 1 2 | cl_drawhud_force_radar 1 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /jestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.tsx?$": "ts-jest" 4 | }, 5 | "roots": [ 6 | "/src/" 7 | ], 8 | "testRegex": "__tests__/.*-test.(ts|tsx|js)", 9 | "moduleFileExtensions": [ 10 | "ts", 11 | "tsx", 12 | "js", 13 | "json" 14 | ], 15 | "testEnvironment": "jsdom" 16 | } 17 | -------------------------------------------------------------------------------- /lint.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "node_modules" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "csgo-custom-hud", 3 | "version": "1.0.0", 4 | "description": "cs:go custom hud", 5 | "scripts": { 6 | "install:sdk": "npm i && npm i nw --nwjs_build_type=sdk", 7 | "test": "cross-env NODE_ENV=test jest --config ./jestconfig.json --no-cache --coverage", 8 | "clean": "rimraf \"./build/**/*\"", 9 | "tslint": "tslint -c ./tslint.json -t verbose \"./src/**/*.ts*\"", 10 | "lint-staged": "lint-staged", 11 | "lint": "npm run tslint", 12 | "start": "nw", 13 | "start:storybook": "start-storybook -s ./src/resources -p 3000", 14 | "prebuild": " npm run clean", 15 | "build": "npm run build:server && npm run build:client", 16 | "build:client": "npm run prebuild && webpack", 17 | "build:server": "tsc -p tsconfig.server.json" 18 | }, 19 | "pre-commit": [ 20 | "lint-staged" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/Thiry1/csgo-custom-hud" 25 | }, 26 | "lint-staged": { 27 | "*.{ts,tsx}": [ 28 | "tslint -p ./lint.tsconfig.json -c ./tslint.json -t verbose --fix", 29 | "tsfmt -r", 30 | "git add" 31 | ] 32 | }, 33 | "author": "thiry", 34 | "license": "WTFPL", 35 | "devDependencies": { 36 | "@storybook/addon-knobs": "^3.3.14", 37 | "@storybook/addons": "^3.3.14", 38 | "@storybook/react": "^3.3.14", 39 | "@types/jest": "^23.3.0", 40 | "@types/lodash": "^4.14.116", 41 | "@types/node": "^9.4.6", 42 | "@types/react": "^16.8.22", 43 | "@types/react-dom": "^16.8.4", 44 | "@types/react-redux": "^5.0.15", 45 | "@types/recompose": "^0.24.5", 46 | "@types/redux-actions": "^2.2.4", 47 | "@types/storybook__addon-knobs": "^3.4.1", 48 | "@types/storybook__react": "^3.0.7", 49 | "babel-core": "^6.26.3", 50 | "babel-loader": "^7.1.2", 51 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 52 | "babel-polyfill": "^6.26.0", 53 | "babel-preset-es2015": "^6.24.1", 54 | "cross-env": "^5.2.0", 55 | "css-loader": "^0.28.10", 56 | "file-loader": "^1.1.11", 57 | "jest": "^23.4.1", 58 | "lint-staged": "^6.1.1", 59 | "pre-commit": "^1.2.2", 60 | "redux-saga-test-plan": "^3.7.0", 61 | "rimraf": "^2.6.2", 62 | "sass-loader": "^6.0.6", 63 | "style-loader": "^0.20.2", 64 | "ts-jest": "^23.0.1", 65 | "ts-loader": "^3.5.0", 66 | "tslint": "^5.9.1", 67 | "tslint-loader": "^3.5.3", 68 | "typescript": "^3.5.1", 69 | "typescript-formatter": "^7.1.0", 70 | "webpack": "^3.11.0" 71 | }, 72 | "dependencies": { 73 | "convert-seconds": "^1.0.1", 74 | "left-pad": "^1.2.0", 75 | "lodash": "^4.17.11", 76 | "lodash-humps": "^3.1.2", 77 | "node-sass": "^4.12.0", 78 | "nw": "^0.38.4", 79 | "react": "^16.8.6", 80 | "react-dom": "^16.8.6", 81 | "react-redux": "^7.1.0", 82 | "recompose": "^0.26.0", 83 | "redux": "^4.0.1", 84 | "redux-actions": "^2.6.5", 85 | "redux-saga": "^0.16.0" 86 | }, 87 | "main": "index.html", 88 | "chromium-args": "--disable-gpu --force-cpu-draw", 89 | "window": { 90 | "title": "CSGO OBSEVER HUD", 91 | "icon": "logo.png", 92 | "toolbar": false, 93 | "frame": false, 94 | "width": 1920, 95 | "height": 1080, 96 | "position": "center", 97 | "transparent": true, 98 | "always_on_top": true 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/config/hudSettings.ts: -------------------------------------------------------------------------------- 1 | export interface HudSettings { 2 | /** 3 | * show team money when round is freeze time. 4 | */ 5 | showTeamMoney: boolean; 6 | } 7 | 8 | export const hudSettings: HudSettings = { 9 | showTeamMoney: true, 10 | }; 11 | -------------------------------------------------------------------------------- /src/config/playerInfo.ts: -------------------------------------------------------------------------------- 1 | export interface PlayerInfo { 2 | /** 3 | * TwitterID. 4 | */ 5 | twitterId?: string; 6 | /** 7 | * Player image. 8 | */ 9 | image?: string; 10 | /** 11 | * Should the image be displayed inline 12 | */ 13 | imageInline?: boolean; 14 | /** 15 | * Fixed player name: will prevent using the GSI provided names. 16 | */ 17 | name?: string; 18 | } 19 | export interface PlayerInfoList { 20 | [steamId: string]: PlayerInfo; 21 | } 22 | export const playerInfoList: PlayerInfoList = { 23 | // `76561198005627722` is Steam ID. 24 | "76561198005627722": { 25 | twitterId: "@thiry_sk", 26 | image: "shroud.jpg", 27 | imageInline: false, 28 | name: "Shroud", 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/config/teamInfo.ts: -------------------------------------------------------------------------------- 1 | import { TeamInfo } from "../redux/modules/teamInfo/teamInfo"; 2 | 3 | // 最初はTサイドとして表示される 4 | // 必要に応じてショートカットでチーム情報をスワップして調整する. 5 | export const team1: TeamInfo = { 6 | /** 7 | * チーム名. 8 | * 空の場合は、`COUNTER TERRORIST` か `TERRORIST` が表示される 9 | */ 10 | name: "", 11 | /** 12 | * チームロゴ画像のファイル名. 13 | * ファイルは /src/resources/teams 内にいれること. 14 | * @example foo.png 15 | */ 16 | logo: "", 17 | }; 18 | // 最初はCTサイドとして表示される 19 | // 必要に応じてショートカットでチーム情報をスワップして調整する. 20 | export const team2: TeamInfo = { 21 | /** 22 | * チーム名. 23 | * 空の場合は、`COUNTER TERRORIST` か `TERRORIST` が表示される 24 | */ 25 | name: "", 26 | /** 27 | * チームロゴ画像のファイル名. 28 | * 省略した場合は表示されない. 29 | * ファイルは /src/resources/teams 内にいれること. 30 | * @example foo.png 31 | */ 32 | logo: "", 33 | }; 34 | -------------------------------------------------------------------------------- /src/container/container.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { compose } from "recompose"; 3 | import { connect } from "react-redux"; 4 | import { State } from "../redux/modules"; 5 | import { Player } from "../redux/modules/players/players"; 6 | import { Template, TemplateProps } from "../views/template/Template"; 7 | import { RoundPhaseState } from "../redux/modules/roundPhase/roundPhase"; 8 | import { GameStateIntegration } from "../dataTypes"; 9 | import { TeamMoneyState } from "../redux/modules/teamMoney/teamMoney"; 10 | import { ScoreState } from "../redux/modules/score/score"; 11 | import { TeamInfoState } from "../redux/modules/teamInfo/teamInfo"; 12 | import { PlayerInfo } from "../config/playerInfo"; 13 | import CurrentPhase = GameStateIntegration.CurrentPhase; 14 | import { BlinkingC4Icon } from "../views/blinkingC4Icon/BlinkingC4Icon"; 15 | import { ProgressBarAxis, ProgressBarDirection } from "../views/percentageTimer/PercentageTimer"; 16 | import { DefuseType } from "../redux/modules/defuseType/defuseType"; 17 | import { RoundWinnerState } from "../redux/modules/roundWinner/roundWinner"; 18 | import { SlotSideState } from "../redux/modules/slotSide/slotSide"; 19 | 20 | export interface ContainerProps { 21 | 22 | } 23 | interface PropsFromState { 24 | players: Player[]; 25 | roundPhase: RoundPhaseState; 26 | teamMoney: TeamMoneyState; 27 | spectatingPlayer: Player & PlayerInfo; 28 | score: ScoreState; 29 | teamInfo: TeamInfoState; 30 | defuseType: DefuseType; 31 | roundWinner: RoundWinnerState; 32 | isHudVisible: boolean; 33 | slotSide: SlotSideState; 34 | } 35 | interface Dispatcher { 36 | 37 | } 38 | interface Props extends ContainerProps, Dispatcher, PropsFromState { 39 | } 40 | 41 | interface ContainerState { 42 | } 43 | class ContainerPage extends React.Component { 44 | private createTemplateProps(): TemplateProps { 45 | return { 46 | visible: this.props.isHudVisible, 47 | players: this.props.players.map(player => { 48 | return { 49 | name: player.name, 50 | money: player.state.money, 51 | health: player.state.health, 52 | armor: player.state.armor, 53 | hasHelmet: player.state.helmet, 54 | hasDefuseKit: !!player.state.defusekit, 55 | showKda: this.props.roundPhase.phase === GameStateIntegration.CurrentPhase.freezetime, 56 | kda: { 57 | kill: player.matchStats.kills, 58 | death: player.matchStats.deaths, 59 | assist: player.matchStats.assists, 60 | team: player.team, 61 | }, 62 | roundKillCount: player.state.roundKills, 63 | roundKillByHeadShotCount: player.state.roundKillHs, 64 | weapon: { 65 | activeWeapon: player.weapons.activeWeapon ? player.weapons.activeWeapon.name : null, 66 | primary: player.weapons.primary ? player.weapons.primary.name : null, 67 | secondary: player.weapons.secondary ? player.weapons.secondary.name : null, 68 | flashBangAmount: player.weapons.flashBangAmount, 69 | smokeAmount: player.weapons.smokeAmount, 70 | highExplosiveAmount: player.weapons.highExplosiveAmount, 71 | molotovAmount: player.weapons.molotovAmount, 72 | incGrenadeAmount: player.weapons.incGrenadeAmount, 73 | decoyAmount: player.weapons.decoyAmount, 74 | hasC4: player.weapons.hasC4, 75 | }, 76 | team: player.team, 77 | observerSlot: player.observerSlot, 78 | isSpectatingByObserver: this.props.spectatingPlayer.steamId === player.steamId, 79 | }; 80 | }), 81 | teamMoney: { 82 | ct: { 83 | slotSide: this.props.slotSide.ct, 84 | team: GameStateIntegration.Team.CT, 85 | ...this.props.teamMoney.ct, 86 | }, 87 | t: { 88 | slotSide: this.props.slotSide.t, 89 | team: GameStateIntegration.Team.T, 90 | ...this.props.teamMoney.t, 91 | }, 92 | }, 93 | spectatingPlayer: { // TODO: Player そのまま渡すで良い. 94 | showSpectatingPlayer: this.props.roundPhase.phase !== GameStateIntegration.CurrentPhase.freezetime, 95 | name: this.props.spectatingPlayer.name, 96 | twitterId: this.props.spectatingPlayer.twitterId, 97 | image: this.props.spectatingPlayer.image, 98 | imageInline: this.props.spectatingPlayer.imageInline, 99 | activeWeapon: this.props.spectatingPlayer.weapons.activeWeapon, 100 | flashBangAmount: this.props.spectatingPlayer.weapons.flashBangAmount, 101 | smokeAmount: this.props.spectatingPlayer.weapons.smokeAmount, 102 | highExplosiveAmount: this.props.spectatingPlayer.weapons.highExplosiveAmount, 103 | molotovAmount: this.props.spectatingPlayer.weapons.molotovAmount, 104 | incGrenadeAmount: this.props.spectatingPlayer.weapons.incGrenadeAmount, 105 | decoyAmount: this.props.spectatingPlayer.weapons.decoyAmount, 106 | health: this.props.spectatingPlayer.state.health, 107 | armor: this.props.spectatingPlayer.state.armor, 108 | hasHelmet: this.props.spectatingPlayer.state.helmet, 109 | kill: this.props.spectatingPlayer.matchStats.kills, 110 | death: this.props.spectatingPlayer.matchStats.deaths, 111 | assist: this.props.spectatingPlayer.matchStats.assists, 112 | team: this.props.spectatingPlayer.team, 113 | }, 114 | topBar: { 115 | teamInfo: { 116 | ct: { 117 | score: this.props.score.ct, 118 | name: this.props.teamInfo.ct.name || "COUNTER TERRORIST", 119 | logo: this.props.teamInfo.ct.logo, 120 | }, 121 | t: { 122 | score: this.props.score.t, 123 | name: this.props.teamInfo.t.name || "TERRORIST", 124 | logo: this.props.teamInfo.t.logo, 125 | }, 126 | }, 127 | roundCounter: { 128 | currentRound: this.props.score.ct + this.props.score.t + 1, 129 | maxRound: 30, 130 | }, 131 | c4Timer: this.props.roundPhase.phase === CurrentPhase.bomb 132 | || this.props.roundPhase.phase === CurrentPhase.defuse ? { 133 | value: this.props.roundPhase.c4Timer, 134 | max: 40, // C4 が爆発するまでの時間 135 | icon: { 136 | component: BlinkingC4Icon, 137 | props: { 138 | visible: true, 139 | }, 140 | }, 141 | progressBarType: { 142 | axis: ProgressBarAxis.Vertical, 143 | direction: ProgressBarDirection.Fill, 144 | }, 145 | } : null, 146 | defuseTimer: this.props.defuseType !== DefuseType.None ? { 147 | value: this.props.roundPhase.time, 148 | max: this.props.defuseType === DefuseType.DefuseWithDefuseKit 149 | ? 5 150 | : 10, 151 | progressBarType: { 152 | axis: ProgressBarAxis.Horizontal, 153 | direction: ProgressBarDirection.Empty, 154 | }, 155 | } : null, 156 | roundTimer: { 157 | time: this.props.roundPhase.time, 158 | }, 159 | currentPhase: this.props.roundPhase.phase, 160 | slotSide: this.props.slotSide, 161 | }, 162 | winnerTeamAnnounce: this.props.roundWinner.team !== null ? { 163 | team: this.props.roundWinner.team, 164 | teamName: this.props.roundWinner.teamName, 165 | slotSide: this.props.roundWinner.team === GameStateIntegration.Team.CT 166 | ? this.props.slotSide.ct 167 | : this.props.slotSide.t, 168 | } : null, 169 | slotSide: this.props.slotSide, 170 | }; 171 | } 172 | render() { 173 | return ( 174 |