mirror of
https://codeberg.org/demostf/frontend.git
synced 2026-06-03 10:14:13 +02:00
show projectiles in viewer
This commit is contained in:
parent
213d2c6753
commit
22ad43a4b7
7 changed files with 215 additions and 89 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
|
@ -6,7 +6,7 @@
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@demostf/edit": "0.2.0",
|
"@demostf/edit": "0.2.0",
|
||||||
"@demostf/tf-demos-viewer": "^0.1.2",
|
"@demostf/tf-demos-viewer": "^0.2.0",
|
||||||
"@lutaok/solid-modal": "^0.1.1",
|
"@lutaok/solid-modal": "^0.1.1",
|
||||||
"@solid-primitives/autofocus": "^0.0.111",
|
"@solid-primitives/autofocus": "^0.0.111",
|
||||||
"@solid-primitives/keyboard": "^1.2.8",
|
"@solid-primitives/keyboard": "^1.2.8",
|
||||||
|
|
@ -23,9 +23,9 @@
|
||||||
"integrity": "sha512-s9wk3QVm+aTpMhIyfdGIHRm5qHp7FQ1dq/Jn0fms+lXsB1xY3wgjfWH+5gwRjjo/Dd3UMNM0o3atjO2uh+CxOQ=="
|
"integrity": "sha512-s9wk3QVm+aTpMhIyfdGIHRm5qHp7FQ1dq/Jn0fms+lXsB1xY3wgjfWH+5gwRjjo/Dd3UMNM0o3atjO2uh+CxOQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@demostf/tf-demos-viewer": {
|
"node_modules/@demostf/tf-demos-viewer": {
|
||||||
"version": "0.1.2",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@demostf/tf-demos-viewer/-/tf-demos-viewer-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@demostf/tf-demos-viewer/-/tf-demos-viewer-0.2.0.tgz",
|
||||||
"integrity": "sha512-BeeUWsFmICuma2FWJK4gjrILwwdgZbeCm2TELZfcJDV4QYfmukI8weNdQb3RFurxcebFZ0IER9fA0oXAdiGYpw=="
|
"integrity": "sha512-Ef/hlTGWPknog1mXnJJ2Uz158Ud6vyLaUBlQ7j1rI/772zKmVeQEfnKY5ut5ZlfoELwNH5Aj3+QBSqSU/DV19A=="
|
||||||
},
|
},
|
||||||
"node_modules/@lutaok/solid-modal": {
|
"node_modules/@lutaok/solid-modal": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@demostf/edit": "0.2.0",
|
"@demostf/edit": "0.2.0",
|
||||||
"@demostf/tf-demos-viewer": "^0.1.2",
|
"@demostf/tf-demos-viewer": "^0.2.0",
|
||||||
"@lutaok/solid-modal": "^0.1.1",
|
"@lutaok/solid-modal": "^0.1.1",
|
||||||
"@solid-primitives/autofocus": "^0.0.111",
|
"@solid-primitives/autofocus": "^0.0.111",
|
||||||
"@solid-primitives/keyboard": "^1.2.8",
|
"@solid-primitives/keyboard": "^1.2.8",
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,7 @@ export const Analyser = (props: AnalyseProps) => {
|
||||||
|
|
||||||
const players = () => parser.getPlayersAtTick(tick());
|
const players = () => parser.getPlayersAtTick(tick());
|
||||||
const buildings = () => parser.getBuildingsAtTick(tick());
|
const buildings = () => parser.getBuildingsAtTick(tick());
|
||||||
|
const projectiles = () => parser.getProjectilesAtTick(tick());
|
||||||
const kills = parser.getKills();
|
const kills = parser.getKills();
|
||||||
const playButtonText = () => (playing()) ? '⏸' : '▶️';
|
const playButtonText = () => (playing()) ? '⏸' : '▶️';
|
||||||
const inShared = session && !session.isOwner();
|
const inShared = session && !session.isOwner();
|
||||||
|
|
@ -226,6 +227,7 @@ export const Analyser = (props: AnalyseProps) => {
|
||||||
<MapRender size={worldSize}
|
<MapRender size={worldSize}
|
||||||
players={players()}
|
players={players()}
|
||||||
buildings={buildings()}
|
buildings={buildings()}
|
||||||
|
projectiles={projectiles()}
|
||||||
header={props.header}
|
header={props.header}
|
||||||
world={backgroundBoundaries}
|
world={backgroundBoundaries}
|
||||||
scale={scale()}/>
|
scale={scale()}/>
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,79 @@
|
||||||
import {ParsedDemo, PlayerState, WorldBoundaries, Kill, BuildingState} from "./Parser";
|
import {BuildingState, Kill, ParsedDemo, PlayerState, ProjectileState, ProjectileType, WorldBoundaries} from "./Parser";
|
||||||
|
|
||||||
function getCacheBuster(): string {
|
function getCacheBuster(): string {
|
||||||
const url = document.querySelector('script[src*="viewer"]').attributes.src.value;
|
const url = document.querySelector('script[src*="viewer"]').attributes.src.value;
|
||||||
return url.substring("/viewer.js".length);
|
return url.substring("/viewer.js".length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AsyncParser {
|
export class AsyncParser {
|
||||||
buffer: ArrayBuffer;
|
buffer: ArrayBuffer;
|
||||||
demo: ParsedDemo;
|
demo: ParsedDemo;
|
||||||
world: WorldBoundaries;
|
world: WorldBoundaries;
|
||||||
progressCallback: (progress: number) => void;
|
progressCallback: (progress: number) => void;
|
||||||
|
|
||||||
constructor(buffer: ArrayBuffer, progressCallback: (progress: number) => void) {
|
constructor(buffer: ArrayBuffer, progressCallback: (progress: number) => void) {
|
||||||
this.buffer = buffer;
|
this.buffer = buffer;
|
||||||
this.progressCallback = progressCallback;
|
this.progressCallback = progressCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
cache(): Promise<ParsedDemo> {
|
cache(): Promise<ParsedDemo> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const worker = new Worker(`/parse-worker.js${getCacheBuster()}`);
|
const worker = new Worker(`/parse-worker.js${getCacheBuster()}`);
|
||||||
worker.postMessage({
|
worker.postMessage({
|
||||||
buffer: this.buffer
|
buffer: this.buffer
|
||||||
}, [this.buffer]);
|
}, [this.buffer]);
|
||||||
worker.onmessage = (event) => {
|
worker.onmessage = (event) => {
|
||||||
if (event.data.error) {
|
if (event.data.error) {
|
||||||
reject(event.data.error);
|
reject(event.data.error);
|
||||||
return;
|
return;
|
||||||
} else if (event.data.progress) {
|
} else if (event.data.progress) {
|
||||||
this.progressCallback(event.data.progress);
|
this.progressCallback(event.data.progress);
|
||||||
return;
|
return;
|
||||||
} else if (event.data.demo) {
|
} else if (event.data.demo) {
|
||||||
const cachedData: ParsedDemo = event.data.demo;
|
const cachedData: ParsedDemo = event.data.demo;
|
||||||
console.log(`packed data: ${(cachedData.data.length / (1024 * 1024)).toFixed(1)}MB`);
|
console.log(`packed data: ${(cachedData.data.length / (1024 * 1024)).toFixed(1)}MB`);
|
||||||
this.world = cachedData.world;
|
this.world = cachedData.world;
|
||||||
this.demo = new ParsedDemo(cachedData.playerCount, cachedData.buildingCount, cachedData.world, cachedData.header, cachedData.data, cachedData.kills, cachedData.playerInfo, cachedData.tickCount);
|
this.demo = new ParsedDemo(cachedData.playerCount, cachedData.buildingCount, cachedData.projectileCount, cachedData.world, cachedData.header, cachedData.data, cachedData.kills, cachedData.playerInfo, cachedData.tickCount);
|
||||||
resolve(this.demo);
|
resolve(this.demo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlayersAtTick(tick: number): PlayerState[] {
|
getPlayersAtTick(tick: number): PlayerState[] {
|
||||||
const players: PlayerState[] = [];
|
const players: PlayerState[] = [];
|
||||||
for (let i = 0; i < this.demo.playerCount; i++) {
|
for (let i = 0; i < this.demo.playerCount; i++) {
|
||||||
players.push(this.demo.getPlayer(tick, i));
|
players.push(this.demo.getPlayer(tick, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return players;
|
return players;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuildingsAtTick(tick: number): BuildingState[] {
|
getBuildingsAtTick(tick: number): BuildingState[] {
|
||||||
const buildings: BuildingState[] = [];
|
const buildings: BuildingState[] = [];
|
||||||
for (let i = 0; i < this.demo.buildingCount; i++) {
|
for (let i = 0; i < this.demo.buildingCount; i++) {
|
||||||
const building = this.demo.getBuilding(tick, i);
|
const building = this.demo.getBuilding(tick, i);
|
||||||
if (building.health > 0) {
|
if (building.health > 0) {
|
||||||
buildings.push(building);
|
buildings.push(building);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildings;
|
return buildings;
|
||||||
}
|
}
|
||||||
|
|
||||||
getKills(): Kill[] {
|
getProjectilesAtTick(tick: number): ProjectileState[] {
|
||||||
return this.demo.kills
|
const projectiles: ProjectileState[] = [];
|
||||||
}
|
for (let i = 0; i < this.demo.projectileCount; i++) {
|
||||||
|
const projectile = this.demo.getProjectile(tick, i);
|
||||||
|
if (projectile.projectileType !== ProjectileType.Unknown && projectile.position.x > this.world.boundary_min.x && projectile.position.y > this.world.boundary_min.y) {
|
||||||
|
projectiles.push(projectile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKills(): Kill[] {
|
||||||
|
return this.demo.kills
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export async function parseDemo(bytes: Uint8Array, progressCallback: (progress:
|
||||||
|
|
||||||
let playerCount = state.player_count;
|
let playerCount = state.player_count;
|
||||||
let buildingCount = state.building_count;
|
let buildingCount = state.building_count;
|
||||||
|
let projectileCount = state.projectile_count;
|
||||||
let boundaries = state.boundaries;
|
let boundaries = state.boundaries;
|
||||||
let interval_per_tick = state.interval_per_tick;
|
let interval_per_tick = state.interval_per_tick;
|
||||||
let tickCount = state.tick_count;
|
let tickCount = state.tick_count;
|
||||||
|
|
@ -56,6 +57,7 @@ export async function parseDemo(bytes: Uint8Array, progressCallback: (progress:
|
||||||
return new ParsedDemo(
|
return new ParsedDemo(
|
||||||
playerCount,
|
playerCount,
|
||||||
buildingCount,
|
buildingCount,
|
||||||
|
projectileCount,
|
||||||
{
|
{
|
||||||
boundary_min: {
|
boundary_min: {
|
||||||
x: boundaries.boundary_min.x,
|
x: boundaries.boundary_min.x,
|
||||||
|
|
@ -115,6 +117,16 @@ export enum BuildingType {
|
||||||
Unknown = 7,
|
Unknown = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ProjectileType {
|
||||||
|
Rocket = 0,
|
||||||
|
HealingArrow = 1,
|
||||||
|
Sticky = 2,
|
||||||
|
Pipe = 3,
|
||||||
|
Flare = 4,
|
||||||
|
LooseCannon = 5,
|
||||||
|
Unknown = 7,
|
||||||
|
}
|
||||||
|
|
||||||
export interface WorldBoundaries {
|
export interface WorldBoundaries {
|
||||||
boundary_min: {
|
boundary_min: {
|
||||||
x: number,
|
x: number,
|
||||||
|
|
@ -151,6 +163,16 @@ export interface BuildingState {
|
||||||
buildingType: BuildingType,
|
buildingType: BuildingType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProjectileState {
|
||||||
|
position: {
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
},
|
||||||
|
angle: number,
|
||||||
|
team: Team,
|
||||||
|
projectileType: ProjectileType,
|
||||||
|
}
|
||||||
|
|
||||||
export interface Header {
|
export interface Header {
|
||||||
interval_per_tick: number,
|
interval_per_tick: number,
|
||||||
map: string
|
map: string
|
||||||
|
|
@ -177,6 +199,7 @@ function unpack_angle(val: number): number {
|
||||||
export class ParsedDemo {
|
export class ParsedDemo {
|
||||||
public readonly playerCount: number;
|
public readonly playerCount: number;
|
||||||
public readonly buildingCount: number;
|
public readonly buildingCount: number;
|
||||||
|
public readonly projectileCount: number;
|
||||||
public readonly world: WorldBoundaries;
|
public readonly world: WorldBoundaries;
|
||||||
public readonly data: Uint8Array;
|
public readonly data: Uint8Array;
|
||||||
public readonly header: Header;
|
public readonly header: Header;
|
||||||
|
|
@ -184,9 +207,10 @@ export class ParsedDemo {
|
||||||
public readonly kills: Kill[];
|
public readonly kills: Kill[];
|
||||||
public readonly playerInfo: PlayerInfo[];
|
public readonly playerInfo: PlayerInfo[];
|
||||||
|
|
||||||
constructor(playerCount: number, buildingCount: number, world: WorldBoundaries, header: Header, data: Uint8Array, kills: Kill[], playerInfo: PlayerInfo[], tickCount: number) {
|
constructor(playerCount: number, buildingCount: number, projectileCount: number, world: WorldBoundaries, header: Header, data: Uint8Array, kills: Kill[], playerInfo: PlayerInfo[], tickCount: number) {
|
||||||
this.playerCount = playerCount;
|
this.playerCount = playerCount;
|
||||||
this.buildingCount = buildingCount;
|
this.buildingCount = buildingCount;
|
||||||
|
this.projectileCount = projectileCount;
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
@ -212,10 +236,22 @@ export class ParsedDemo {
|
||||||
const base = (this.playerCount * this.tickCount * PLAYER_PACK_SIZE) + ((buildingIndex * this.tickCount) + tick) * BUILDING_PACK_SIZE;
|
const base = (this.playerCount * this.tickCount * PLAYER_PACK_SIZE) + ((buildingIndex * this.tickCount) + tick) * BUILDING_PACK_SIZE;
|
||||||
return unpackBuilding(this.data, base, this.world);
|
return unpackBuilding(this.data, base, this.world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProjectile(tick: number, projectileIndex: number): ProjectileState {
|
||||||
|
if (projectileIndex >= this.projectileCount) {
|
||||||
|
throw new Error("Projectile out of bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
const base = (this.playerCount * this.tickCount * PLAYER_PACK_SIZE) +
|
||||||
|
(this.buildingCount * this.tickCount * BUILDING_PACK_SIZE) +
|
||||||
|
((projectileIndex * this.tickCount) + tick) * PROJECTILE_PACK_SIZE;
|
||||||
|
return unpackProjectile(this.data, base, this.world);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const PLAYER_PACK_SIZE = 8;
|
const PLAYER_PACK_SIZE = 8;
|
||||||
const BUILDING_PACK_SIZE = 7;
|
const BUILDING_PACK_SIZE = 7;
|
||||||
|
const PROJECTILE_PACK_SIZE = 6;
|
||||||
|
|
||||||
function unpackPlayer(bytes: Uint8Array, base: number, world: WorldBoundaries, info: PlayerInfo): PlayerState {
|
function unpackPlayer(bytes: Uint8Array, base: number, world: WorldBoundaries, info: PlayerInfo): PlayerState {
|
||||||
const x = unpack_f32(bytes[base] + (bytes[base + 1] << 8), world.boundary_min.x, world.boundary_max.x);
|
const x = unpack_f32(bytes[base] + (bytes[base + 1] << 8), world.boundary_min.x, world.boundary_max.x);
|
||||||
|
|
@ -257,3 +293,19 @@ function unpackBuilding(bytes: Uint8Array, base: number, world: WorldBoundaries)
|
||||||
level,
|
level,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function unpackProjectile(bytes: Uint8Array, base: number, world: WorldBoundaries): ProjectileState {
|
||||||
|
const x = unpack_f32(bytes[base] + (bytes[base + 1] << 8), world.boundary_min.x, world.boundary_max.x);
|
||||||
|
const y = unpack_f32(bytes[base + 2] + (bytes[base + 3] << 8), world.boundary_min.y, world.boundary_max.y);
|
||||||
|
const team_type = bytes[base + 4];
|
||||||
|
const team = (((team_type >> 4) & 1) === 0) ? Team.Blue : Team.Red;
|
||||||
|
const projectileType = ((team_type >> 5) & 7) as ProjectileType;
|
||||||
|
const angle = unpack_angle(bytes[base + 5]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: {x, y},
|
||||||
|
angle,
|
||||||
|
team,
|
||||||
|
projectileType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,50 @@
|
||||||
import {Player as PlayerDot} from './Render/Player';
|
import {Player as PlayerDot} from './Render/Player';
|
||||||
import {Building as BuildingDot} from './Render/Building';
|
import {Building as BuildingDot} from './Render/Building';
|
||||||
|
import {Projectile as ProjectileDot} from './Render/Projectile';
|
||||||
import {findMapAlias} from './MapBoundries';
|
import {findMapAlias} from './MapBoundries';
|
||||||
import {PlayerState, Header, WorldBoundaries, BuildingState} from "./Data/Parser";
|
import {PlayerState, Header, WorldBoundaries, BuildingState, ProjectileState} from "./Data/Parser";
|
||||||
import {createEffect, Show} from "solid-js";
|
import {Show} from "solid-js";
|
||||||
|
|
||||||
export interface MapRenderProps {
|
export interface MapRenderProps {
|
||||||
header: Header;
|
header: Header;
|
||||||
players: PlayerState[];
|
players: PlayerState[];
|
||||||
buildings: BuildingState[];
|
buildings: BuildingState[];
|
||||||
size: {
|
projectiles: ProjectileState[];
|
||||||
width: number;
|
size: {
|
||||||
height: number;
|
width: number;
|
||||||
},
|
height: number;
|
||||||
world: WorldBoundaries;
|
},
|
||||||
scale: number;
|
world: WorldBoundaries;
|
||||||
|
scale: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const map_root = document.querySelector('[data-maps]').getAttribute('data-maps');
|
const map_root = document.querySelector('[data-maps]').getAttribute('data-maps');
|
||||||
|
|
||||||
export function MapRender(props: MapRenderProps) {
|
export function MapRender(props: MapRenderProps) {
|
||||||
const mapAlias = findMapAlias(props.header.map);
|
const mapAlias = findMapAlias(props.header.map);
|
||||||
const image = `${map_root}images/${mapAlias}.webp`;
|
const image = `${map_root}images/${mapAlias}.webp`;
|
||||||
const background = `url(${image})`;
|
const background = `url(${image})`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg class="map-background" width={props.size.width} height={props.size.height}
|
<svg class="map-background" width={props.size.width} height={props.size.height}
|
||||||
style={{"background-image": background}}>
|
style={{"background-image": background}}>
|
||||||
<For each={props.players}>{(player) =>
|
<For each={props.players}>{(player) =>
|
||||||
<Show when={player.health}>
|
<Show when={player.health}>
|
||||||
<PlayerDot player={player} mapBoundary={props.world} targetSize={props.size} scale={props.scale} />
|
<PlayerDot player={player} mapBoundary={props.world} targetSize={props.size} scale={props.scale}/>
|
||||||
</Show>
|
</Show>
|
||||||
}</For>
|
}</For>
|
||||||
<For each={props.buildings}>{(building) =>
|
<For each={props.buildings}>{(building) =>
|
||||||
<Show when={building.position.x}>
|
<Show when={building.position.x}>
|
||||||
<BuildingDot building={building} mapBoundary={props.world} targetSize={props.size} scale={props.scale}/>
|
<BuildingDot building={building} mapBoundary={props.world} targetSize={props.size}
|
||||||
</Show>
|
scale={props.scale}/>
|
||||||
}</For>
|
</Show>
|
||||||
</svg>
|
}</For>
|
||||||
);
|
<For each={props.projectiles}>{(projectile) =>
|
||||||
|
<Show when={projectile.position.x}>
|
||||||
|
<ProjectileDot projectile={projectile} mapBoundary={props.world} targetSize={props.size}
|
||||||
|
scale={props.scale}/>
|
||||||
|
</Show>
|
||||||
|
}</For>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
51
script/viewer/Analyse/Render/Projectile.tsx
Normal file
51
script/viewer/Analyse/Render/Projectile.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {ProjectileState, ProjectileType, Team, WorldBoundaries} from "../Data/Parser";
|
||||||
|
import {Show} from "solid-js";
|
||||||
|
|
||||||
|
export interface ProjectileProp {
|
||||||
|
projectile: ProjectileState;
|
||||||
|
mapBoundary: WorldBoundaries;
|
||||||
|
targetSize: {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
scale: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Projectile(props: ProjectileProp) {
|
||||||
|
const worldWidth = props.mapBoundary.boundary_max.x - props.mapBoundary.boundary_min.x;
|
||||||
|
const worldHeight = props.mapBoundary.boundary_max.y - props.mapBoundary.boundary_min.y;
|
||||||
|
const x = () => props.projectile.position.x - props.mapBoundary.boundary_min.x;
|
||||||
|
const y = () => props.projectile.position.y - props.mapBoundary.boundary_min.y;
|
||||||
|
const scaledX = () => x() / worldWidth * props.targetSize.width;
|
||||||
|
const scaledY = () => (worldHeight - y()) / worldHeight * props.targetSize.height;
|
||||||
|
const teamColor = () => (props.projectile.team === Team.Red) ? '#a75d50' : '#5b818f';
|
||||||
|
|
||||||
|
const transform = () => `translate(${scaledX()} ${scaledY()}) scale(${1 / props.scale})`;
|
||||||
|
const rotate = () => `rotate(${270 - props.projectile.angle})`;
|
||||||
|
try {
|
||||||
|
return <g transform={transform()}>
|
||||||
|
<Show when={projectileIsAngled(props.projectile.projectileType)}>
|
||||||
|
<polygon points="-3,-4 0,0 3,-4 0,8" stroke="white" fill={teamColor()}
|
||||||
|
transform={rotate()}/>
|
||||||
|
</Show>
|
||||||
|
<Show when={!projectileIsAngled(props.projectile.projectileType)}>
|
||||||
|
<circle r={3} stroke-width={1} stroke="white" fill={teamColor()}/>
|
||||||
|
</Show>
|
||||||
|
</g>
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function projectileIsAngled(type: ProjectileType): boolean {
|
||||||
|
switch (type) {
|
||||||
|
case ProjectileType.Flare:
|
||||||
|
case ProjectileType.HealingArrow:
|
||||||
|
case ProjectileType.Rocket:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue