Cluster color reflects claimed contents; drop redundant team word from status
- iconCreateFunction now inspects each cluster's actual child markers (tracked via a WeakMap<L.Marker, Team> since markercluster only hands back raw marker instances, not our Place data) and colors the cluster orange/green if every claim inside belongs to one team, a split orange/green 'contested' look if both teams have claims inside, cyan neutral only when nothing in it is claimed. Previously every cluster looked identical regardless of contents. - Status panel: 'held since block XXXXXX' — dropped 'in orange'/'in green', the color already carries that (was redundant).
This commit is contained in:
+28
-3
@@ -85,7 +85,11 @@ h1, h2, .font-display {
|
|||||||
.regress-field-green { filter: drop-shadow(0 0 6px #39ff14); }
|
.regress-field-green { filter: drop-shadow(0 0 6px #39ff14); }
|
||||||
|
|
||||||
/* Reskin leaflet.markercluster's default light-green bubble look to match
|
/* Reskin leaflet.markercluster's default light-green bubble look to match
|
||||||
the HUD theme — overrides MarkerCluster.Default.css. */
|
the HUD theme — overrides MarkerCluster.Default.css. Color reflects what's
|
||||||
|
actually inside: neutral cyan if nothing in the cluster is claimed, team
|
||||||
|
color if it's only one team, a split orange/green "contested" look if
|
||||||
|
both teams have a claim inside — otherwise a cluster full of claimed
|
||||||
|
nodes looks identical to an empty one, which was the whole complaint. */
|
||||||
.regress-cluster div {
|
.regress-cluster div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -93,11 +97,32 @@ h1, h2, .font-display {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background: radial-gradient(circle at 35% 35%, rgba(0, 255, 242, 0.35), rgba(0, 255, 242, 0.12) 70%);
|
|
||||||
border: 2px solid #00fff2;
|
|
||||||
color: #d8fbff;
|
color: #d8fbff;
|
||||||
font-family: 'Share Tech Mono', monospace;
|
font-family: 'Share Tech Mono', monospace;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.regress-cluster--neutral div {
|
||||||
|
background: radial-gradient(circle at 35% 35%, rgba(0, 255, 242, 0.35), rgba(0, 255, 242, 0.12) 70%);
|
||||||
|
border: 2px solid #00fff2;
|
||||||
box-shadow: 0 0 10px rgba(0, 255, 242, 0.5);
|
box-shadow: 0 0 10px rgba(0, 255, 242, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.regress-cluster--orange div {
|
||||||
|
background: radial-gradient(circle at 35% 35%, rgba(255, 95, 31, 0.45), rgba(255, 95, 31, 0.15) 70%);
|
||||||
|
border: 2px solid #ff5f1f;
|
||||||
|
box-shadow: 0 0 12px rgba(255, 95, 31, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.regress-cluster--green div {
|
||||||
|
background: radial-gradient(circle at 35% 35%, rgba(57, 255, 20, 0.45), rgba(57, 255, 20, 0.15) 70%);
|
||||||
|
border: 2px solid #39ff14;
|
||||||
|
box-shadow: 0 0 12px rgba(57, 255, 20, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.regress-cluster--contested div {
|
||||||
|
background: linear-gradient(135deg, rgba(255, 95, 31, 0.45) 50%, rgba(57, 255, 20, 0.45) 50%);
|
||||||
|
border: 2px solid #ffffff;
|
||||||
|
box-shadow: 0 0 12px rgba(255, 0, 229, 0.55);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'leaflet.markercluster';
|
|||||||
import { useAuthStore } from '../stores/auth';
|
import { useAuthStore } from '../stores/auth';
|
||||||
import { useGameStore } from '../stores/game';
|
import { useGameStore } from '../stores/game';
|
||||||
import type { Place } from '../stores/game';
|
import type { Place } from '../stores/game';
|
||||||
|
import type { Team } from '../stores/auth';
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const game = useGameStore();
|
const game = useGameStore();
|
||||||
@@ -19,6 +20,11 @@ let map: L.Map | null = null;
|
|||||||
let markersLayer: L.MarkerClusterGroup | null = null;
|
let markersLayer: L.MarkerClusterGroup | null = null;
|
||||||
let linksLayer: L.LayerGroup | null = null;
|
let linksLayer: L.LayerGroup | null = null;
|
||||||
let fieldsLayer: L.LayerGroup | null = null;
|
let fieldsLayer: L.LayerGroup | null = null;
|
||||||
|
// Cluster icons need to know if anything inside is claimed (and by whom) —
|
||||||
|
// leaflet.markercluster only gives back the child L.Marker instances, not
|
||||||
|
// our Place data, so track the mapping on the side (WeakMap so old markers
|
||||||
|
// from a previous redraw() just fall out rather than needing manual cleanup).
|
||||||
|
const markerTeam = new WeakMap<L.Marker, Team | null>();
|
||||||
|
|
||||||
const selectedPlaceId = ref<number | null>(null);
|
const selectedPlaceId = ref<number | null>(null);
|
||||||
const linkSourceId = ref<number | null>(null);
|
const linkSourceId = ref<number | null>(null);
|
||||||
@@ -68,6 +74,7 @@ function redraw() {
|
|||||||
const marker = L.marker([place.lat, place.lon], { icon: portalIcon(place, isSource) });
|
const marker = L.marker([place.lat, place.lon], { icon: portalIcon(place, isSource) });
|
||||||
marker.on('click', () => onMarkerClick(place));
|
marker.on('click', () => onMarkerClick(place));
|
||||||
marker.bindTooltip(place.name || `Place ${place.id}`, { direction: 'top' });
|
marker.bindTooltip(place.name || `Place ${place.id}`, { direction: 'top' });
|
||||||
|
markerTeam.set(marker, place.claim_team);
|
||||||
return marker;
|
return marker;
|
||||||
});
|
});
|
||||||
markersLayer.addLayers(markers);
|
markersLayer.addLayers(markers);
|
||||||
@@ -184,8 +191,19 @@ onMounted(async () => {
|
|||||||
iconCreateFunction: (cluster) => {
|
iconCreateFunction: (cluster) => {
|
||||||
const count = cluster.getChildCount();
|
const count = cluster.getChildCount();
|
||||||
const size = count < 100 ? 34 : count < 1000 ? 42 : 50;
|
const size = count < 100 ? 34 : count < 1000 ? 42 : 50;
|
||||||
|
|
||||||
|
let hasOrange = false;
|
||||||
|
let hasGreen = false;
|
||||||
|
for (const marker of cluster.getAllChildMarkers()) {
|
||||||
|
const team = markerTeam.get(marker as L.Marker);
|
||||||
|
if (team === 'orange') hasOrange = true;
|
||||||
|
else if (team === 'green') hasGreen = true;
|
||||||
|
if (hasOrange && hasGreen) break;
|
||||||
|
}
|
||||||
|
const modifier = hasOrange && hasGreen ? 'contested' : hasOrange ? 'orange' : hasGreen ? 'green' : 'neutral';
|
||||||
|
|
||||||
return L.divIcon({
|
return L.divIcon({
|
||||||
className: 'regress-cluster',
|
className: `regress-cluster regress-cluster--${modifier}`,
|
||||||
html: `<div style="width:${size}px;height:${size}px;">${count}</div>`,
|
html: `<div style="width:${size}px;height:${size}px;">${count}</div>`,
|
||||||
iconSize: [size, size],
|
iconSize: [size, size],
|
||||||
});
|
});
|
||||||
@@ -272,7 +290,7 @@ watch(linkSourceId, redraw);
|
|||||||
class="font-bold uppercase"
|
class="font-bold uppercase"
|
||||||
:class="selectedPlace.claim_team === 'orange' ? 'text-orange-neon text-glow-orange' : 'text-green-neon text-glow-green'"
|
:class="selectedPlace.claim_team === 'orange' ? 'text-orange-neon text-glow-orange' : 'text-green-neon text-glow-green'"
|
||||||
>
|
>
|
||||||
held since block {{ selectedPlace.claimed_at_block ?? '?' }} in {{ selectedPlace.claim_team }}
|
held since block {{ selectedPlace.claimed_at_block ?? '?' }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user