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:
2026-08-05 17:50:52 +00:00
parent ab5fe5cb96
commit 6b8f5b1945
2 changed files with 48 additions and 5 deletions
+20 -2
View File
@@ -5,6 +5,7 @@ import 'leaflet.markercluster';
import { useAuthStore } from '../stores/auth';
import { useGameStore } from '../stores/game';
import type { Place } from '../stores/game';
import type { Team } from '../stores/auth';
const auth = useAuthStore();
const game = useGameStore();
@@ -19,6 +20,11 @@ let map: L.Map | null = null;
let markersLayer: L.MarkerClusterGroup | null = null;
let linksLayer: 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 linkSourceId = ref<number | null>(null);
@@ -68,6 +74,7 @@ function redraw() {
const marker = L.marker([place.lat, place.lon], { icon: portalIcon(place, isSource) });
marker.on('click', () => onMarkerClick(place));
marker.bindTooltip(place.name || `Place ${place.id}`, { direction: 'top' });
markerTeam.set(marker, place.claim_team);
return marker;
});
markersLayer.addLayers(markers);
@@ -184,8 +191,19 @@ onMounted(async () => {
iconCreateFunction: (cluster) => {
const count = cluster.getChildCount();
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({
className: 'regress-cluster',
className: `regress-cluster regress-cluster--${modifier}`,
html: `<div style="width:${size}px;height:${size}px;">${count}</div>`,
iconSize: [size, size],
});
@@ -272,7 +290,7 @@ watch(linkSourceId, redraw);
class="font-bold uppercase"
: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>
</p>