<!-- APP CREATOR - S GIBBS -->
<!-- MCARS -  MAPPING CONTROL & AWARENESS RESPONSE-->
<!-- RED ALERT - BORG ATTACK GAME -->
<!-- 16/01/2026, STABLE VERSION V1 -->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Borg Tactical Rebuild v2</title>

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>

<style>
html,body{
  margin:0;
  height:100%;
  overflow:hidden;
  font-family:Impact;
  background:#000;
}

/* BACKGROUND */
#bg{
  position:fixed;
  inset:0;
  background:url("C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/Borg Background 2.gif");
  background-size:cover;
  z-index:0;
}

/* MAP */
#map{
  position:absolute;
  top:40px;
  left:0px;
  right:440px;
  bottom:200px;
  z-index:2;
}

/* PANEL */
#panel{
  position:absolute;
  top:210px;
  right:0;
  width:180px;
  bottom:0;
  z-index:3;
  padding:10px;
  box-sizing:border-box;
  color:#00d7ff;
  display:flex;
  flex-direction:column;
  gap:15px;
}

.btn{
  padding:10px;
  border-radius:999px;
  border:none;
  cursor:pointer;
  background:#aa00ff;
  font-family:Impact;
}
.btn:hover{ background:#bfeaff; }

.hud{ font-size:18px; }

#gameOver{
  position:fixed;
  inset:0;
  display:none;
  align-items:center;
  justify-content:center;
  font-size:70px;
  color:red;
  background:rgba(0,0,0,0.8);
  z-index:20000;
}

/* START SCREEN */
#startScreen{
  position:fixed;
  inset:0;
  background:#000;
  display:flex;
  align-items:center;
  justify-content:center;
  z-index:30000;
}

.pill{
  padding:18px 60px;
  border-radius:999px;
  border:none;
  cursor:pointer;
  font-size:20px;
  background:red;
}

/* TITLE */
#titleIntro{
  position:fixed;
  inset:0;
  background:#000;
  display:none;
  align-items:center;
  justify-content:center;
  z-index:25000;
}

#titleIntro img{
  width:100%;
  height:100%;
  object-fit:contain;
}

/* BORG */
.borgWrap{
  width:40px;
  height:40px;
  filter:drop-shadow(0 0 10px cyan);
  animation:pulse 1s infinite;
}

@keyframes pulse{
  0%{transform:scale(1);}
  50%{transform:scale(1.1);}
  100%{transform:scale(1);}
}

.leaflet-container {
  background: transparent !important;
}

#map {
  background: transparent !important;
  border: none !important;
  outline: none !important;
}

.leaflet-image-layer {
  image-rendering: pixelated;
}

.no-border {
  outline: none !important;
  border: none !important;
}

/* RADAR BEAM (VISIBLE SWEEP) */
#radarBeam{
  position:absolute;
  top:0;
  left:-30%;
  width:30%;
  height:100%;
  z-index:9999;
  pointer-events:none;
  background:linear-gradient(
    to right,
    rgba(0,255,255,0) 0%,
    rgba(0,255,255,0.25) 50%,
    rgba(0,255,255,0) 100%
  );
}

#void{
  position:absolute;
  top:40px;
  left:0px;
  right:440px;
  bottom:200px;

  z-index:1;

  background:url("C:/Users/steve/Pictures/LCARS Wall Display/starfield background.png");
  background-size:cover;
  background-position:center;
  background-repeat:no-repeat;
}

.btn.exit {
  margin-top: 60px; /* adjust as needed */
}
</style>
</head>

<body>

<div id="bg"></div>
<div id="void"></div>

<div id="startScreen">
  <button class="pill" onclick="startIntro()">TAP TO START</button>
</div>

<div id="titleIntro">
  <img src="C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/Red Alert Title GIF.gif">
</div>

<div id="map"></div>

<div id="panel">
  <div class="hud">LEVEL: <span id="level">1</span></div>
  <div class="hud">TIME: <span id="time">60</span></div>
  <div class="hud">HITS: <span id="hits">0</span></div>

<button class="btn" onclick="playBtnSound(); startGame()">START</button>
<button class="btn" onclick="playBtnSound(); toggleTheme()">LIGHT/DARK</button>
<button class="btn" onclick="playBtnSound(); toggleRadar()">RADAR</button>
<button class="btn" onclick="playBtnSound(); recenterMap()">RE-CENTER</button>
<button class="btn exit" onclick="playBtnSound(); window.close()">EXIT</button>
</div>

<div id="gameOver">GAME OVER</div>

<script>

