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
+28 -3
View File
@@ -85,7 +85,11 @@ h1, h2, .font-display {
.regress-field-green { filter: drop-shadow(0 0 6px #39ff14); }
/* 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 {
width: 100%;
height: 100%;
@@ -93,11 +97,32 @@ h1, h2, .font-display {
display: flex;
align-items: 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;
font-family: 'Share Tech Mono', monospace;
font-weight: bold;
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);
}
.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);
}
+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>