initial commit
@@ -0,0 +1,110 @@
|
||||
import React from 'react';
|
||||
import { Bomb } from 'csgogsi-socket';
|
||||
import maps, { ScaleConfig, MapConfig, ZoomAreas } from './maps';
|
||||
import './index.css';
|
||||
import { RadarPlayerObject, RadarGrenadeObject } from './interface';
|
||||
import config from './config';
|
||||
interface IProps {
|
||||
players: RadarPlayerObject[];
|
||||
grenades: RadarGrenadeObject[];
|
||||
bomb?: Bomb | null;
|
||||
mapName: string;
|
||||
zoom?: ZoomAreas;
|
||||
mapConfig: MapConfig,
|
||||
reverseZoom: string,
|
||||
parsePosition: (position: number[], size: number, config: ScaleConfig) => number[]
|
||||
}
|
||||
|
||||
const isShooting = (lastShoot: number) => (new Date()).getTime() - lastShoot <= 250;
|
||||
class App extends React.Component<IProps> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
players: [],
|
||||
grenades: [],
|
||||
bomb: null
|
||||
}
|
||||
}
|
||||
|
||||
renderGrenade = (grenade: RadarGrenadeObject) => {
|
||||
if ("flames" in grenade) {
|
||||
return null;
|
||||
}
|
||||
const { reverseZoom } = this.props;
|
||||
return (
|
||||
<div key={grenade.id} className={`grenade ${grenade.type} ${grenade.side || ''} ${grenade.state} ${grenade.visible ? 'visible':'hidden'}`}
|
||||
style={{
|
||||
transform: `translateX(${grenade.position[0].toFixed(2)}px) translateY(${grenade.position[1].toFixed(2)}px) translateZ(10px) scale(${reverseZoom})`,
|
||||
}}>
|
||||
<div className="explode-point"></div>
|
||||
<div className="background"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
renderDot = (player: RadarPlayerObject) => {
|
||||
const { reverseZoom } = this.props;
|
||||
return (
|
||||
<div key={player.id}
|
||||
className={`player ${player.shooting? 'shooting':''} ${player.flashed ? 'flashed':''} ${player.side} ${player.hasBomb ? 'hasBomb':''} ${player.isActive ? 'active' : ''} ${!player.isAlive ? 'dead' : ''} ${player.visible ? 'visible':'hidden'}`}
|
||||
style={{
|
||||
transform: `translateX(${player.position[0].toFixed(2)}px) translateY(${player.position[1].toFixed(2)}px) translateZ(10px) scale(${reverseZoom})`,
|
||||
width: config.playerSize * player.scale,
|
||||
height: config.playerSize * player.scale,
|
||||
}}>
|
||||
<div className="background-fire" style={{ transform: `rotate(${-90 + player.position[2]}deg)`, opacity: isShooting(player.lastShoot) ? 1 : 0 }} ><div className="bg"/></div>
|
||||
<div className="background" style={{ transform: `rotate(${45 + player.position[2]}deg)` }}></div>
|
||||
<div className="label">{player.label}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
renderBomb = () => {
|
||||
const { bomb, mapConfig, reverseZoom } = this.props;
|
||||
if(!bomb) return null;
|
||||
if(bomb.state === "carried" || bomb.state === "planting") return null;
|
||||
if("config" in mapConfig){
|
||||
const position = this.props.parsePosition(bomb.position, 30, mapConfig.config);
|
||||
if(!position) return null;
|
||||
|
||||
return (
|
||||
<div className={`bomb ${bomb.state} visible`}
|
||||
style={{
|
||||
transform: `translateX(${position[0].toFixed(2)}px) translateY(${position[1].toFixed(2)}px) translateZ(10px) scale(${reverseZoom})`
|
||||
}}>
|
||||
<div className="explode-point"></div>
|
||||
<div className="background"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return mapConfig.configs.map(config => {
|
||||
const position = this.props.parsePosition(bomb.position, 30, config.config);
|
||||
if(!position) return null;
|
||||
return (
|
||||
<div className={`bomb ${bomb.state} ${config.isVisible(bomb.position[2]) ? 'visible':'hidden'}`}
|
||||
style={{
|
||||
transform: `translateX(${position[0].toFixed(2)}px) translateY(${position[1].toFixed(2)}px) translateZ(10px)`
|
||||
}}>
|
||||
<div className="explode-point"></div>
|
||||
<div className="background"></div>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const { players, grenades, zoom } = this.props;
|
||||
|
||||
const style: React.CSSProperties = { backgroundImage: `url(${maps[this.props.mapName].file})` }
|
||||
|
||||
if(zoom){
|
||||
style.transform = `scale(${zoom.zoom})`;
|
||||
style.transformOrigin = `${zoom.origin[0]}px ${zoom.origin[1]}px`;
|
||||
}
|
||||
//if(players.length === 0) return null;
|
||||
return <div className="map" style={style}>
|
||||
{players.map(this.renderDot)}
|
||||
{grenades.map(this.renderGrenade)}
|
||||
{this.renderBomb()}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,322 @@
|
||||
import React from 'react';
|
||||
import { Player, Bomb } from 'csgogsi-socket';
|
||||
import maps, { ScaleConfig } from './maps';
|
||||
import LexoRadar from './LexoRadar';
|
||||
import { ExtendedGrenade, Grenade, RadarPlayerObject, RadarGrenadeObject } from './interface';
|
||||
import config from './config';
|
||||
|
||||
const DESCALE_ON_ZOOM = true;
|
||||
|
||||
|
||||
let playersStates: Player[][] = [];
|
||||
let grenadesStates: ExtendedGrenade[][] = [];
|
||||
const directions: Record<string, number> = {};
|
||||
type ShootingState = {
|
||||
ammo: number,
|
||||
weapon: string,
|
||||
lastShoot: number
|
||||
}
|
||||
let shootingState: Record<string, ShootingState> = {};
|
||||
|
||||
const calculateDirection = (player: Player) => {
|
||||
if (directions[player.steamid] && !player.state.health) return directions[player.steamid];
|
||||
|
||||
const [forwardV1, forwardV2] = player.forward;
|
||||
let direction = 0;
|
||||
|
||||
const [axisA, axisB] = [Math.asin(forwardV1), Math.acos(forwardV2)].map(axis => axis * 180 / Math.PI);
|
||||
|
||||
if (axisB < 45) {
|
||||
direction = Math.abs(axisA);
|
||||
} else if (axisB > 135) {
|
||||
direction = 180 - Math.abs(axisA);
|
||||
} else {
|
||||
direction = axisB;
|
||||
}
|
||||
|
||||
if (axisA < 0) {
|
||||
direction = -(direction -= 360);
|
||||
}
|
||||
|
||||
if (!directions[player.steamid]) {
|
||||
directions[player.steamid] = direction;
|
||||
}
|
||||
|
||||
const previous = directions[player.steamid];
|
||||
|
||||
let modifier = previous;
|
||||
modifier -= 360 * Math.floor(previous / 360);
|
||||
modifier = -(modifier -= direction);
|
||||
|
||||
if (Math.abs(modifier) > 180) {
|
||||
modifier -= 360 * Math.abs(modifier) / modifier;
|
||||
}
|
||||
directions[player.steamid] += modifier;
|
||||
|
||||
return directions[player.steamid];
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
players: Player[],
|
||||
bomb?: Bomb | null,
|
||||
player: Player | null,
|
||||
grenades?: any
|
||||
size?: number,
|
||||
mapName: string
|
||||
}
|
||||
|
||||
class App extends React.Component<IProps> {
|
||||
round = (n: number) => {
|
||||
const r = 0.02;
|
||||
return Math.round(n / r) * r;
|
||||
}
|
||||
|
||||
parsePosition = (position: number[], size: number, config: ScaleConfig) => {
|
||||
if (!(this.props.mapName in maps)) {
|
||||
return [0, 0];
|
||||
}
|
||||
const left = config.origin.x + (position[0] * config.pxPerUX) - (size / 2);
|
||||
const top = config.origin.y + (position[1] * config.pxPerUY) - (size / 2);
|
||||
|
||||
return [this.round(left), this.round(top)];
|
||||
}
|
||||
|
||||
parseGrenadePosition = (grenade: ExtendedGrenade, config: ScaleConfig) => {
|
||||
if (!("position" in grenade)) {
|
||||
return null;
|
||||
}
|
||||
let size = 30;
|
||||
if (grenade.type === "smoke") {
|
||||
size = 60;
|
||||
}
|
||||
return this.parsePosition(grenade.position.split(", ").map(pos => Number(pos)), size, config);
|
||||
}
|
||||
getGrenadePosition = (grenade: ExtendedGrenade, config: ScaleConfig) => {
|
||||
const grenadeData = grenadesStates.slice(0, 5).map(grenades => grenades.filter(gr => gr.id === grenade.id)[0]).filter(pl => !!pl);
|
||||
if (grenadeData.length === 0) return null;
|
||||
const positions = grenadeData.map(grenadeEntry => this.parseGrenadePosition(grenadeEntry, config)).filter(posData => posData !== null) as number[][];
|
||||
if (positions.length === 0) return null;
|
||||
const entryAmount = positions.length;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
for (const position of positions) {
|
||||
x += position[0];
|
||||
y += position[1];
|
||||
}
|
||||
|
||||
return [x / entryAmount, y / entryAmount];
|
||||
}
|
||||
getPosition = (player: Player, mapConfig: ScaleConfig, scale: number) => {
|
||||
const playerData = playersStates.slice(0, 5).map(players => players.filter(pl => pl.steamid === player.steamid)[0]).filter(pl => !!pl);
|
||||
if (playerData.length === 0) return [0, 0];
|
||||
const positions = playerData.map(playerEntry => this.parsePosition(playerEntry.position, config.playerSize * scale, mapConfig));
|
||||
const entryAmount = positions.length;
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
for (const position of positions) {
|
||||
x += position[0];
|
||||
y += position[1];
|
||||
}
|
||||
|
||||
const degree = calculateDirection(player);
|
||||
return [x / entryAmount, y / entryAmount, degree];
|
||||
}
|
||||
mapPlayer = (active: Player | null) => (player: Player): RadarPlayerObject | RadarPlayerObject[] | null => {
|
||||
if (!(this.props.mapName in maps)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const weapons = player.weapons ? Object.values(player.weapons) : [];
|
||||
const weapon = weapons.find(weapon => weapon.state === "active" && weapon.type !== "C4" && weapon.type !== "Knife" && weapon.type !== "Grenade");
|
||||
|
||||
const shooting: ShootingState = { ammo: weapon && weapon.ammo_clip || 0, weapon: weapon && weapon.name || '', lastShoot: 0 };
|
||||
|
||||
const lastShoot = shootingState[player.steamid] || shooting;
|
||||
|
||||
let isShooting = false;
|
||||
|
||||
if (shooting.weapon === lastShoot.weapon && shooting.ammo < lastShoot.ammo) {
|
||||
isShooting = true;
|
||||
}
|
||||
|
||||
shooting.lastShoot = isShooting ? (new Date()).getTime() : lastShoot.lastShoot;
|
||||
|
||||
shootingState[player.steamid] = shooting;
|
||||
|
||||
|
||||
const map = maps[this.props.mapName];
|
||||
const playerObject: RadarPlayerObject = {
|
||||
id: player.steamid,
|
||||
label: player.observer_slot !== undefined ? player.observer_slot : "",
|
||||
side: player.team.side,
|
||||
position: [],
|
||||
visible: true,
|
||||
isActive: !!active && active.steamid === player.steamid,
|
||||
forward: 0,
|
||||
steamid: player.steamid,
|
||||
isAlive: player.state.health > 0,
|
||||
hasBomb: !!Object.values(player.weapons).find(weapon => weapon.type === "C4"),
|
||||
flashed: player.state.flashed > 35,
|
||||
shooting: isShooting,
|
||||
lastShoot: shooting.lastShoot,
|
||||
scale: 1,
|
||||
player
|
||||
}
|
||||
if ("config" in map) {
|
||||
const scale = map.config.originHeight === undefined ? 1 : (1 + (player.position[2] - map.config.originHeight) / 1000);
|
||||
|
||||
playerObject.scale = scale;
|
||||
|
||||
const position = this.getPosition(player, map.config, scale);
|
||||
playerObject.position = position;
|
||||
|
||||
return playerObject;
|
||||
}
|
||||
return map.configs.map(config => {
|
||||
const scale = config.config.originHeight === undefined ? 1 : (1 + (player.position[2] - config.config.originHeight) / 750);
|
||||
|
||||
playerObject.scale = scale;
|
||||
|
||||
return ({
|
||||
...playerObject,
|
||||
position: this.getPosition(player, config.config, scale),
|
||||
id: `${player.steamid}_${config.id}`,
|
||||
visible: config.isVisible(player.position[2])
|
||||
})
|
||||
});
|
||||
}
|
||||
mapGrenade = (extGrenade: ExtendedGrenade) => {
|
||||
if (!(this.props.mapName in maps)) {
|
||||
return null;
|
||||
}
|
||||
const map = maps[this.props.mapName];
|
||||
if (extGrenade.type === "inferno") {
|
||||
const mapFlame = (id: string) => {
|
||||
if ("config" in map) {
|
||||
return ({
|
||||
position: this.parsePosition(extGrenade.flames[id].split(", ").map(pos => Number(pos)), 12, map.config),
|
||||
id: `${id}_${extGrenade.id}`,
|
||||
visible: true
|
||||
});
|
||||
}
|
||||
return map.configs.map(config => ({
|
||||
id: `${id}_${extGrenade.id}_${config.id}`,
|
||||
visible: config.isVisible(extGrenade.flames[id].split(", ").map(Number)[2]),
|
||||
position: this.parsePosition(extGrenade.flames[id].split(", ").map(pos => Number(pos)), 12, config.config)
|
||||
}));
|
||||
}
|
||||
const flames = Object.keys(extGrenade.flames).map(mapFlame).flat();
|
||||
const flameObjects: RadarGrenadeObject[] = flames.map(flame => ({
|
||||
...flame,
|
||||
side: extGrenade.side,
|
||||
type: 'inferno',
|
||||
state: 'landed'
|
||||
}));
|
||||
return flameObjects;
|
||||
}
|
||||
|
||||
if ("config" in map) {
|
||||
const position = this.getGrenadePosition(extGrenade, map.config);
|
||||
if (!position) return null;
|
||||
const grenadeObject: RadarGrenadeObject = {
|
||||
type: extGrenade.type,
|
||||
state: 'inair',
|
||||
side: extGrenade.side,
|
||||
position,
|
||||
id: extGrenade.id,
|
||||
visible: true
|
||||
}
|
||||
if (extGrenade.type === "smoke") {
|
||||
if (extGrenade.effecttime !== "0.0") {
|
||||
grenadeObject.state = "landed";
|
||||
if (Number(extGrenade.effecttime) >= 16.5) {
|
||||
grenadeObject.state = 'exploded';
|
||||
}
|
||||
}
|
||||
} else if (extGrenade.type === 'flashbang' || extGrenade.type === 'frag') {
|
||||
if (Number(extGrenade.lifetime) >= 1.25) {
|
||||
grenadeObject.state = 'exploded';
|
||||
}
|
||||
}
|
||||
return grenadeObject;
|
||||
}
|
||||
return map.configs.map(config => {
|
||||
const position = this.getGrenadePosition(extGrenade, config.config);
|
||||
if (!position) return null;
|
||||
const grenadeObject: RadarGrenadeObject = {
|
||||
type: extGrenade.type,
|
||||
state: 'inair',
|
||||
side: extGrenade.side,
|
||||
position,
|
||||
id: `${extGrenade.id}_${config.id}`,
|
||||
visible: config.isVisible(extGrenade.position.split(", ").map(Number)[2])
|
||||
}
|
||||
if (extGrenade.type === "smoke") {
|
||||
if (extGrenade.effecttime !== "0.0") {
|
||||
grenadeObject.state = "landed";
|
||||
if (Number(extGrenade.effecttime) >= 16.5) {
|
||||
grenadeObject.state = 'exploded';
|
||||
}
|
||||
}
|
||||
} else if (extGrenade.type === 'flashbang' || extGrenade.type === 'frag') {
|
||||
if (Number(extGrenade.lifetime) >= 1.25) {
|
||||
grenadeObject.state = 'exploded';
|
||||
}
|
||||
}
|
||||
return grenadeObject;
|
||||
}).filter((grenade): grenade is RadarGrenadeObject => grenade !== null);
|
||||
|
||||
}
|
||||
getSideOfGrenade = (grenade: Grenade) => {
|
||||
const owner = this.props.players.find(player => player.steamid === grenade.owner);
|
||||
if (!owner) return null;
|
||||
return owner.team.side;
|
||||
}
|
||||
render() {
|
||||
const players: RadarPlayerObject[] = this.props.players.map(this.mapPlayer(this.props.player)).filter((player): player is RadarPlayerObject => player !== null).flat();
|
||||
playersStates.unshift(this.props.players);
|
||||
if (playersStates.length > 5) {
|
||||
playersStates = playersStates.slice(0, 5);
|
||||
}
|
||||
let grenades: RadarGrenadeObject[] = [];
|
||||
const currentGrenades = Object.keys(this.props.grenades as { [key: string]: Grenade }).map(grenadeId => ({ ...this.props.grenades[grenadeId], id: grenadeId, side: this.getSideOfGrenade(this.props.grenades[grenadeId]) })) as ExtendedGrenade[];
|
||||
if (currentGrenades) {
|
||||
grenades = currentGrenades.map(this.mapGrenade).filter(entry => entry !== null).flat() as RadarGrenadeObject[];
|
||||
grenadesStates.unshift(currentGrenades);
|
||||
}
|
||||
if (grenadesStates.length > 5) {
|
||||
grenadesStates = grenadesStates.slice(0, 5);
|
||||
}
|
||||
const size = this.props.size || 300;
|
||||
const offset = (size - (size * size / 1024)) / 2;
|
||||
|
||||
const config = maps[this.props.mapName];
|
||||
|
||||
const zooms = config && config.zooms || [];
|
||||
|
||||
const activeZoom = zooms.find(zoom => zoom.threshold(players.map(pl => pl.player)));
|
||||
|
||||
const reverseZoom = 1/(activeZoom && activeZoom.zoom || 1);
|
||||
|
||||
// s*(1024-s)/2048
|
||||
if (!(this.props.mapName in maps)) {
|
||||
return <div className="map-container" style={{ width: size, height: size, transform: `scale(${size / 1024})`, top: -offset, left: -offset }}>
|
||||
Unsupported map
|
||||
</div>;
|
||||
}
|
||||
return <div className="map-container" style={{ width: size, height: size, transform: `scale(${size / 1024})`, top: -offset, left: -offset }}>
|
||||
<LexoRadar
|
||||
players={players}
|
||||
grenades={grenades}
|
||||
parsePosition={this.parsePosition}
|
||||
bomb={this.props.bomb}
|
||||
mapName={this.props.mapName}
|
||||
mapConfig={maps[this.props.mapName]}
|
||||
zoom={activeZoom}
|
||||
reverseZoom={DESCALE_ON_ZOOM ? reverseZoom.toFixed(2) : '1'}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,5 @@
|
||||
const config = {
|
||||
playerSize: 60,
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,5 @@
|
||||
import Firebomb from './firebomb.png'
|
||||
import Flash from './flash.png'
|
||||
import Smoke from './smoke.png'
|
||||
|
||||
export { Firebomb, Flash, Smoke };
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,285 @@
|
||||
html, body, .map-container {
|
||||
width:100%;
|
||||
height:100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@keyframes FlashOrFragDeployed {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.8);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 50px rgba(0, 0, 0, 0.8);
|
||||
opacity:0;
|
||||
}
|
||||
}
|
||||
@keyframes BombPlanted {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(185, 5, 5, 0.8);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 50px rgba(185, 5, 5, 0);
|
||||
}
|
||||
}
|
||||
@keyframes BombExploded {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(185, 5, 5, 0.8);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 150px rgba(185, 5, 5, 0);
|
||||
}
|
||||
}
|
||||
@keyframes BombDefused {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(5, 185, 5, 0.8);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 150px rgba(5, 185, 5, 0);
|
||||
}
|
||||
}
|
||||
.map-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.map-container .map {
|
||||
width:1024px;
|
||||
height: 1024px;
|
||||
position: relative;
|
||||
transform: scale(1);
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.map .player, .map .grenade, .map .bomb {
|
||||
position: absolute;
|
||||
height:30px;
|
||||
width:30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity 0.5s ease;
|
||||
/*transition: all 0.1s ease;/**/
|
||||
}
|
||||
.map .player .background {
|
||||
/*background-color:white;*/
|
||||
/*clip-path: polygon(0 0, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0 0);*/
|
||||
background-image: url('./assets/playerBg.png');
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
transition:transform 0.2s ease;
|
||||
}
|
||||
.map .player .background-fire {
|
||||
position: absolute;
|
||||
width:200%;
|
||||
height:200%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition:transform 0.2s ease;
|
||||
/*background-color:white;*/
|
||||
/*clip-path: polygon(0 0, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0 0);*/
|
||||
|
||||
}
|
||||
@keyframes Blink {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.map .player .background-fire .bg {
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-image: url('./assets/shootFire.png');
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
animation: Blink;
|
||||
animation-duration: 0.25s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
.map .player.dead {
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
}
|
||||
.map .player {
|
||||
transition:transform 0.1s ease, opacity 1s;
|
||||
image-rendering: -moz-crisp-edges; /* Firefox */
|
||||
image-rendering: -o-crisp-edges; /* Opera */
|
||||
image-rendering: -webkit-optimize-contrast; /* Webkit (non-standard naming) */
|
||||
image-rendering: crisp-edges;
|
||||
-ms-interpolation-mode: nearest-neighbor; /* IE (non-standard property) */
|
||||
}
|
||||
.map .player:not(.dead) {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.map .player.flashed.CT:not(.dead) .label {
|
||||
background: rgb(159 197 255);
|
||||
}
|
||||
.map .player.flashed.T:not(.dead) .label {
|
||||
background: rgb(255 219 165);
|
||||
}
|
||||
.map .player.active .background {
|
||||
width:120%;
|
||||
height:120%;
|
||||
}
|
||||
|
||||
/*
|
||||
.map .player.shooting .background {
|
||||
width: 110%;
|
||||
height: 110%;
|
||||
}
|
||||
|
||||
.map .player.active.shooting .background {
|
||||
width: 132%;
|
||||
height: 132%;
|
||||
}
|
||||
*/
|
||||
.map .player.active {
|
||||
width:120%;
|
||||
height:120%;
|
||||
z-index: 3;
|
||||
}
|
||||
.map .grenade .background {
|
||||
border-radius:50%;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
opacity:1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
.map .grenade.smoke .background {
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
opacity: 1;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
.map .grenade.smoke {
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.map .grenade.smoke.inair .background {
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
background-image: url('./grenades/smoke.png');
|
||||
filter: invert(1);
|
||||
}
|
||||
.map .grenade.smoke.exploded .background {
|
||||
opacity: 0;
|
||||
}
|
||||
.map .grenade.flashbang, .map .grenade.frag {
|
||||
filter: invert(1);
|
||||
}
|
||||
.map .grenade.flashbang .background {
|
||||
background-image: url('./grenades/flash.png');
|
||||
background-color: transparent;
|
||||
}
|
||||
.map .grenade.frag .background {
|
||||
background-image: url('./grenades/frag.png');
|
||||
background-color: transparent;
|
||||
}
|
||||
.map .grenade .explode-point, .map .bomb .explode-point {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
border-radius: 0.08px;
|
||||
}
|
||||
.map .grenade.flashbang.exploded .explode-point, .map .grenade.frag.exploded .explode-point {
|
||||
animation: FlashOrFragDeployed 0.25s 1 forwards;
|
||||
}
|
||||
|
||||
.map .grenade.flashbang.exploded .background, .map .grenade.frag.exploded .background {
|
||||
opacity: 0;
|
||||
}
|
||||
.map .grenade.smoke .background {
|
||||
border: 5px solid grey;
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
.map .grenade.smoke.CT .background {
|
||||
border: 5px solid var(--color-new-ct);
|
||||
background-color: rgba(255, 255, 255,0.5);
|
||||
}
|
||||
.map .grenade.smoke.T .background {
|
||||
border: 5px solid var(--color-new-t);
|
||||
background-color: rgba(255, 255, 255,0.5);
|
||||
}
|
||||
.map .grenade.firebomb .background {
|
||||
background-color:transparent;
|
||||
background-image: url('./grenades/firebomb.png');
|
||||
filter: invert(1);
|
||||
}
|
||||
.map .grenade.inferno {
|
||||
width:12px;
|
||||
height:12px;
|
||||
}
|
||||
.map .grenade.smoke {
|
||||
width:60px;
|
||||
height:60px;
|
||||
}
|
||||
.map .grenade.inferno .background {
|
||||
background-color: red;
|
||||
opacity: 0.5;
|
||||
border: 2px solid orange;
|
||||
}
|
||||
.map .player .background, .map .player .label,.map .grenade .background, .map .bomb .background {
|
||||
position: absolute;
|
||||
width:100%;
|
||||
height:100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.map .player .label {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.5s;
|
||||
-webkit-font-smoothing: crisp-edges;
|
||||
font-size: 37px;
|
||||
text-shadow: 4px 4px 0 black;
|
||||
}
|
||||
.map .bomb {
|
||||
transition:transform 0.1s ease;
|
||||
}
|
||||
.map .bomb .background {
|
||||
background-image: url('./grenades/bomb.png');
|
||||
background-size: 66%;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border-radius:50%;
|
||||
}
|
||||
.map .bomb.planted .explode-point, .map .bomb.defusing .explode-point {
|
||||
animation: BombPlanted 2s infinite;
|
||||
}
|
||||
.map .bomb.exploded .explode-point {
|
||||
animation: BombExploded 2s 1 forwards;
|
||||
}
|
||||
.map .bomb.defused .explode-point {
|
||||
animation: BombDefused 2s 1 forwards;
|
||||
}
|
||||
.map .player.CT .label {
|
||||
background: rgb(16, 88, 197)
|
||||
}
|
||||
.map .player.T .label {
|
||||
background: rgb(255, 153, 0);
|
||||
}
|
||||
.map .player.T.hasBomb .label {
|
||||
background: red;
|
||||
}
|
||||
|
||||
@keyframes Hidden {
|
||||
from {
|
||||
}
|
||||
to {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.map .hidden {
|
||||
opacity: 0;
|
||||
animation: Hidden 1s ease 1s 1;
|
||||
animation-fill-mode: forwards;/**/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Player, Side } from "csgogsi";
|
||||
|
||||
export interface RadarPlayerObject {
|
||||
id: string,
|
||||
label: string | number,
|
||||
visible: boolean,
|
||||
side: Side,
|
||||
position: number[],
|
||||
forward: number,
|
||||
isActive: boolean,
|
||||
isAlive: boolean,
|
||||
steamid: string,
|
||||
hasBomb: boolean,
|
||||
flashed: boolean,
|
||||
shooting: boolean,
|
||||
lastShoot: number,
|
||||
scale: number,
|
||||
player: Player
|
||||
}
|
||||
|
||||
export interface RadarGrenadeObject {
|
||||
state: 'inair' | 'landed' | 'exploded'
|
||||
side: Side | null,
|
||||
type: 'decoy' | 'smoke' | 'frag' | 'firebomb' | 'flashbang' | 'inferno',
|
||||
position: number[],
|
||||
visible: boolean,
|
||||
id: string,
|
||||
}
|
||||
export interface GrenadeBase {
|
||||
owner: string,
|
||||
type: 'decoy' | 'smoke' | 'frag' | 'firebomb' | 'flashbang' | 'inferno'
|
||||
lifetime: string
|
||||
}
|
||||
|
||||
export interface DecoySmokeGrenade extends GrenadeBase {
|
||||
position: string,
|
||||
velocity: string,
|
||||
type: 'decoy' | 'smoke',
|
||||
effecttime: string,
|
||||
}
|
||||
|
||||
export interface DefaultGrenade extends GrenadeBase {
|
||||
position: string,
|
||||
type: 'frag' | 'firebomb' | 'flashbang',
|
||||
velocity: string,
|
||||
}
|
||||
|
||||
export interface InfernoGrenade extends GrenadeBase {
|
||||
type: 'inferno',
|
||||
flames: { [key: string]: string }
|
||||
}
|
||||
|
||||
export type Grenade = DecoySmokeGrenade | DefaultGrenade | InfernoGrenade;
|
||||
|
||||
export type ExtendedGrenade = Grenade & { id: string, side: Side | null, };
|
||||
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 583.2590342775677,
|
||||
"y": 428.92222042149115
|
||||
},
|
||||
"pxPerUX": 0.1983512056034216,
|
||||
"pxPerUY": -0.20108163914549304
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 27 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 536.3392873296655,
|
||||
"y": 638.0789844851904
|
||||
},
|
||||
"pxPerUX": 0.1907910426894958,
|
||||
"pxPerUY": -0.18993888105312648
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 361.7243823603619,
|
||||
"y": 579.553558767951
|
||||
},
|
||||
"pxPerUX": 0.1830927328891829,
|
||||
"pxPerUY": -0.17650705879909936
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 563.1339320329055,
|
||||
"y": 736.9535330430065
|
||||
},
|
||||
"pxPerUX": 0.2278315639654376,
|
||||
"pxPerUY": -0.22776482548619972
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 426.51386123945593,
|
||||
"y": 790.7266981544722
|
||||
},
|
||||
"pxPerUX": 0.2041685571162696,
|
||||
"pxPerUY": -0.20465735943851654
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,16 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 645.7196725473384,
|
||||
"y": 340.2921393569175
|
||||
},
|
||||
"pxPerUX": 0.20118507589946494,
|
||||
"pxPerUY": -0.20138282875746794,
|
||||
"originHeight": -170,
|
||||
},
|
||||
"file": radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,37 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const high = {
|
||||
"origin": {
|
||||
"x": 473.1284773048749,
|
||||
"y": 165.7329003801045
|
||||
},
|
||||
"pxPerUX": 0.14376095926926907,
|
||||
"pxPerUY": -0.14736670935219626
|
||||
};
|
||||
|
||||
const low = {
|
||||
"origin": {
|
||||
"x": 473.66746071612374,
|
||||
"y": 638.302101754172
|
||||
},
|
||||
"pxPerUX": 0.1436068746398272,
|
||||
"pxPerUY": -0.14533406508526941
|
||||
};
|
||||
|
||||
const config = {
|
||||
configs: [
|
||||
{
|
||||
id: 'high',
|
||||
config: high,
|
||||
isVisible: (height: number) => height >= -450,
|
||||
},
|
||||
{
|
||||
id: 'low',
|
||||
config: low,
|
||||
isVisible: (height: number) => height < -450,
|
||||
},
|
||||
],
|
||||
file: radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 927.3988878244819,
|
||||
"y": 343.8221009185496
|
||||
},
|
||||
"pxPerUX": 0.1923720959212443,
|
||||
"pxPerUY": -0.19427507725530338
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 127 KiB |
@@ -0,0 +1,15 @@
|
||||
import radar from './radar.png'
|
||||
|
||||
const config = {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 527.365542903922,
|
||||
"y": 511.81469648562296
|
||||
},
|
||||
"pxPerUX": 0.21532584158170223,
|
||||
"pxPerUY": -0.21299254526091588
|
||||
},
|
||||
"file":radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,67 @@
|
||||
import { Player } from 'csgogsi-socket';
|
||||
import radar from './radar.png'
|
||||
|
||||
const high = {
|
||||
"origin": {
|
||||
"x": 784.4793452283254,
|
||||
"y": 255.42597837029027
|
||||
},
|
||||
"pxPerUX": 0.19856123172015677,
|
||||
"pxPerUY": -0.19820052722907044
|
||||
};
|
||||
|
||||
const low = {
|
||||
"origin": {
|
||||
"x": 780.5145858437052,
|
||||
"y": 695.4259783702903
|
||||
},
|
||||
"pxPerUX": 0.1989615567841087,
|
||||
"pxPerUY": -0.19820052722907044
|
||||
};
|
||||
|
||||
const config = {
|
||||
configs: [
|
||||
{
|
||||
id: 'high',
|
||||
config: high,
|
||||
isVisible: (height: number) => height >= 11700,
|
||||
},
|
||||
{
|
||||
id: 'low',
|
||||
config: low,
|
||||
isVisible: (height: number) => height < 11700,
|
||||
},
|
||||
],
|
||||
zooms: [{
|
||||
threshold: (players: Player[]) => {
|
||||
const alivePlayers = players.filter(player => player.state.health);
|
||||
return alivePlayers.length > 0 && alivePlayers.every(player => player.position[2] < 11700)
|
||||
},
|
||||
origin: [472, 1130],
|
||||
zoom: 2
|
||||
}, {
|
||||
threshold: (players: Player[]) => {
|
||||
const alivePlayers = players.filter(player => player.state.health);
|
||||
return alivePlayers.length > 0 && players.filter(player => player.state.health).every(player => player.position[2] >= 11700);
|
||||
},
|
||||
origin: [528, 15],
|
||||
zoom: 1.75
|
||||
}],
|
||||
file: radar
|
||||
}
|
||||
|
||||
export default config;
|
||||
/*
|
||||
import radar from './radar.png'
|
||||
|
||||
export default {
|
||||
"config": {
|
||||
"origin": {
|
||||
"x": 971.5536135341899,
|
||||
"y": 424.5618319055844
|
||||
},
|
||||
"pxPerUX": 0.34708183044632246,
|
||||
"pxPerUY": -0.3450882697407333
|
||||
},
|
||||
"file":radar
|
||||
}*/
|
||||
|
After Width: | Height: | Size: 77 KiB |
@@ -0,0 +1,71 @@
|
||||
import de_mirage from './de_mirage';
|
||||
import de_cache from './de_cache';
|
||||
import de_dust2 from './de_dust2';
|
||||
import de_inferno from './de_inferno';
|
||||
import de_train from './de_train';
|
||||
import de_overpass from './de_overpass';
|
||||
import de_nuke from './de_nuke';
|
||||
import de_vertigo from './de_vertigo';
|
||||
import de_anubis from './de_anubis';
|
||||
import de_ancient from './de_ancient';
|
||||
import api from '../../../../api/api';
|
||||
import { Player } from 'csgogsi-socket';
|
||||
|
||||
export type ZoomAreas = {
|
||||
threshold: (players: Player[]) => boolean;
|
||||
origin: number[],
|
||||
zoom: number
|
||||
}
|
||||
|
||||
export interface ScaleConfig {
|
||||
origin: {
|
||||
x:number,
|
||||
y:number
|
||||
},
|
||||
pxPerUX: number,
|
||||
pxPerUY: number,
|
||||
originHeight?: number
|
||||
}
|
||||
|
||||
interface SingleLayer {
|
||||
config: ScaleConfig,
|
||||
file: string,
|
||||
zooms?: ZoomAreas[]
|
||||
}
|
||||
|
||||
interface DoubleLayer {
|
||||
configs: {
|
||||
id: string,
|
||||
config: ScaleConfig,
|
||||
isVisible: (height: number) => boolean
|
||||
}[],
|
||||
file: string,
|
||||
zooms?: ZoomAreas[]
|
||||
}
|
||||
|
||||
export type MapConfig = SingleLayer | DoubleLayer;
|
||||
|
||||
const maps: { [key: string] : MapConfig} = {
|
||||
de_mirage,
|
||||
de_cache,
|
||||
de_inferno,
|
||||
de_dust2,
|
||||
de_train,
|
||||
de_overpass,
|
||||
de_nuke,
|
||||
de_vertigo,
|
||||
de_ancient,
|
||||
de_anubis
|
||||
}
|
||||
|
||||
api.maps.get().then(fallbackMaps => {
|
||||
const mapNames = Object.keys(fallbackMaps);
|
||||
for(const mapName of mapNames){
|
||||
if(mapName in maps){
|
||||
continue;
|
||||
}
|
||||
maps[mapName] = fallbackMaps[mapName];
|
||||
}
|
||||
}).catch(() => {});
|
||||
|
||||
export default maps;
|
||||
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import { isDev } from './../../api/api';
|
||||
import { CSGO } from "csgogsi-socket";
|
||||
import LexoRadarContainer from './LexoRadar/LexoRadarContainer';
|
||||
|
||||
|
||||
|
||||
interface Props { radarSize: number, game: CSGO }
|
||||
interface State {
|
||||
showRadar: boolean,
|
||||
loaded: boolean,
|
||||
boltobserv:{
|
||||
css: boolean,
|
||||
maps: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export default class Radar extends React.Component<Props, State> {
|
||||
state = {
|
||||
showRadar: true,
|
||||
loaded: !isDev,
|
||||
boltobserv: {
|
||||
css: true,
|
||||
maps: true
|
||||
}
|
||||
}
|
||||
async componentDidMount(){
|
||||
/*if(isDev){
|
||||
const response = await fetch('hud.json');
|
||||
const hud = await response.json();
|
||||
const boltobserv = {
|
||||
css: Boolean(hud && hud.boltobserv && hud.boltobserv.css),
|
||||
maps: Boolean(hud && hud.boltobserv && hud.boltobserv.maps)
|
||||
}
|
||||
this.setState({boltobserv, loaded: true});
|
||||
}*/
|
||||
}
|
||||
|
||||
render() {
|
||||
const { players, player, bomb, grenades, map } = this.props.game;
|
||||
return <LexoRadarContainer
|
||||
players={players}
|
||||
player={player}
|
||||
bomb={bomb}
|
||||
grenades={grenades}
|
||||
size={this.props.radarSize}
|
||||
mapName={map.name.substring(map.name.lastIndexOf('/')+1)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import "./radar.scss";
|
||||
import { Match, Veto } from "../../api/interfaces";
|
||||
import { Map, CSGO, Team } from 'csgogsi-socket';
|
||||
import { actions } from './../../App';
|
||||
import Radar from './Radar'
|
||||
import TeamLogo from "../MatchBar/TeamLogo";
|
||||
|
||||
interface Props { match: Match | null, map: Map, game: CSGO }
|
||||
interface State { showRadar: boolean, radarSize: number, showBig: boolean }
|
||||
|
||||
export default class RadarMaps extends React.Component<Props, State> {
|
||||
state = {
|
||||
showRadar: true,
|
||||
radarSize: 350,
|
||||
showBig: false
|
||||
}
|
||||
componentDidMount() {
|
||||
actions.on('radarBigger', () => this.radarChangeSize(20));
|
||||
actions.on('radarSmaller', () => this.radarChangeSize(-20));
|
||||
actions.on('toggleRadar', () => { this.setState(state => ({ showRadar: !state.showRadar })) });
|
||||
|
||||
actions.on("toggleRadarView", () => {
|
||||
this.setState({showBig:!this.state.showBig});
|
||||
});
|
||||
}
|
||||
radarChangeSize = (delta: number) => {
|
||||
const newSize = this.state.radarSize + delta;
|
||||
this.setState({ radarSize: newSize > 0 ? newSize : this.state.radarSize });
|
||||
}
|
||||
render() {
|
||||
const { match } = this.props;
|
||||
const { radarSize, showBig, showRadar } = this.state;
|
||||
const size = showBig ? 600 : radarSize;
|
||||
return (
|
||||
<div id={`radar_maps_container`} className={`${!showRadar ? 'hide' : ''} ${showBig ? 'preview':''}`}>
|
||||
<div className="radar-component-container" style={{width: `${size}px`, height: `${size}px`}}><Radar radarSize={size} game={this.props.game} /></div>
|
||||
{match ? <MapsBar match={this.props.match} map={this.props.map} game={this.props.game} /> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MapsBar extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const { match, map } = this.props;
|
||||
if (!match || !match.vetos.length) return '';
|
||||
const picks = match.vetos.filter(veto => veto.type !== "ban" && veto.mapName);
|
||||
if (picks.length > 3) {
|
||||
const current = picks.find(veto => map.name.includes(veto.mapName));
|
||||
if (!current) return null;
|
||||
return <div id="maps_container">
|
||||
{<MapEntry veto={current} map={map} team={current.type === "decider" ? null : map.team_ct.id === current.teamId ? map.team_ct : map.team_t} />}
|
||||
</div>
|
||||
}
|
||||
return <div id="maps_container">
|
||||
{match.vetos.filter(veto => veto.type !== "ban").filter(veto => veto.teamId || veto.type === "decider").map(veto => <MapEntry key={veto.mapName} veto={veto} map={this.props.map} team={veto.type === "decider" ? null : map.team_ct.id === veto.teamId ? map.team_ct : map.team_t} />)}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
class MapEntry extends React.PureComponent<{ veto: Veto, map: Map, team: Team | null }> {
|
||||
render() {
|
||||
const { veto, map, team } = this.props;
|
||||
return <div className="veto_entry">
|
||||
<div className="team_logo">{team ? <TeamLogo team={team} /> : null}</div>
|
||||
<div className={`map_name ${map.name.includes(veto.mapName) ? 'active' : ''}`}>{veto.mapName}</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#radar_maps_container {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
border: none;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
transition: all 1s;
|
||||
.map-container {
|
||||
transition: all 1s;
|
||||
}
|
||||
}
|
||||
#iframe_radar {
|
||||
border: none;
|
||||
}
|
||||
#radar_maps_container.hide {
|
||||
opacity: 0;
|
||||
}
|
||||
#radar_maps_container.preview {
|
||||
transform: rotate3d(1, 0, 0, 14deg) translateX(-50%);
|
||||
transform-style: preserve-3d;
|
||||
left: 50%;
|
||||
top: 100px;
|
||||
.map {
|
||||
perspective: 500px;
|
||||
}
|
||||
}
|
||||
.radar-component-container {
|
||||
width: 350px;
|
||||
height:350px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#maps_container {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
.veto_entry {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
>div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.team_logo {
|
||||
img {
|
||||
max-height: 23px;
|
||||
padding-right: 3px;
|
||||
max-width: 23px;
|
||||
}
|
||||
.logo {
|
||||
height: 23px;
|
||||
width: 23px;
|
||||
}
|
||||
}
|
||||
.map_name.active {
|
||||
text-shadow: 0 0 15px white;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||