/* CONFIG */
const CFG = {
  darkMap:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/ST Night Map.png",
  lightMap:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/ST Day Map.png",
  borgImg:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/Borg cube.png",
explosionGif: "C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/K Explosion.gif",
sphereImg: "C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/Borg Sphere.png",
  explosion:"C:/Users/steve/Pictures/LCARS Wall Display/Game Klingon/Explosion.wav",
  introSound:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/voy red alert.wav",
  gameOverSound:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/Game Over Tune.wav",
  roundTime:60,
btnSound:"C:/Users/steve/Pictures/LCARS Wall Display/My Maps Rescaled/K Button Press Theme.wav",
};
function playBtnSound(){
  new Audio(CFG.btnSound).play();
}

/* LEVELS */
const LEVELS = {
  1:{spawn:1200,speed:4.5,target:20},
  2:{spawn:1100,speed:5.5,target:22},
  3:{spawn:1000,speed:6.5,target:24},
  4:{spawn:900,speed:7.1,target:26},
  5:{spawn:800,speed:7.7,target:28},
  6:{spawn:700,speed:8.2,target:30},
  7:{spawn:600,speed:8.6,target:32},
  8:{spawn:500,speed:9.0,target:35},
  9:{spawn:450,speed:9.5,target:38},
  10:{spawn:400,speed:9.9,target:42}
};

/* STATE */
let map, layer;
let borgs=[];
let level=1;
let hits=0;
let timeLeft=60;
let running=false;

let timer, spawnLoop, moveLoop, sphereLoop;

function init(){
  map = L.map("map", {
    crs: L.CRS.Simple,
    zoomControl: false,
    attributionControl: false
  });

  const bounds = [[0,0],[700,1200]];

  layer = L.imageOverlay(CFG.darkMap, bounds).addTo(map);

  map.fitBounds(bounds);

  // 🔧 Force Leaflet to respect CSS layout changes
  setTimeout(() => {
    map.invalidateSize();
  }, 100);
}

/* INTRO */
function startIntro(){
  document.getElementById("startScreen").style.display="none";
  document.getElementById("titleIntro").style.display="flex";
  new Audio(CFG.introSound).play();

  setTimeout(()=>{
    document.getElementById("titleIntro").style.display="none";
  },6000);
}

/* START GAME */
function startGame(){

  reset();

  running=true;
  hits=0;
  timeLeft=60;

  updateHUD();

  timer=setInterval(()=>{
    if(!running) return;

    timeLeft--;
    updateHUD();

    if(timeLeft<=0){
      checkLevel();
    }
  },1000);

  spawnLoop=setInterval(spawnBorg,LEVELS[level].spawn);
  moveLoop=setInterval(moveBorgs,40);
sphereLoop = setInterval(() => {

  if (!running) return;

  if (Math.random() < 0.25) {
    spawnBorgSphere();
  }

}, 4000);
}

/* RESET */
function reset(){
  borgs.forEach(b=>{
    if(map.hasLayer(b.marker)) map.removeLayer(b.marker);
  });
  borgs=[];
  clearInterval(timer);
  clearInterval(spawnLoop);
  clearInterval(moveLoop);
clearInterval(sphereLoop);
}

/* SPAWN */
function spawnBorg(){

  if(!running) return;

  if(borgs.length >= 15){
    endGame(false);
    return;
  }

  let x=Math.random()*1200;
  let y=Math.random()*700;

  let marker=L.marker([y,x],{
    icon:L.divIcon({
      className:"",
      html:`<div class="borgWrap"><img src="${CFG.borgImg}" style="width:100%;height:100%"></div>`,
      iconSize:[40,40],
      iconAnchor:[20,20]
    })
  }).addTo(map);

  borgs.push({
    marker,
    x,y,
    vx:(Math.random()-0.5)*LEVELS[level].speed,
    vy:(Math.random()-0.5)*LEVELS[level].speed
  });

marker.on("click",()=>{

  hits++;
  updateHUD();

  new Audio(CFG.explosion).play();

const pos = marker.getLatLng();

const explosionMarker = L.marker(pos, {
  icon: L.divIcon({
    className: "",
    html: `<img src="${CFG.explosionGif}" style="width:90px;height:90px;transform:translate(-25px,-25px);pointer-events:none;">`
  })
}).addTo(map);

setTimeout(()=>{
  if(map.hasLayer(marker)) map.removeLayer(marker);
  borgs = borgs.filter(b=>b.marker!==marker);
},100);

setTimeout(()=>{
  if(map.hasLayer(explosionMarker)) map.removeLayer(explosionMarker);
},800);

  if(hits >= LEVELS[level].target){
    clearInterval(timer);
    clearInterval(spawnLoop);
    clearInterval(moveLoop);

    running = false;

    document.getElementById("gameOver").innerText = "LEVEL COMPLETE";
    document.getElementById("gameOver").style.display = "flex";

    setTimeout(()=>{
      document.getElementById("gameOver").style.display = "none";

      level++;

      if(!LEVELS[level]){
        endGame(true);
        return;
      }

      startGame();

    },2000);
  }

}); // ← THIS IS CRITICAL (must exist)
}

