:root {
  --bg:#071022; --ui:#0f2b3a; --accent:#4fd1c5; --text:#e6eef8;
  --ui-size:16px;
}
*{box-sizing:border-box}
body{margin:0;font-family:system-ui,Segoe UI,Roboto,Arial;background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased}
#uiTop{position:fixed;left:12px;top:12px;z-index:40;display:flex;gap:8px}
button{padding:8px 10px;border-radius:8px;background:var(--ui);color:var(--text);border:1px solid rgba(255,255,255,0.03)}
#gameArea{display:flex;justify-content:center;align-items:center;height:100vh}
canvas{border-radius:10px;box-shadow:0 10px 30px #0008}
.joystick{position:fixed;width:160px;height:160px;border-radius:50%;background:rgba(255,255,255,0.02);z-index:35;touch-action:none}
#leftStick{left:12px;bottom:12px}
#rightStick{right:12px;bottom:12px}
.modal{position:fixed;left:0;top:0;width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.6);z-index:60}
.hidden{display:none}
.modalContent{background:#071a24;padding:16px;border-radius:12px;width:320px}
.modalContent label{display:block;margin:8px 0;font-size:0.9rem}
.modalActions{display:flex;gap:8px;justify-content:flex-end;margin-top:12px}
.log{position:fixed;right:8px;bottom:8px;max-width:320px;opacity:0.9;background:#0008;color:#fff;padding:8px;border-radius:6px;font-size:12px}
// main.js - Web client for Star Clash
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.23.0/firebase-app.js";
import { getAuth, signInAnonymously } from "https://www.gstatic.com/firebasejs/9.23.0/firebase-auth.js";
import { getFunctions, httpsCallable } from "https://www.gstatic.com/firebasejs/9.23.0/firebase-functions.js";
import { getFirestore, doc, getDoc } from "https://www.gstatic.com/firebasejs/9.23.0/firebase-firestore.js";

/* ====== REPLACE WITH YOUR FIREBASE CONFIG ======
   Get it from Firebase Console -> Project settings -> Web app
*/
const FIREBASE_CONFIG = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "",
  messagingSenderId: "",
  appId: ""
};
/* ============================================== */

const SIGNER_URL = "https://YOUR_SIGNER_HOST/sign-match"; // signer deploy url or leave empty to skip signing

const app = initializeApp(FIREBASE_CONFIG);
const auth = getAuth(app);
const functions = getFunctions(app);
const db = getFirestore(app);

const logEl = document.getElementById('log');
function log(...a){ logEl.textContent = a.map(x => (typeof x==='object' ? JSON.stringify(x,null,2) : String(x))).join(' ') + '\n' + logEl.textContent; }

// UI
const btnLogin = document.getElementById('btnLogin');
const btnLeader = document.getElementById('btnLeaderboard');
const btnSettings = document.getElementById('btnSettings');
const btnReport = document.getElementById('btnReport');
const settingsModal = document.getElementById('settingsModal');
const btnCloseSettings = document.getElementById('closeSettings');
const btnSaveSettings = document.getElementById('saveSettings');

const sSpeed = document.getElementById('settingSpeed');
const sAim = document.getElementById('settingAim');
const sFire = document.getElementById('settingFire');
const sVolume = document.getElementById('settingVolume');
const sContrast = document.getElementById('settingContrast');
const sUISize = document.getElementById('settingUISize');

const SETTINGS_KEY = 'starclash_settings_v1';
let playerSettings = JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}');
if(!playerSettings.speed) playerSettings.speed = 180;
if(!playerSettings.aimSens) playerSettings.aimSens = 1.2;
if(!playerSettings.fireMul) playerSettings.fireMul = 1.0;
if(!playerSettings.volume) playerSettings.volume = 0.8;
if(playerSettings.contrast===undefined) playerSettings.contrast = false;
if(!playerSettings.uiSize) playerSettings.uiSize = 16;

function applySettingsToUI(){
  sSpeed.value = playerSettings.speed;
  sAim.value = playerSettings.aimSens;
  sFire.value = playerSettings.fireMul;
  sVolume.value = playerSettings.volume;
  sContrast.checked = playerSettings.contrast;
  sUISize.value = playerSettings.uiSize;
  document.documentElement.style.setProperty('--ui-size', playerSettings.uiSize + 'px');
  if(playerSettings.contrast) document.documentElement.style.setProperty('--bg','#000'); else document.documentElement.style.setProperty('--bg','#071022');
}
applySettingsToUI();

btnSettings.addEventListener('click', ()=> { applySettingsToUI(); settingsModal.classList.remove('hidden'); });
btnCloseSettings.addEventListener('click', ()=> settingsModal.classList.add('hidden'));
btnSaveSettings.addEventListener('click', ()=> {
  playerSettings.speed = Number(sSpeed.value);
  playerSettings.aimSens = Number(sAim.value);
  playerSettings.fireMul = Number(sFire.value);
  playerSettings.volume = Number(sVolume.value);
  playerSettings.contrast = sContrast.checked;
  playerSettings.uiSize = Number(sUISize.value);
  localStorage.setItem(SETTINGS_KEY, JSON.stringify(playerSettings));
  applySettingsToUI();
  settingsModal.classList.add('hidden');
  log('Settings saved');
});

// Auth
btnLogin.addEventListener('click', async ()=> {
  try {
    const cred = await signInAnonymously(auth);
    log('Signed in anon', cred.user.uid);
  } catch(e) {
    log('Auth err', e.message || e);
  }
});

btnLeader.addEventListener('click', async ()=> {
  try {
    const fn = httpsCallable(functions, 'getLeaderboard');
    const r = await fn({ limit: 10 });
    log('Leaderboard', r.data.leaderboard);
  } catch(e) {
    log('getLeaderboard err', e.message || e);
  }
});

// simple canvas game
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let W = canvas.width, H = canvas.height;

const player = { x: W/2 - 120, y: H/2, vx:0, vy:0, radius:20, color:'#4fd1c5', hp:1000, fireCooldown:0, superCharge:0 };
const enemy = { x: W/2 + 120, y: H/2, radius:20, color:'#ff6b6b', hp:800 };

const bullets = [];
let aimDirection = {x:1,y:0};

// load gameSettings (global) from Firestore
async function loadGlobalSettings() {
  try {
    const docRef = doc(db, 'gameSettings', 'current');
    const snap = await getDoc(docRef);
    if (snap.exists()) {
      const data = snap.data();
      log('Global gameSettings loaded');
      // apply as needed (not enforced)
    }
  } catch(e) { console.warn('load global settings fail', e); }
}
loadGlobalSettings();

// joystick simple handlers
importJoy('leftStick', true, (dx,dy) => {
  player.vx = dx * playerSettings.speed;
  player.vy = dy * playerSettings.speed;
});
importJoy('rightStick', false, (dx,dy) => {
  if(Math.abs(dx)+Math.abs(dy) > 0.05) aimDirection = {x:dx, y:dy};
});

function importJoy(domId, isMove, cb) {
  const el = document.getElementById(domId);
  let active = false, startX=0, startY=0;
  el.addEventListener('pointerdown', (e)=> { active = true; el.setPointerCapture(e.pointerId); startX = e.clientX; startY = e.clientY; });
  window.addEventListener('pointermove', (e)=> {
    if(!active) return;
    const dx = (e.clientX - startX) / 60;
    const dy = (e.clientY - startY) / 60;
    const len = Math.hypot(dx,dy) || 1;
    cb(clamp(dx/len, -1,1), clamp(dy/len, -1,1));
  });
  window.addEventListener('pointerup', (e)=> { if(!active) return; active=false; cb(0,0); });
}

function clamp(v,a,b){ return Math.max(a,Math.min(b,v)); }

// firing
function tryFire(dt) {
  player.fireCooldown -= dt;
  const baseCd = 1.2 * playerSettings.fireMul;
  if(player.fireCooldown <= 0 && (Math.abs(aimDirection.x)+Math.abs(aimDirection.y)) > 0.05) {
    player.fireCooldown = baseCd;
    const speed = 420;
    bullets.push({
      x: player.x + aimDirection.x * (player.radius + 8),
      y: player.y + aimDirection.y * (player.radius + 8),
      vx: aimDirection.x * speed,
      vy: aimDirection.y * speed,
      dmg: 100
    });
    player.superCharge = clamp(player.superCharge + 8, 0, 100);
  }
}

// loop
let last = performance.now();
function loop(t) {
  const dt = (t - last) / 1000; last = t;
  player.x += player.vx * dt; player.y += player.vy * dt;
  player.x = clamp(player.x, 32, W-32); player.y = clamp(player.y, 32, H-32);
  for(let i=bullets.length-1;i>=0;i--){
    const b = bullets[i];
    b.x += b.vx * dt; b.y += b.vy * dt;
    const dx = b.x - enemy.x, dy = b.y - enemy.y;
    if(dx*dx + dy*dy < (enemy.radius+6)*(enemy.radius+6)) { enemy.hp -= b.dmg; bullets.splice(i,1); }
    else if(b.x < -50 || b.x > W+50 || b.y < -50 || b.y > H+50) bullets.splice(i,1);
  }
  tryFire(dt);
  render();
  requestAnimationFrame(loop);
}
requestAnimationFrame(loop);

