feat: Add CS:GO rank icons for legacy matches
Implemented rank icon display system for pre-CS2 matches: New Components: - RankIcon.svelte: Displays CS:GO skill group icons (0-18) - Supports sm/md/lg sizes - Shows appropriate rank icon based on skill group - Includes hover tooltips with rank names - Handles all 19 rank tiers (Silver I → Global Elite) Updated Components: - PremierRatingBadge: Now intelligently switches between: - CS:GO rank icons (when rank_old exists, rank_new doesn't) - Premier rating badge (when rank_new exists) - "Unranked" text (when neither exists) Assets: - Rank icons already present in static/images/rank_icons/ - Weapon icons already present in static/images/weapons/ - All icons in SVG format for crisp display at any size Display Logic: - Legacy matches (pre-Sept 2023): Show CS:GO rank icons - Modern matches (CS2): Show Premier rating with trophy icon - Automatically detects based on rank_old/rank_new fields The scoreboard now displays the appropriate ranking visualization based on match era, matching the original CSGO.WTF design. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { formatPremierRating, getPremierRatingChange } from '$lib/utils/formatters';
|
||||
import { Trophy, TrendingUp, TrendingDown } from 'lucide-svelte';
|
||||
import RankIcon from './RankIcon.svelte';
|
||||
|
||||
interface Props {
|
||||
rating: number | undefined | null;
|
||||
@@ -22,6 +23,11 @@
|
||||
class: className = ''
|
||||
}: Props = $props();
|
||||
|
||||
// Determine if this is a legacy CS:GO match (has old rank but no new rating)
|
||||
const isLegacyRank = $derived(
|
||||
(!rating || rating === 0) && oldRating !== undefined && oldRating !== null && oldRating > 0
|
||||
);
|
||||
|
||||
const tierInfo = $derived(formatPremierRating(rating));
|
||||
const changeInfo = $derived(showChange ? getPremierRatingChange(oldRating, rating) : null);
|
||||
|
||||
@@ -44,7 +50,15 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class={classes}>
|
||||
{#if isLegacyRank}
|
||||
<!-- Show CS:GO rank icon for legacy matches -->
|
||||
<RankIcon skillGroup={oldRating} {size} class={className} />
|
||||
{:else if !rating || rating === 0}
|
||||
<!-- No rating available -->
|
||||
<span class="text-sm text-base-content/50">Unranked</span>
|
||||
{:else}
|
||||
<!-- Show Premier rating for CS2 matches -->
|
||||
<div class={classes}>
|
||||
{#if showIcon}
|
||||
<Trophy class={iconSizes[size]} />
|
||||
{/if}
|
||||
@@ -65,4 +79,5 @@
|
||||
{changeInfo.display}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
69
src/lib/components/ui/RankIcon.svelte
Normal file
69
src/lib/components/ui/RankIcon.svelte
Normal file
@@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* CS:GO Skill Group Rank Icon Component
|
||||
* Displays the appropriate rank icon based on skill group (0-18)
|
||||
*/
|
||||
interface Props {
|
||||
/** CS:GO skill group (0-18) */
|
||||
skillGroup: number | undefined | null;
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
showLabel?: boolean;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let { skillGroup, size = 'md', showLabel = false, class: className = '' }: Props = $props();
|
||||
|
||||
// Map skill groups to rank names
|
||||
const rankNames: Record<number, string> = {
|
||||
0: 'Unranked',
|
||||
1: 'Silver I',
|
||||
2: 'Silver II',
|
||||
3: 'Silver III',
|
||||
4: 'Silver IV',
|
||||
5: 'Silver Elite',
|
||||
6: 'Silver Elite Master',
|
||||
7: 'Gold Nova I',
|
||||
8: 'Gold Nova II',
|
||||
9: 'Gold Nova III',
|
||||
10: 'Gold Nova Master',
|
||||
11: 'Master Guardian I',
|
||||
12: 'Master Guardian II',
|
||||
13: 'Master Guardian Elite',
|
||||
14: 'Distinguished Master Guardian',
|
||||
15: 'Legendary Eagle',
|
||||
16: 'Legendary Eagle Master',
|
||||
17: 'Supreme Master First Class',
|
||||
18: 'The Global Elite'
|
||||
};
|
||||
|
||||
const sizeClasses = {
|
||||
sm: 'h-6 w-6',
|
||||
md: 'h-8 w-8',
|
||||
lg: 'h-12 w-12'
|
||||
};
|
||||
|
||||
const labelSizeClasses = {
|
||||
sm: 'text-xs',
|
||||
md: 'text-sm',
|
||||
lg: 'text-base'
|
||||
};
|
||||
|
||||
const iconPath = $derived(
|
||||
skillGroup !== undefined && skillGroup !== null && skillGroup >= 0 && skillGroup <= 18
|
||||
? `/images/rank_icons/skillgroup${skillGroup}.svg`
|
||||
: '/images/rank_icons/skillgroup_none.svg'
|
||||
);
|
||||
|
||||
const rankName = $derived(
|
||||
skillGroup !== undefined && skillGroup !== null ? rankNames[skillGroup] || 'Unknown' : 'Unknown'
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if showLabel}
|
||||
<div class="inline-flex items-center gap-2 {className}">
|
||||
<img src={iconPath} alt={rankName} class={sizeClasses[size]} />
|
||||
<span class="font-medium {labelSizeClasses[size]}">{rankName}</span>
|
||||
</div>
|
||||
{:else}
|
||||
<img src={iconPath} alt={rankName} title={rankName} class={`${sizeClasses[size]} ${className}`} />
|
||||
{/if}
|
||||
1
static/images/static/images/rank_icons/skillgroup.svg
Normal file
1
static/images/static/images/rank_icons/skillgroup.svg
Normal file
@@ -0,0 +1 @@
|
||||
Not found.
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="13px" viewBox="0 0 32 13" enable-background="new 0 0 32 13" xml:space="preserve">
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-3.0381" y1="-0.6826" x2="42.4632" y2="16.1303">
|
||||
<stop offset="0" style="stop-color:#414042"/>
|
||||
<stop offset="1" style="stop-color:#29292A"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M30.475,0.281h-3.842h-1.055h-1.574h-3.68h-0.68h-8.289H5.018H2.492H1.416
|
||||
c-0.119,0-0.229,0.028-0.334,0.067C1.059,0.357,1.037,0.365,1.014,0.376C0.916,0.422,0.826,0.48,0.75,0.555
|
||||
C0.738,0.566,0.73,0.579,0.719,0.591C0.682,0.63,0.645,0.668,0.615,0.713C0.594,0.746,0.582,0.785,0.564,0.82
|
||||
C0.551,0.849,0.535,0.875,0.525,0.905C0.484,1.012,0.457,1.123,0.457,1.244v0.213v0.127v2.094v1.127v1.307v5.338
|
||||
c0,0.125,0.027,0.24,0.07,0.35c0.014,0.037,0.039,0.068,0.057,0.104c0.027,0.05,0.049,0.104,0.084,0.148
|
||||
c0.01,0.013,0.025,0.018,0.037,0.029c0.061,0.07,0.135,0.123,0.215,0.173c0.033,0.021,0.061,0.049,0.096,0.065
|
||||
c0.123,0.057,0.256,0.094,0.4,0.094h1.297h3.969h2.135l0.076,0.053c-0.01-0.019-0.021-0.034-0.029-0.053h5.553h4.799h1.58H21.1
|
||||
h3.223h0.787h0.979h2.426h0.461h1.5c0.053,0,0.105-0.006,0.156-0.018c0.041-0.008,0.078-0.034,0.119-0.049
|
||||
c0.061-0.02,0.117-0.038,0.174-0.069c0.066-0.035,0.121-0.08,0.18-0.128c0.047-0.039,0.094-0.072,0.135-0.118
|
||||
c0.014-0.014,0.031-0.023,0.045-0.038c0.043-0.052,0.068-0.112,0.102-0.169c0.025-0.04,0.055-0.076,0.072-0.118
|
||||
c0.053-0.119,0.088-0.242,0.088-0.367V9.729V8.857V8.324V4.387V2.875V2.502V1.133C31.545,0.602,31.004,0.281,30.475,0.281z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="26.291" y1="12.4131" x2="3.1042" y2="-0.899">
|
||||
<stop offset="0" style="stop-color:#565759"/>
|
||||
<stop offset="0.5" style="stop-color:#666669"/>
|
||||
<stop offset="1" style="stop-color:#77787B"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M30.631,0.16H1.26c-0.664,0-1.203,0.543-1.203,1.207v10.268c0,0.666,0.539,1.205,1.203,1.205h29.371
|
||||
c0.662,0,1.313-0.652,1.313-1.318V1.256C31.943,0.57,31.275,0.16,30.631,0.16z M30.631,12.246H1.26
|
||||
c-0.336,0-0.607-0.275-0.607-0.611V1.367c0-0.336,0.271-0.609,0.607-0.609h29.371c0.352,0,0.717,0.186,0.717,0.498v10.266
|
||||
C31.348,11.859,30.965,12.246,30.631,12.246z"/>
|
||||
<g>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="20.7305" y1="9.5322" x2="11.461" y2="3.4704">
|
||||
<stop offset="0" style="stop-color:#565759"/>
|
||||
<stop offset="0.5" style="stop-color:#666669"/>
|
||||
<stop offset="1" style="stop-color:#77787B"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_3_)" d="M16,2.01c-2.441,0-4.429,1.987-4.429,4.429s1.987,4.429,4.429,4.429s4.429-1.987,4.429-4.429
|
||||
S18.441,2.01,16,2.01z M16,9.956c-1.939,0-3.517-1.578-3.517-3.518S14.061,2.921,16,2.921c1.94,0,3.519,1.578,3.519,3.518
|
||||
S17.94,9.956,16,9.956z"/>
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="20.0166" y1="8.9063" x2="12.1641" y2="3.7711">
|
||||
<stop offset="0" style="stop-color:#565759"/>
|
||||
<stop offset="0.5" style="stop-color:#666669"/>
|
||||
<stop offset="1" style="stop-color:#77787B"/>
|
||||
</linearGradient>
|
||||
<polygon fill="url(#SVGID_4_)" points="16.466,3.524 15.631,3.524 15.631,6.4 14.223,8.048 14.856,8.591 16.466,6.713 "/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
56
static/images/static/images/rank_icons/skillgroup_none.svg
Normal file
56
static/images/static/images/rank_icons/skillgroup_none.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="32px" height="13px" viewBox="0 0 32 13" enable-background="new 0 0 32 13" xml:space="preserve">
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="2.4365" y1="-0.043" x2="34.4364" y2="15.04">
|
||||
<stop offset="0" style="stop-color:#414042"/>
|
||||
<stop offset="1" style="stop-color:#29292A"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_1_)" d="M30.474,0.281h-3.842h-1.055h-1.574h-3.68h-0.68h-8.289H5.017H2.491H1.415
|
||||
c-0.118,0-0.229,0.028-0.333,0.067C1.059,0.357,1.036,0.365,1.014,0.376C0.915,0.422,0.825,0.48,0.749,0.555
|
||||
c-0.012,0.012-0.02,0.024-0.03,0.036C0.682,0.63,0.644,0.668,0.614,0.713C0.593,0.746,0.581,0.785,0.563,0.82
|
||||
C0.55,0.849,0.535,0.875,0.524,0.905c-0.04,0.106-0.068,0.218-0.068,0.339v0.213v0.127v2.094v1.127v1.307v5.338
|
||||
c0,0.125,0.027,0.24,0.07,0.35c0.015,0.037,0.039,0.068,0.058,0.104c0.027,0.05,0.048,0.104,0.083,0.148
|
||||
c0.01,0.013,0.026,0.018,0.037,0.029c0.062,0.07,0.136,0.123,0.215,0.173c0.033,0.021,0.061,0.049,0.097,0.065
|
||||
c0.122,0.057,0.256,0.094,0.399,0.094h1.297h3.969h2.136l0.075,0.053c-0.01-0.019-0.021-0.034-0.029-0.053h5.553h4.799h1.58h0.305
|
||||
h3.223h0.787h0.979h2.426h0.461h1.5c0.053,0,0.105-0.006,0.156-0.018c0.042-0.008,0.079-0.034,0.12-0.049
|
||||
c0.061-0.02,0.117-0.038,0.174-0.069c0.065-0.035,0.121-0.08,0.18-0.128c0.047-0.039,0.093-0.072,0.135-0.118
|
||||
c0.013-0.014,0.031-0.023,0.044-0.038c0.043-0.052,0.069-0.112,0.103-0.169c0.024-0.04,0.054-0.076,0.072-0.118
|
||||
c0.053-0.119,0.087-0.242,0.087-0.367V9.729V8.857V8.324V4.387V2.875V2.502V1.133C31.544,0.602,31.003,0.281,30.474,0.281z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="27.7578" y1="13.084" x2="6.7578" y2="1.334">
|
||||
<stop offset="0" style="stop-color:#565759"/>
|
||||
<stop offset="0.5" style="stop-color:#666669"/>
|
||||
<stop offset="1" style="stop-color:#77787B"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_2_)" d="M30.631,0.16H1.26c-0.664,0-1.203,0.543-1.203,1.207v10.268c0,0.666,0.539,1.205,1.203,1.205h29.371
|
||||
c0.662,0,1.313-0.652,1.313-1.318V1.256C31.943,0.57,31.275,0.16,30.631,0.16z M30.631,12.246H1.26
|
||||
c-0.336,0-0.607-0.275-0.607-0.611V1.367c0-0.336,0.271-0.609,0.607-0.609h29.371c0.352,0,0.717,0.186,0.717,0.498v10.266
|
||||
C31.348,11.859,30.965,12.246,30.631,12.246z"/>
|
||||
<g>
|
||||
<path d="M16.673,8.211h-1.367V7.406c0-0.288,0.047-0.504,0.143-0.647c0.098-0.145,0.289-0.292,0.576-0.444
|
||||
c0.064-0.032,0.201-0.102,0.408-0.21c0.209-0.108,0.369-0.19,0.48-0.246c0.184-0.104,0.275-0.231,0.275-0.384V4.311
|
||||
c0-0.088-0.031-0.162-0.096-0.222c-0.064-0.061-0.141-0.091-0.229-0.091H15.7c-0.088,0-0.164,0.03-0.227,0.091
|
||||
c-0.064,0.06-0.096,0.134-0.096,0.222v0.696h-1.381V3.998c0-0.352,0.121-0.649,0.365-0.894s0.539-0.366,0.883-0.366h2.053
|
||||
c0.352,0,0.65,0.122,0.898,0.366s0.373,0.542,0.373,0.894v1.86c0,0.2-0.041,0.368-0.121,0.504c-0.08,0.137-0.16,0.232-0.24,0.288
|
||||
c-0.078,0.056-0.215,0.141-0.406,0.252c-0.064,0.04-0.16,0.095-0.289,0.162c-0.127,0.068-0.24,0.13-0.336,0.186
|
||||
c-0.096,0.057-0.172,0.101-0.229,0.133c-0.184,0.111-0.275,0.235-0.275,0.372V8.211z M16.806,10.73h-1.561V9.087h1.561V10.73z"/>
|
||||
</g>
|
||||
<g>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="18.7041" y1="8.6094" x2="12.9541" y2="3.2764">
|
||||
<stop offset="0" style="stop-color:#565759"/>
|
||||
<stop offset="0.5" style="stop-color:#666669"/>
|
||||
<stop offset="1" style="stop-color:#77787B"/>
|
||||
</linearGradient>
|
||||
<path fill="url(#SVGID_3_)" d="M16.36,7.773h-1.367V6.969c0-0.288,0.047-0.504,0.143-0.647c0.098-0.145,0.289-0.292,0.576-0.444
|
||||
c0.064-0.032,0.201-0.102,0.408-0.21c0.209-0.108,0.369-0.19,0.48-0.246c0.184-0.104,0.275-0.231,0.275-0.384V3.873
|
||||
c0-0.088-0.031-0.162-0.096-0.222c-0.064-0.061-0.141-0.091-0.229-0.091h-1.164c-0.088,0-0.164,0.03-0.227,0.091
|
||||
c-0.064,0.06-0.096,0.134-0.096,0.222v0.696h-1.381V3.561c0-0.352,0.121-0.649,0.365-0.894s0.539-0.366,0.883-0.366h2.053
|
||||
c0.352,0,0.65,0.122,0.898,0.366s0.373,0.542,0.373,0.894v1.86c0,0.2-0.041,0.368-0.121,0.504c-0.08,0.137-0.16,0.232-0.24,0.288
|
||||
c-0.078,0.056-0.215,0.141-0.406,0.252c-0.064,0.04-0.16,0.095-0.289,0.162c-0.127,0.068-0.24,0.13-0.336,0.186
|
||||
c-0.096,0.057-0.172,0.101-0.229,0.133c-0.184,0.111-0.275,0.235-0.275,0.372V7.773z M16.493,10.293h-1.561V8.649h1.561V10.293z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
Reference in New Issue
Block a user