function spawnBorgSphere() {

  let side = Math.floor(Math.random() * 4);

  let x, y, vx, vy;

  const speed = 2; // FAST — adjust if needed

  /* spawn from random screen edge */
  if (side === 0) { // top
    x = Math.random() * 1200;
    y = 0;
    vx = (Math.random() - 0.5) * 6;
    vy = speed;
  }

  if (side === 1) { // bottom
    x = Math.random() * 1200;
    y = 700;
    vx = (Math.random() - 0.5) * 6;
    vy = -speed;
  }

  if (side === 2) { // left
    x = 0;
    y = Math.random() * 700;
    vx = speed;
    vy = (Math.random() - 0.5) * 6;
  }

  if (side === 3) { // right
    x = 1200;
    y = Math.random() * 700;
    vx = -speed;
    vy = (Math.random() - 0.5) * 6;
  }

  let marker = L.marker([y, x], {
    icon: L.divIcon({
      className: "",
      html: `<img src="${CFG.sphereImg}" style="width:60px;height:60px;pointer-events:none;">`,
      iconSize: [60, 60],
      iconAnchor: [30, 30]
    })
  }).addTo(map);

  let sphere = { marker, x, y, vx, vy };

  borgs.push(sphere);

  /* CLICK BEHAVIOUR */
 marker.on("click", () => {

  hits += 3;
  updateHUD();

  new Audio(CFG.explosion).play();

  marker.setIcon(L.divIcon({
    html: `<img src="${CFG.explosionGif}" style="width:110px;height:110px;">`,
    className: ""
  }));

  setTimeout(() => {
    if (map.hasLayer(marker)) map.removeLayer(marker);
    borgs = borgs.filter(b => b.marker !== marker);
  }, 200);

});

  /* MOVEMENT LOOP */
  let fly = setInterval(() => {

    sphere.x += sphere.vx;
    sphere.y += sphere.vy;

    marker.setLatLng([sphere.y, sphere.x]);

    /* REMOVE IF OFF SCREEN */
    if (
      sphere.x < -200 || sphere.x > 1400 ||
      sphere.y < -200 || sphere.y > 900
    ) {
      clearInterval(fly);
      if (map.hasLayer(marker)) map.removeLayer(marker);
      borgs = borgs.filter(b => b.marker !== marker);
    }

  }, 30);
}

/* MOVE */
function moveBorgs(){
  if(!running) return;

  borgs.forEach(b=>{
    b.x+=b.vx;
    b.y+=b.vy;
    b.marker.setLatLng([b.y,b.x]);
  });
}

/* HUD */
function updateHUD(){
  document.getElementById("hits").innerText=hits;
  document.getElementById("time").innerText=timeLeft;
  document.getElementById("level").innerText=level;
}

/* LEVEL CHECK */
function checkLevel(){

  if(hits >= LEVELS[level].target){

    running = false;

    clearInterval(timer);
    clearInterval(spawnLoop);
    clearInterval(moveLoop);

    document.getElementById("gameOver").innerText = "LEVEL COMPLETE";
    document.getElementById("gameOver").style.display = "flex";

    setTimeout(()=>{

      document.getElementById("gameOver").style.display = "none";

      level++;

      if(!LEVELS[level]){
        endGame(true);
        return;
      }

      startGame();

    },2000);

  }
}

/* GAME OVER */
function endGame(win){

  running=false;

  clearInterval(timer);
  clearInterval(spawnLoop);
  clearInterval(moveLoop);

  document.getElementById("gameOver").innerText =
    win ? "MISSION COMPLETE" : "GAME OVER";

  document.getElementById("gameOver").style.display="flex";

  new Audio(CFG.gameOverSound).play();

  setTimeout(()=>{
    document.getElementById("gameOver").style.display="none";
  },3000);
}

/* THEME */
function toggleTheme(){
  map.removeLayer(layer);

  layer=L.imageOverlay(
    layer._url===CFG.darkMap ? CFG.lightMap : CFG.darkMap,
    [[0,0],[700,1200]]
  ).addTo(map);
}

/* RADAR */
let radarOn=false;
let radarInterval=null;

function toggleRadar(){

  radarOn=!radarOn;

  let beam=document.getElementById("radarBeam");

  if(!radarOn){
    if(beam) beam.remove();
    if(radarInterval) clearInterval(radarInterval);
    return;
  }

  beam=document.createElement("div");
  beam.id="radarBeam";
  document.getElementById("map").appendChild(beam);

  function sweep(){
    beam.style.transition="none";
    beam.style.left="-30%";
    beam.offsetHeight;
    beam.style.transition="left 3s linear";
    beam.style.left="100%";
  }

  sweep();
  radarInterval = setInterval(sweep,3500);
}

function recenterMap(){
  const bounds = [[0,0],[700,1200]];
  map.fitBounds(bounds);
}

init();

</script>

</body>
</html>