function render(){
  ctx.clearRect(0,0,W,H);
  ctx.fillStyle = '#041019'; ctx.fillRect(0,0,W,H);
  ctx.beginPath(); ctx.fillStyle = enemy.color; ctx.arc(enemy.x,enemy.y,enemy.radius,0,Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.fillStyle = player.color; ctx.arc(player.x,player.y,player.radius,0,Math.PI*2); ctx.fill();
  ctx.beginPath(); ctx.fillStyle = '#fff7'; ctx.arc(player.x + aimDirection.x*40, player.y + aimDirection.y*40, 6,0,Math.PI*2); ctx.fill();
  bullets.forEach(b => { ctx.beginPath(); ctx.fillStyle = '#ffd39b'; ctx.arc(b.x,b.y,6,0,Math.PI*2); ctx.fill(); });
}

// debug: report match (signer + Cloud Function)
btnReport.addEventListener('click', async ()=> {
  if(!auth.currentUser) { log('Sign in first'); return; }
  const matchId = 'match_' + crypto.randomUUID();
  const participants = [
    { uid: auth.currentUser.uid, charId: 'Dash', placement: 1 },
    { uid: 'otherUid1', charId: 'Neon', placement: 2 },
    { uid: 'otherUid2', charId: 'Rookie', placement: 3 }
  ];
  try {
    if(!SIGNER_URL) throw new Error('SIGNER_URL not configured');
    const resp = await fetch(SIGNER_URL, { method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({ matchId, mode:'3v3', participants })});
    const sign = await resp.json();
    const fn = httpsCallable(functions, 'reportMatchResult');
    const r = await fn({ matchId, mode:'3v3', participants, signedPayloadString: sign.signedPayloadString, signerSig: sign.signature });
    log('reportResult', r.data);
  } catch(e) {
    log('reportErr', e.message || e);
  }
});
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class DualJoystickController : MonoBehaviour
{
    public RectTransform leftBg, leftHandle;
    public RectTransform rightBg, rightHandle;
    public float maxRadius = 70f;
    public float moveSpeed = 180f;
    public float aimSensitivity = 1.2f;
    public GameObject bulletPrefab;
    public Transform bulletParent;
    public float baseFireCooldown = 1.2f;

    Vector2 leftInput = Vector2.zero, rightInput = Vector2.zero;
    float fireCooldown = 0f;

    void Start()
    {
        moveSpeed = PlayerPrefs.GetFloat("setting_speed", moveSpeed);
        aimSensitivity = PlayerPrefs.GetFloat("setting_aim", aimSensitivity);
        baseFireCooldown = PlayerPrefs.GetFloat("setting_fire", baseFireCooldown);
    }

    void Update()
    {
        HandleMouseJoystick(leftBg, leftHandle, ref leftInput);
        HandleMouseJoystick(rightBg, rightHandle, ref rightInput);

        Vector3 delta = new Vector3(leftInput.x, 0, leftInput.y) * moveSpeed * Time.deltaTime;
        transform.position += delta;

        fireCooldown -= Time.deltaTime;
        if(rightInput.magnitude > 0.2f && fireCooldown <= 0f) {
            FireBullet(rightInput.normalized);
            fireCooldown = baseFireCooldown;
        }
    }

    void HandleMouseJoystick(RectTransform bg, RectTransform handle, ref Vector2 output) {
        if(Input.GetMouseButton(0)) {
            Vector2 mousePos;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(bg.parent as RectTransform, Input.mousePosition, null, out mousePos);
            Vector2 local = mousePos - (Vector2)bg.anchoredPosition;
            Vector2 clamped = Vector2.ClampMagnitude(local, maxRadius);
            handle.anchoredPosition = clamped;
            output = clamped / maxRadius;
        } else {
            handle.anchoredPosition = Vector2.zero;
            output = Vector2.zero;
        }
    }

    void FireBullet(Vector2 dir){
        if(bulletPrefab != null){
            var b = Instantiate(bulletPrefab, transform.position + new Vector3(dir.x,0,dir.y) * 1.2f, Quaternion.identity, bulletParent);
            var rb = b.GetComponent();
            if(rb) rb.velocity = new Vector3(dir.x, 0, dir.y) * 12f;
        }
    }

    public void UpdateSettings(float speed, float aim, float fire) {
        moveSpeed = speed; aimSensitivity = aim; baseFireCooldown = fire;
        PlayerPrefs.SetFloat("setting_speed", speed);
        PlayerPrefs.SetFloat("setting_aim", aim);
        PlayerPrefs.SetFloat("setting_fire", fire);
    }
}
using System.Collections.Generic;
using UnityEngine;
using Firebase;
using Firebase.Auth;
using Firebase.Functions;
using Firebase.Extensions;

public class UnityFirebaseTrophies : MonoBehaviour
{
    FirebaseAuth auth;
    FirebaseUser user;
    FirebaseFunctions functions;

    void Start()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
            var status = task.Result;
            if (status == DependencyStatus.Available) {
                auth = FirebaseAuth.DefaultInstance;
                functions = FirebaseFunctions.DefaultInstance;
                SignInAnonymously();
            } else {
                Debug.LogError("Firebase dependencies missing");
            }
        });
    }

    void SignInAnonymously()
    {
        auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(t => {
            if (t.IsFaulted) { Debug.LogError("SignIn failed: " + t.Exception); return; }
            user = t.Result;
            Debug.Log("Signed in as: " + user.UserId);
        });
    }

    public void SendMatchResult(string matchId, List> participants, string signedPayloadString = null, string signature = null)
    {
        if (user == null) { Debug.LogError("Not signed in"); return; }
        var payload = new Dictionary {
            { "matchId", matchId }, { "mode", "3v3" }, { "participants", participants }
        };
        if (!string.IsNullOrEmpty(signedPayloadString)) payload["signedPayloadString"] = signedPayloadString;
        if (!string.IsNullOrEmpty(signature)) payload["signerSig"] = signature;

        var callable = functions.GetHttpsCallable("reportMatchResult");
        callable.CallAsync(payload).ContinueWithOnMainThread(task => {
            if (task.IsFaulted) { Debug.LogError("Function error: " + task.Exception); return; }
            var result = task.Result.Data;
            Debug.Log("reportMatchResult response: " + MiniJSON.Json.Serialize(result));
        });
    }

    [ContextMenu("FakeMatchUnity")]
    void FakeMatch() {
        if (user == null) { Debug.Log("Wait for login"); return; }
        var participants = new List> {
            new Dictionary { {"uid", user.UserId}, {"charId","Dash"}, {"placement", 1} },
            new Dictionary { {"uid", "otherUid1"}, {"charId","Neon"}, {"placement", 2} },
            new Dictionary { {"uid", "otherUid2"}, {"charId","Rookie"}, {"placement", 3} }
        };
        SendMatchResult("match_" + System.Guid.NewGuid().ToString(), participants);
    }
}
[
{"id":1,"name":"Astra","rarity":"Legendary","role":"Mage","hp":1000,"speed":4,"attack_damage":110,"attack_range":7,"attack_reload":1.2,"super_name":"Pluie astrale","super_effect":"Zone d'astéroïdes infligeant 350 dmg sur 3s","description":"Tirs perforants d'énergie cosmique."},
{"id":2,"name":"Neon","rarity":"Legendary","role":"Assassin","hp":700,"speed":7,"attack_damage":140,"attack_range":4,"attack_reload":0.9,"super_name":"Transcendance","super_effect":"Téléportation instantanée + hit critique 1.8x","description":"Rapide, inflige gros dégâts en embuscade."},
{"id":3,"name":"Titanox","rarity":"Legendary","role":"Tank","hp":1200,"speed":2,"attack_damage":90,"attack_range":2.2,"attack_reload":1.4,"super_name":"Armure d'acier","super_effect":"Réduit les dégâts reçus de 50% pendant 6s","description":"Très résistant, ralenti mais inflige fort AoE."},
{"id":4,"name":"Spectra","rarity":"Legendary","role":"Support","hp":900,"speed":6,"attack_damage":60,"attack_range":6,"attack_reload":1.1,"super_name":"Voile d'ombre","super_effect":"Rend invisibles et booste vitesse alliés 4s","description":"Soutien mobile et furtif."},
{"id":5,"name":"Chaos","rarity":"Legendary","role":"Contrôle","hp":950,"speed":4,"attack_damage":95,"attack_range":5,"attack_reload":1.3,"super_name":"Singularité","super_effect":"Crée un mini-trou noir qui attire ennemis 3s","description":"Perturbateur de zone."},
{"id":6,"name":"Solar","rarity":"Legendary","role":"Distance","hp":750,"speed":4,"attack_damage":125,"attack_range":9,"attack_reload":1.6,"super_name":"Orbital","super_effect":"Laser orbital traversant la map 600 dmg","description":"Sniper longue portée continu."},
{"id":7,"name":"Blitz","rarity":"Legendary","role":"Rapide","hp":850,"speed":8,"attack_damage":80,"attack_range":3.5,"attack_reload":0.7,"super_name":"Furie électrique","super_effect":"Triple dash avec étourdissement bref","description":"Énorme mobilité et chaos en mêlée."},
{"id":8,"name":"Orion","rarity":"Legendary","role":"Sniper","hp":700,"speed":4,"attack_damage":200,"attack_range":11,"attack_reload":2.6,"super_name":"Tirs concentrés","super_effect":"3 tirs très puissants (x2.5 dmg) en rafale","description":"Un coup peut changer le match."},
{"id":9,"name":"Golem","rarity":"Legendary","role":"Tank","hp":1500,"speed":1.5,"attack_damage":130,"attack_range":2.5,"attack_reload":1.8,"super_name":"Statue","super_effect":"Devient invincible mais immobile 5s","description":"Mur vivant, idéal pour hold point."},
{"id":10,"name":"Eclipse","rarity":"Legendary","role":"Polyvalent","hp":900,"speed":6,"attack_damage":90,"attack_range":5,"attack_reload":1.0,"super_name":"Clone d'ombre","super_effect":"Crée un clone qui attaque 6s","description":"Flexible et dangereux en duo."},
{"id":11,"name":"Vortex","rarity":"Mythic","role":"Contrôle","hp":820,"speed":4,"attack_damage":90,"attack_range":5,"attack_reload":1.3,"super_name":"Mur de vent","super_effect":"Repousse ennemis sur une ligne","description":"Contrôle de position."},
{"id":12,"name":"Kali","rarity":"Mythic","role":"Assassin","hp":680,"speed":8,"attack_damage":135,"attack_range":3.8,"attack_reload":0.95,"super_name":"Siphon","super_effect":"Vol de vie 6s (soin proportionnel aux dégâts)","description":"Sustain en duel."},
{"id":13,"name":"Glacio","rarity":"Mythic","role":"Distance","hp":760,"speed":3,"attack_damage":100,"attack_range":7,"attack_reload":1.4,"super_name":"Cage de glace","super_effect":"Enferme cible 2s","description":"Contrôle à distance."},
{"id":14,"name":"Rage","rarity":"Mythic","role":"Bruiser","hp":1000,"speed":6,"attack_damage":110,"attack_range":2.6,"attack_reload":1.0,"super_name":"Frénésie","super_effect":"ATK x2 pendant 4s","description":"Burst puissant proche."},
{"id":15,"name":"Tempo","rarity":"Mythic","role":"Support","hp":700,"speed":5,"attack_damage":65,"attack_range":5.5,"attack_reload":1.1,"super_name":"Cadence","super_effect":"Aura vitesse alliés 5s","description":"Boost teamfight."},
{"id":16,"name":"Jade","rarity":"Mythic","role":"Tank","hp":1100,"speed":2.5,"attack_damage":95,"attack_range":2.4,"attack_reload":1.5,"super_name":"Peau de pierre","super_effect":"Réduit dégâts subis 40% 5s","description":"Hold et frontliner."},
{"id":17,"name":"Pyro","rarity":"Mythic","role":"Area","hp":770,"speed":4,"attack_damage":105,"attack_range":4.5,"attack_reload":1.25,"super_name":"Zone enflammée","super_effect":"Flammes au sol qui persistent 4s (total 250 dmg)","description":"Domination de zone."},
{"id":18,"name":"Tazer","rarity":"Mythic","role":"Contrôle","hp":800,"speed":4,"attack_damage":95,"attack_range":5.5,"attack_reload":1.2,"super_name":"Chaîne","super_effect":"Éclair rebondissant sur 4 cibles","description":"Bon pour punir regroupements."},
{"id":19,"name":"Nox","rarity":"Mythic","role":"Poison","hp":720,"speed":6,"attack_damage":80,"attack_range":5,"attack_reload":1.1,"super_name":"Nuage toxique","super_effect":"Zone toxique 4s (dot total 220)","description":"Affaiblit zones tenues."},
{"id":20,"name":"Gaia","rarity":"Mythic","role":"SupportTank","hp":1150,"speed":2.8,"attack_damage":90,"attack_range":2.5,"attack_reload":1.4,"super_name":"Bouclier de pierre","super_effect":"Donne bouclier 600 HP à un allié","description":"Protection ciblée."},
{"id":21,"name":"Dash","rarity":"Epic","role":"Rapide","hp":600,"speed":8,"attack_damage":85,"attack_range":2.8,"attack_reload":0.8,"super_name":"Double dash","super_effect":"Deux dashes consécutifs pour repositionner","description":"Mobilité pure."},
{"id":22,"name":"Coral","rarity":"Epic","role":"Distance","hp":650,"speed":4,"attack_damage":95,"attack_range":6.5,"attack_reload":1.2,"super_name":"Vague poussive","super_effect":"Vague qui repousse et inflige 180 dmg","description":"Contrôle eau."},
{"id":23,"name":"Boomy","rarity":"Epic","role":"Engineer","hp":700,"speed":3.2,"attack_damage":60,"attack_range":5,"attack_reload":1.6,"super_name":"Tourelle","super_effect":"Pose tourelle explosive 12s (dmg 200)","description":"Zone denial."},
{"id":24,"name":"Sakura","rarity":"Epic","role":"Support","hp":620,"speed":5,"attack_damage":55,"attack_range":4.5,"attack_reload":1.1,"super_name":"Champ de pétales","super_effect":"Soigne alliés 6s (total 300)","description":"Soin en zone."},
{"id":25,"name":"Wrecker","rarity":"Epic","role":"Tank","hp":1100,"speed":2.4,"attack_damage":115,"attack_range":2.2,"attack_reload":1.5,"super_name":"Cri convulsif","super_effect":"Stun proches 1.2s + knockback","description":"Initie les mêlées."},
{"id":26,"name":"Pulse","rarity":"Epic","role":"Distance","hp":640,"speed":6,"attack_damage":95,"attack_range":5.5,"attack_reload":1.0,"super_name":"Rayon traversant","super_effect":"Rayon traversant 1 ligne 320 dmg","description":"Traverse obstacles lumineux."},
{"id":27,"name":"Turbo","rarity":"Epic","role":"Runner","hp":700,"speed":9,"attack_damage":70,"attack_range":2.6,"attack_reload":0.7,"super_name":"Turbo x3","super_effect":"Grande accélération 3s","description":"Idéal pour objectifs."},
{"id":28,"name":"Loki","rarity":"Epic","role":"Trickster","hp":620,"speed":7,"attack_damage":75,"attack_range":4,"attack_reload":1.0,"super_name":"Illusion","super_effect":"Crée un clone qui attire l'attention 5s","description":"Trompe l'ennemi."},
{"id":29,"name":"Robo-X","rarity":"Epic","role":"Bruiser","hp":1000,"speed":4,"attack_damage":110,"attack_range":2.8,"attack_reload":1.1,"super_name":"Mode survolté","super_effect":"ATK+30% et speed+20% 6s","description":"Force robotique polyvalente."},
{"id":30,"name":"Frosty","rarity":"Epic","role":"Contrôle","hp":760,"speed":3,"attack_damage":90,"attack_range":4.5,"attack_reload":1.3,"super_name":"Sol glissant","super_effect":"Zone qui ralentit 4s","description":"Gèle la mobilité ennemie."},
{"id":31,"name":"Spike Jr","rarity":"Rare","role":"Distance","hp":560,"speed":4,"attack_damage":95,"attack_range":5,"attack_reload":1.2,"super_name":"Grenade cactus","super_effect":"Explose en éclats (total 180)","description":"Dégâts splash."},
{"id":32,"name":"Hammer","rarity":"Rare","role":"Tank","hp":1080,"speed":2.6,"attack_damage":105,"attack_range":2.2,"attack_reload":1.4,"super_name":"Bouclier instant","super_effect":"Bouclier 400HP 5s","description":"Robuste et stable."},
{"id":33,"name":"Flint","rarity":"Rare","role":"Sniper","hp":520,"speed":4,"attack_damage":150,"attack_range":9,"attack_reload":2.0,"super_name":"Tir concentré","super_effect":"Tir chargé +50% dmg","description":"Sniper simple et efficace."},
{"id":34,"name":"Rookie","rarity":"Rare","role":"Polyvalent","hp":700,"speed":6,"attack_damage":80,"attack_range":4,"attack_reload":1.0,"super_name":"Burst speed","super_effect":"Gain vitesse 3s","description":"Facile à jouer."},
{"id":35,"name":"Bubble","rarity":"Rare","role":"Support","hp":600,"speed":4,"attack_damage":40,"attack_range":4,"attack_reload":1.1,"super_name":"Bulle protectrice","super_effect":"Protège allié 6s (absorbe 350 dmg)","description":"Protection ciblée."},
{"id":36,"name":"Bricko","rarity":"Rare","role":"Bruiser","hp":920,"speed":3.2,"attack_damage":95,"attack_range":2.2,"attack_reload":1.2,"super_name":"Armure temporaire","super_effect":"Réduction dégâts 30% 4s","description":"Tank agressif."},
{"id":37,"name":"Zippy","rarity":"Rare","role":"Rapide","hp":520,"speed":9,"attack_damage":70,"attack_range":2.6,"attack_reload":0.75,"super_name":"Sprint","super_effect":"Rapide courte durée + esquive","description":"Harceleur mobile."},
{"id":38,"name":"Magma","rarity":"Rare","role":"Zone","hp":700,"speed":3.1,"attack_damage":100,"attack_range":4.2,"attack_reload":1.3,"super_name":"Flamme circulaire","super_effect":"Cercle de feu 3s (total 260)","description":"Contrôle zone agressif."},
{"id":39,"name":"Pilot","rarity":"Rare","role":"Distance","hp":680,"speed":4.6,"attack_damage":85,"attack_range":6,"attack_reload":1.25,"super_name":"Drone garde","super_effect":"Drone allié tire 8s","description":"Soutien à distance."},
{"id":40,"name":"Bolt","rarity":"Rare","role":"Distance","hp":560,"speed":6,"attack_damage":95,"attack_range":5,"attack_reload":1.0,"super_name":"Charge électrique","super_effect":"Charge qui traverse cible (stun bref)","description":"Peu de cooldown, bon poke."},
{"id":41,"name":"Joe","rarity":"Common","role":"Basique","hp":650,"speed":5,"attack_damage":80,"attack_range":4,"attack_reload":1.0,"super_name":"Tir boosté","super_effect":"Tir puissant 1.5x dmg","description":"Bon apprentissage."},
{"id":42,"name":"Nina","rarity":"Common","role":"Distance","hp":520,"speed":6,"attack_damage":85,"attack_range":5.5,"attack_reload":0.95,"super_name":"Double flèche","super_effect":"Tir double 2 projectiles","description":"DPS stable."},
{"id":43,"name":"Rex","rarity":"Common","role":"Tank","hp":980,"speed":2.8,"attack_damage":95,"attack_range":2.2,"attack_reload":1.3,"super_name":"Récupération","super_effect":"Soigne petit montant instant","description":"Durée et sustain."},
{"id":44,"name":"Toto","rarity":"Common","role":"Melee","hp":680,"speed":5,"attack_damage":85,"attack_range":2.2,"attack_reload":0.9,"super_name":"Coup puissant","super_effect":"Coup plus fort et knockback","description":"Simplicité brute."},
{"id":45,"name":"Léo","rarity":"Common","role":"Distance","hp":540,"speed":6,"attack_damage":78,"attack_range":5.2,"attack_reload":0.95,"super_name":"Sprint tactique","super_effect":"Vitesse + petit shield","description":"Polyvalent rapide."},
{"id":46,"name":"Mia","rarity":"Common","role":"Support","hp":520,"speed":5,"attack_damage":45,"attack_range":4.5,"attack_reload":1.1,"super_name":"Soins groupés","super_effect":"Soigne alliés proches 220 total","description":"Soin basique accessible."},
{"id":47,"name":"Gus","rarity":"Common","role":"Bruiser","hp":820,"speed":3.2,"attack_damage":92,"attack_range":2.6,"attack_reload":1.15,"super_name":"Endurance","super_effect":"Réduit dégâts 20% 5s","description":"Tenace et simple."},
{"id":48,"name":"Rita","rarity":"Common","role":"Sniper","hp":540,"speed":4.2,"attack_damage":130,"attack_range":8.5,"attack_reload":2.0,"super_name":"Zoom","super_effect":"Augmente portée/precision 3s","description":"Positionnement clé."},
{"id":49,"name":"Drip","rarity":"Common","role":"Zone","hp":700,"speed":3.0,"attack_damage":88,"attack_range":4.0,"attack_reload":1.25,"super_name":"Splash","super_effect":"Cercle d'eau qui réduit vitesse 3s","description":"Zone slow utile."},
{"id":50,"name":"Moss","rarity":"Common","role":"Poison","hp":560,"speed":4.5,"attack_damage":70,"attack_range":4.8,"attack_reload":1.2,"super_name":"Champ toxique","super_effect":"Champ toxique 4s (dot 180)","description":"Contrôle durable."}
]