⚖️
LAW EMPIRE
Sierra Leone · Web Demo v3
"Justice rewards those who prepare."
⚖️
8 Cases · 7 Stages
Choose prosecution or defence
⚔️
Trial Arena
Multiplayer vs AI · ELO ranking
Dev Points
Free earn + premium fast-track
🏛️
Chambers System
Join chambers · get help
🏗️
Office Builder
Design your own law firm
🎁
Gift Codes
Daily on @lawempire_sl
lawempire.xyz · @lawempire_sl · Android Studio project also available
LAW EMPIRE · SL
🪙 1,000
0 DP
💎 3
⭐ Lv1
⚔️ 1000
Good Day

Experience0 / 200 XP
📅 Daily Rewards
📋 Activity Log
⚡ Quick Actions
0 cases await · Path:
Legal Cases

Select a case and choose your side. All players can compete at all levels. Rewards include Dev Points earned free through gameplay.

Skill Development

Upgrade legal expertise. 🪙 200 per level. Your career path gives starting skill bonuses. Higher skills = greater win probability.

My Profile
⚖️

ELO 1000
⚡ 0 DP
Attributes
Career Ladder
Global Rankings

ELO-ranked. Win Trial Arena matches to climb.

⚖️
LEVEL UP!

NEW PRIVILEGES

    // ═══════════════════════════════════════════════════ // SVG SCENES // ═══════════════════════════════════════════════════ function scene(type){ const s={ criminal:``, land:``, contract:``, family:``, environmental:``, company:``, international:``, freetown:`` }; return s[type]||s.freetown; } // ═══════════════════════════════════════════════════ // PLAYER STATE // ═══════════════════════════════════════════════════ let P={ name:"Lawyer",city:"Freetown",careerPath:"general", level:1,coins:1000,gems:3,xp:0,rep:0,elo:1000,devPoints:0, attrs:{intelligence:5,communication:5,negotiation:5,ethics:5,research:5,advocacy:5,leadership:5}, skills:{negotiation:1,advocacy:1,research:1,drafting:1,mediation:1,arbitration:1,leadership:1,diplomacy:1,forensics:1,ethics:1}, owned:[],officeGrid:[],chambers:null,helpBonus:0, completedCases:[],wonCases:[],wonTypes:[],mpWins:0, achievements:[],claimedAchievements:[], dailyClaimed:0,dailyStreak:0,log:[],redeemedCodes:[], }; // Trial state let curCase=null,chosenSide=null,stageIdx=0,stageScore=0; let selEv=new Set(),foundClues=new Set(); let pendingAch=null,obSelItem=null,obFloor={w:4,h:3}; let chosenCP=null,audioMuted=false; // ═══════════════════════════════════════════════════ // PARTICLES // ═══════════════════════════════════════════════════ const cv=document.getElementById('fxcvs'); const cx2=cv.getContext('2d'); let parts=[]; function rzCv(){cv.width=innerWidth;cv.height=innerHeight;} rzCv();addEventListener('resize',rzCv); function spawnP(x,y,n=40,c='#c9942a'){ for(let i=0;ip.a>.02); parts.forEach(p=>{p.x+=p.vx;p.y+=p.vy;p.vy+=.22;p.a-=.018; cx2.globalAlpha=p.a;cx2.fillStyle=p.c;cx2.beginPath();cx2.arc(p.x,p.y,p.r,0,Math.PI*2);cx2.fill();}); cx2.globalAlpha=1;requestAnimationFrame(tickP); } tickP(); // ═══════════════════════════════════════════════════ // UTILITY // ═══════════════════════════════════════════════════ function opn(id){document.getElementById(id).classList.add('on');} function cls(id){document.getElementById(id).classList.remove('on');} function showScr(id){document.querySelectorAll('.scr').forEach(s=>s.classList.remove('on'));document.getElementById(id).classList.add('on');} function showP(id){ document.querySelectorAll('.panel').forEach(p=>p.classList.remove('on')); document.querySelectorAll('.nb').forEach(b=>b.classList.remove('on')); const p=document.getElementById('panel-'+id);if(p)p.classList.add('on'); const nb=document.getElementById('nb-'+id);if(nb)nb.classList.add('on'); if(id==='dash')renderDash(); if(id==='lb')renderLB(); if(id==='profile')renderProfile(); } function careerTitle(){return CAREER[P.level-1]?.t||'Global Legal Icon';} function toast(msg,type=''){ const c=document.getElementById('toasts'); const d=document.createElement('div');d.className='toast'; d.style.borderLeftColor=type==='gold'?'var(--gold-lt)':type==='dp'?'#b080e8':'var(--gold)'; d.textContent=msg;c.appendChild(d); setTimeout(()=>{d.classList.add('out');setTimeout(()=>d.remove(),300);},3200); } function logAct(msg){ const t=new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); P.log.push({m:msg,t});if(P.log.length>20)P.log.shift(); } function updateHUD(){ document.getElementById('hc').textContent=P.coins.toLocaleString(); document.getElementById('hdp').textContent=P.devPoints; document.getElementById('hg').textContent=P.gems; document.getElementById('hl').textContent=P.level; document.getElementById('he').textContent=P.elo; document.getElementById('gc-dot').classList.toggle('on',false); const ub=P.achievements.filter(id=>!P.claimedAchievements.includes(id)).length; document.getElementById('ach-dot').classList.toggle('on',ub>0); } function hint(t){document.getElementById('trial-hint').textContent=t;} function toggleAudio(){ audioMuted=!audioMuted; document.getElementById('audio-btn').textContent=audioMuted?'🔇':'🔊'; toast(audioMuted?'Audio muted':'Audio on'); } // ═══════════════════════════════════════════════════ // CAREER PATH SELECT // ═══════════════════════════════════════════════════ function showCP(){ renderCPGrid();opn('cpOvl');showScr('splash');document.getElementById('splash').classList.add('on'); } function renderCPGrid(){ document.getElementById('cp-grid').innerHTML=CAREER_PATHS.map(cp=> `
    ${cp.ico}
    ${cp.n}
    ${cp.desc}
    ${cp.bonus}
    `).join(''); } function pickCP(id){ chosenCP=id; document.getElementById('cp-confirm').disabled=false; renderCPGrid(); } function confirmCP(){ cls('cpOvl'); const name=prompt('Enter your lawyer name (e.g. Amara Koroma):')||'Amara Koroma'; P.name=name.trim()||'Amara Koroma'; const cities=['Freetown','Bo','Kenema','Makeni','Port Loko','Koidu']; P.city=cities[Math.floor(Math.random()*cities.length)]; P.careerPath=chosenCP||'general'; const cp=CAREER_PATHS.find(c=>c.id===P.careerPath); if(cp)Object.entries(cp.skills).forEach(([k,v])=>P.skills[k]=(P.skills[k]||1)+v); logAct(`Career started: ${cp?.n||'General Practitioner'} in ${P.city}`); showScr('game');initGame(); toast(`Welcome to the bar, ${P.name}! Path: ${cp?.n||'General Practitioner'}`,'gold'); } function loadDemo(){ P={...P,name:"Demo Lawyer",city:"Freetown",careerPath:"criminal", level:3,coins:5000,gems:8,xp:1200,rep:25,elo:1150,devPoints:85, wonCases:[1,2],wonTypes:["criminal","land"],completedCases:[1,2],mpWins:1, skills:{negotiation:3,advocacy:4,research:3,drafting:2,mediation:2,arbitration:2,leadership:2,diplomacy:2,forensics:3,ethics:2}}; showScr('game');logAct('Demo loaded — all features available!');initGame(); toast('Demo loaded! Explore all features freely.','gold'); } // ═══════════════════════════════════════════════════ // INIT // ═══════════════════════════════════════════════════ function initGame(){ updateHUD();renderDash();renderCases();renderSkills();renderProfile();renderLB(); renderOB();renderChambers();renderShop('dp');renderMP();renderAchievements(); } // ═══════════════════════════════════════════════════ // DASHBOARD // ═══════════════════════════════════════════════════ function renderDash(){ document.getElementById('dash-scene').innerHTML=scene('freetown'); document.getElementById('d-welcome').textContent=`Good Day, ${P.name}`; const cp=CAREER_PATHS.find(c=>c.id===P.careerPath); document.getElementById('d-career').textContent=`${careerTitle()} · ${P.city}${P.chambers?' · '+P.chambers:''}`; document.getElementById('d-path').textContent=cp?.n||'General Practitioner'; const need=CAREER[P.level]?.xp||50000,prev=CAREER[P.level-1]?.xp||0; const pct=Math.min(100,((P.xp-prev)/(need-prev))*100)||0; document.getElementById('d-xp-bar').style.width=pct+'%'; document.getElementById('d-xp-txt').textContent=`${P.xp.toLocaleString()} / ${need.toLocaleString()} XP`; document.getElementById('d-stats').innerHTML=[ {ico:'⚖️',v:P.wonCases.length,l:'Cases Won'},{ico:'🪙',v:P.coins.toLocaleString(),l:'Coins'}, {ico:'⚡',v:P.devPoints,l:'Dev Points'},{ico:'🏆',v:P.rep,l:'Reputation'}, {ico:'⚔️',v:P.mpWins,l:'MP Wins'},{ico:'📊',v:P.elo,l:'ELO'}, ].map(x=>`
    ${x.ico}
    ${x.v}
    ${x.l}
    `).join(''); document.getElementById('d-daily').innerHTML=DAILY_D.map((d,i)=>`
    Day ${d.d}
    ${d.ico}
    ${d.lbl.split(' ').slice(-1)[0]}
    `).join(''); const logEl=document.getElementById('d-log'); logEl.innerHTML=(P.log.length?P.log.slice(-5).reverse():[{m:'No activity yet.',t:''}]) .map(l=>`
    ${l.t} ${l.m}
    `).join(''); document.getElementById('d-active').textContent=CASES.filter(c=>c.minLv<=P.level&&!P.completedCases.includes(c.id)).length; } function claimDaily(i){ const d=DAILY_D[i]; P.coins+=d.coins;P.xp+=d.xp;P.gems+=d.gems;P.devPoints+=d.dp||0; P.dailyClaimed++;P.dailyStreak++; toast(`Daily reward: ${d.lbl}${d.dp?' + ⚡'+d.dp+' DP':''}`,'gold'); logAct(`Daily claimed: ${d.lbl}`); doLvlCheck();achCheck();updateHUD();renderDash(); } // ═══════════════════════════════════════════════════ // CASES // ═══════════════════════════════════════════════════ const TC={criminal:'#7a1a1a',land:'#2d7a4f',contract:'#8a6010',family:'#7a4a8a',environmental:'#3a6a2a',company:'#2a5a8a',international:'#4a4a8a'}; function renderCases(){ document.getElementById('cases-grid').innerHTML=CASES.map(c=>{ const lk=c.minLv>P.level,dn=P.completedCases.includes(c.id); return`
    ${scene(c.type)}
    ${c.cat}
    ${dn?'
    ✓ COMPLETED
    ':''} ${lk?`
    🔒 Requires Level ${c.minLv}
    `:''}
    ${c.title}
    ${c.desc.slice(0,78)}…
    ${[1,2,3,4,5].map(i=>`
    `).join('')}
    🪙${c.coins.toLocaleString()} ⚡+${c.dp}DP
    📍${c.city} · ⚔️ ${c.sideA} vs ${c.sideB}
    `; }).join(''); } // ═══════════════════════════════════════════════════ // SIDE SELECTION // ═══════════════════════════════════════════════════ function openSide(id){ curCase=CASES.find(c=>c.id===id);chosenSide=null; document.getElementById('side-title').textContent=`Choose Your Side — ${curCase.title}`; document.getElementById('side-scene-wrap').innerHTML=scene(curCase.type); document.getElementById('sides-row').innerHTML=`
    ⚔️

    ${curCase.sideA}

    ${curCase.sideADesc}

    🛡️

    ${curCase.sideB}

    ${curCase.sideBDesc}

    `; document.getElementById('side-confirm').disabled=true; opn('sideOvl'); } function pickSide(s){ chosenSide=s; document.querySelectorAll('.side-opt').forEach(el=>el.classList.remove('chosen')); document.getElementById('sopt-'+s).classList.add('chosen'); document.getElementById('side-confirm').disabled=false; } function confirmSide(){ if(!chosenSide)return;cls('sideOvl'); stageIdx=0;stageScore=0;selEv=new Set();foundClues=new Set(); document.getElementById('my-side-tag').textContent=`${chosenSide==='A'?'⚔️':'🛡️'} ${chosenSide==='A'?curCase.sideA:curCase.sideB} (You)`; const opp=AI_OPP[Math.min(Math.floor(P.level/2),AI_OPP.length-1)]; document.getElementById('opp-side-tag').textContent=`🆚 ${opp.av} ${opp.n}`; opn('trialOvl');renderStage(); } // ═══════════════════════════════════════════════════ // TRIAL ENGINE // ═══════════════════════════════════════════════════ const STAGE_SEQ=['investigation','evidence','pretrial','opening','crossexam','closing','verdict']; const STAGE_META={ investigation:{ico:'🔍',lbl:'Scene Investigation'},evidence:{ico:'📋',lbl:'Evidence Review'}, pretrial:{ico:'⚖️',lbl:'Pre-Trial Motions'},opening:{ico:'🗣️',lbl:'Opening Statement'}, crossexam:{ico:'❓',lbl:'Cross-Examination'},closing:{ico:'📢',lbl:'Closing Arguments'},verdict:{ico:'🔨',lbl:'Verdict'}, }; function renderStage(){ const sKey=STAGE_SEQ[stageIdx],def=STAGE_META[sKey]; document.getElementById('th-ico').textContent=def.ico; document.getElementById('th-title').textContent=`${def.lbl} — ${curCase.title}`; document.getElementById('th-stg').textContent=`Stage ${stageIdx+1}/${STAGE_SEQ.length}`; document.getElementById('trial-prog').innerHTML=STAGE_SEQ.map((_,i)=>`
    `).join(''); document.getElementById('trial-svg').innerHTML=scene(curCase.type); const body=document.getElementById('tbody'),nxt=document.getElementById('next-btn'); nxt.disabled=true;nxt.textContent='Continue →';nxt.onclick=advStage; document.getElementById('skip-btn').style.display=sKey==='pretrial'?'block':'none'; hint('Make your selection to continue.'); if(sKey==='investigation')stgInvest(body,nxt); else if(sKey==='evidence')stgEv(body,nxt); else if(sKey==='pretrial')stgPretrial(body,nxt); else if(sKey==='opening')stgOpening(body,nxt); else if(sKey==='crossexam')stgXExam(body,nxt); else if(sKey==='closing')stgClosing(body,nxt); else stgVerdict(body,nxt); } function advStage(){stageIdx++;if(stageIdx>=STAGE_SEQ.length)stgVerdict(document.getElementById('tbody'),document.getElementById('next-btn'));else renderStage();} function skipStage(){stageScore+=8;advStage();} function witHtml(w,alt){ if(!w)return''; return`
    ${w.av}
    ${w.name} — ${w.role}
    "${alt||w.line}"
    `; } function stgInvest(body,nxt){ const clues=curCase.clues,labels=['Document','Witness','Physical','Digital','Forensic','Financial']; body.innerHTML=`
    Scene Investigation
    Examine the crime scene. Find 3+ clues to strengthen your case.
    Clues found: 0/${clues.length}Find 3+ to continue
    ${witHtml(curCase.witnesses[0])}`; const bd=document.getElementById('cl-board'); [...Array(6)].map((_,i)=>i).sort(()=>Math.random()-.5).forEach((p,i)=>{ const d=document.createElement('div');d.className='cl-tile'; d.innerHTML=`
    ${clues[p]||'📎'}
    ${labels[p]||'Clue'}
    `; d.onclick=()=>{ if(foundClues.has(p))return;foundClues.add(p);d.classList.add('found');stageScore+=15; document.getElementById('cl-ct').textContent=foundClues.size; toast(`Clue found! +15 pts`); if(foundClues.size>=3){nxt.disabled=false;hint(foundClues.size+' clues found. Continue when ready.');} }; bd.appendChild(d); }); } function stgEv(body,nxt){ const ev=curCase.evidence; body.innerHTML=`
    Evidence Review
    Select the most relevant evidence. Each item adds to your case strength.
    Selected: 0/${ev.length}
    `; const grid=document.getElementById('ev-grid'); ev.forEach((e,i)=>{ const d=document.createElement('div');d.className='ev-item'; d.innerHTML=`
    ${e.icon}
    ${e.txt}
    ⚡ +${e.str}%
    `; d.onclick=()=>{ d.classList.toggle('sel'); if(d.classList.contains('sel')){selEv.add(i);stageScore+=18;}else{selEv.delete(i);stageScore=Math.max(0,stageScore-18);} document.getElementById('ev-ct').textContent=selEv.size; nxt.disabled=selEv.size===0; hint(selEv.size>0?selEv.size+' items selected.':'Select at least one piece of evidence.'); }; grid.appendChild(d); }); } function stgPretrial(body,nxt){ const strats=curCase.strats[chosenSide]||[]; const motions=[ {m:strats[0]||"File preliminary motion",pts:30,risk:"Low"}, {m:strats[1]||"Request case disclosure",pts:25,risk:"Low"}, {m:strats[2]||"Challenge procedural points",pts:22,risk:"Medium"}, {m:strats[3]||"Bold early motion",pts:35,risk:"High"}, {m:"Challenge court jurisdiction",pts:15,risk:"High"}, {m:"Apply for adjournment to prepare",pts:10,risk:"Low"}, ]; body.innerHTML=`
    Pre-Trial Motions
    File strategic motions to shape the trial in your favour before proceedings begin.
    `; const grid=document.getElementById('mo-grid'); motions.forEach(m=>{ const b=document.createElement('button');b.className='act-btn'; b.innerHTML=`${m.m}
    +${m.pts}pts
    Risk: ${m.risk}
    `; b.onclick=()=>{document.querySelectorAll('#mo-grid .act-btn').forEach(x=>x.classList.remove('sel'));b.classList.add('sel');stageScore+=m.pts;nxt.disabled=false;hint('Motion filed. Continue.');}; grid.appendChild(b); }); } function stgOpening(body,nxt){ const stmts=[ {s:`"The evidence today will demonstrate beyond all doubt the truth of our case. We will walk this court through a clear, compelling narrative backed by irrefutable evidence."`,pts:38}, {s:`"This case speaks to the soul of justice in Sierra Leone. We owe it to our people to pursue this with the utmost rigour the law demands."`,pts:44}, {s:`"The facts are stark. The law is clear. Once you have heard the evidence, there can be only one verdict consistent with justice."`,pts:32}, {s:`"We present a comprehensive case built on solid evidence, reliable witnesses, and sound legal principles this court cannot ignore."`,pts:36}, ]; body.innerHTML=`
    Opening Statement
    Set the tone. Choose your opening to frame the entire trial narrative.
    ${witHtml({av:"👨🏾‍⚖️",name:"His Honour the Judge",role:"Presiding Judge"},"Counsel, you may begin your opening. The court is in session.")}
    `; const g=document.getElementById('st-grid'); stmts.forEach(s=>{ const b=document.createElement('button');b.className='act-btn'; b.innerHTML=`
    ${s.s.slice(0,90)}…
    +${s.pts}pts
    `; b.onclick=()=>{document.querySelectorAll('#st-grid .act-btn').forEach(x=>x.classList.remove('sel'));b.classList.add('sel');stageScore+=s.pts;nxt.disabled=false;hint('Statement delivered.');}; g.appendChild(b); }); } function stgXExam(body,nxt){ const qs=curCase.examQ;let qi=0,pts=0; const showQ=()=>{ if(qi>=qs.length){ nxt.disabled=false; body.insertAdjacentHTML('beforeend',`
    ✓ Cross-exam complete! +${pts} pts
    `); hint('Cross-examination complete. Continue.');return; } const q=qs[qi]; body.innerHTML=`
    Cross-Examination
    Your questioning technique directly affects the trial outcome.
    ${witHtml(curCase.witnesses[1]||curCase.witnesses[0])}
    Q${qi+1}: ${q.q}
    `; qs[qi].opts.forEach((o,oi)=>{ const b=document.createElement('button');b.className='xo';b.textContent=o; b.onclick=()=>{ document.querySelectorAll('.xo').forEach(x=>x.disabled=true); b.classList.add(oi===q.c?'correct':'wrong'); if(oi===q.c){pts+=q.pts;stageScore+=q.pts;toast(`Correct! +${q.pts} pts`);} else toast('A stronger argument was available.'); qi++;setTimeout(showQ,850); }; document.getElementById('xam-opts').appendChild(b); }); }; showQ(); } function stgClosing(body,nxt){ const args=[ {a:`"The weight of evidence is overwhelming. Every witness, document, and exhibit points to one conclusion. The court cannot reach any other verdict."`,pts:44}, {a:`"On behalf of the people of Sierra Leone, I urge this court to deliver a verdict that upholds the rule of law and sends a clear message."`,pts:48}, {a:`"Your Honour, the law is unambiguous. The facts are undeniable. There is only one verdict consistent with justice and the evidence."`,pts:36}, {a:`"Having heard all the evidence, the matter has been proven to the required standard. The court should find accordingly."`,pts:40}, ]; body.innerHTML=`
    Closing Arguments
    Your final opportunity to persuade. Make every word count.
    ${witHtml({av:"👨🏾‍⚖️",name:"His Honour the Judge",role:"Presiding Judge"},"Counsel, you have the floor for your closing argument.")}
    `; const g=document.getElementById('cl-grid'); args.forEach(a=>{ const b=document.createElement('button');b.className='act-btn'; b.innerHTML=`
    "${a.a.slice(0,90)}…"
    +${a.pts}pts
    `; b.onclick=()=>{document.querySelectorAll('#cl-grid .act-btn').forEach(x=>x.classList.remove('sel'));b.classList.add('sel');stageScore+=a.pts;nxt.disabled=false;hint('Closing delivered. Verdict imminent.');}; g.appendChild(b); }); } function stgVerdict(body,nxt){ const sKey={criminal:'advocacy',land:'research',contract:'negotiation',family:'mediation',company:'drafting',environmental:'research',international:'arbitration'}[curCase.type]||'advocacy'; const skill=P.skills[sKey]||1; const cp=CAREER_PATHS.find(c=>c.id===P.careerPath); const cpBonus=cp?.id&&curCase.type&&( (cp.id==='criminal'&&curCase.type==='criminal'?.28)|| (cp.id==='civil'&&(curCase.type==='contract'||curCase.type==='land')?.22)|| (cp.id==='corporate'&&(curCase.type==='company'||curCase.type==='contract')?.22)|| (cp.id==='family'&&curCase.type==='family'?.28)|| (cp.id==='enviro'&&curCase.type==='environmental'?.36)|| (cp.id==='intl'&&curCase.type==='international'?.32)|| (cp.id==='general'?.08)||0)||.02; const chBon=P.chambers?(P.chambers.includes('Silk')||P.chambers.includes('elite')?.18:P.chambers.includes('General')?.08:.12):0; const evBon=selEv.size*.07,scoreBon=Math.min(.22,stageScore/1300),lvBon=Math.min(.14,(P.level/15)*.14); const helpBon=P.helpBonus||0;P.helpBonus=0; const winP=Math.min(.95,Math.max(.05,.4+(skill*.04)+evBon+scoreBon+lvBon+chBon+cpBonus+helpBon)); const won=Math.random()The Verdict
    All stages complete. Opponent: ${opp.n} (${opp.st})
    Path bonus: ${Math.round(cpBonus*100)}% · Chambers bonus: ${Math.round(chBon*100)}% · Evidence: ${selEv.size} items
    Your case strength${Math.round(winP*100)}%
    Score: ${stageScore} · Clues: ${foundClues.size}
    🔨
    The court deliberates…
    ${won?'JUDGMENT FOR YOUR CLIENT':'JUDGMENT AGAINST'}
    `; nxt.disabled=false;nxt.textContent='Accept Verdict →';nxt.onclick=()=>finalise(won); setTimeout(()=>{ const vw=document.getElementById('vwd');vw.style.transition='opacity .5s';vw.style.opacity='1'; const vd=document.getElementById('vd-detail');vd.style.transition='opacity .5s';vd.style.opacity='1'; vd.textContent=won?`Score ${stageScore} pts and ${selEv.size} evidence items secured the verdict.`:`Score ${stageScore} pts was not sufficient. ${opp.n} prevailed today.`; if(won){const r=vw.getBoundingClientRect();spawnP(r.left+r.width/2,r.top+20,50,'#c9942a');spawnP(r.left+r.width/2,r.top+20,22,'#2d7a4f');} hint('Accept the verdict to collect your rewards.'); },900); } function finalise(won){ if(!curCase)return; const mult=won?1+(P.skills.negotiation-1)*.05:.2; const coins=Math.floor(curCase.coins*mult),xp=Math.floor(curCase.xp*(won?1:.45)); const rep=won?curCase.rep:Math.max(1,Math.floor(curCase.rep*.15)); const dp=won?curCase.dp:Math.max(2,Math.floor(curCase.dp*.2)); const exp=1/(1+Math.pow(10,(1000-P.elo)/400)); const eloD=won?Math.floor(25*(1-exp)):Math.floor(-18*exp); P.coins+=coins;P.xp+=xp;P.rep+=rep;P.devPoints+=dp;P.elo=Math.max(100,P.elo+eloD); if(won){if(!P.wonCases.includes(curCase.id))P.wonCases.push(curCase.id); if(!P.completedCases.includes(curCase.id))P.completedCases.push(curCase.id); if(!P.wonTypes.includes(curCase.type))P.wonTypes.push(curCase.type);} logAct(`${won?'Won ✓':'Lost ✗'} "${curCase.title}" (+🪙${coins},+${xp}XP,+⚡${dp}DP,ELO${eloD>=0?'+':''}${eloD})`); const leveled=doLvlCheck();const newAch=achCheck(); cls('trialOvl');showResult(won,coins,xp,rep,dp); updateHUD();renderCases();renderDash(); if(newAch.length>0)setTimeout(()=>showAchNotif(newAch[0]),2200); if(leveled)setTimeout(()=>{ document.getElementById('lu-career').textContent=careerTitle(); document.getElementById('lu-perks-list').innerHTML=(PERKS[P.level-1]||['New opportunities']).map(p=>`
  • ✓ ${p}
  • `).join(''); opn('lvlOvl');spawnP(innerWidth/2,innerHeight/3,65,'#c9942a'); const ldp=P.level*10;P.devPoints+=ldp; logAct(`Promoted to ${careerTitle()}! +⚡${ldp} Dev Points`); toast(`Level Up! +⚡${ldp} Dev Points earned`,'dp'); updateHUD(); },2000); } function showResult(won,coins,xp,rep,dp){ document.getElementById('res-scene').innerHTML=scene(curCase?.type||'freetown'); document.getElementById('res-ico').textContent=won?'🏆':'📋'; document.getElementById('res-title').textContent=won?'Case Won!':'Case Concluded'; document.getElementById('res-sub').textContent=won?'Excellent advocacy! The verdict is in your favour.':'A challenging case. The experience will make you stronger.'; document.getElementById('res-rws').innerHTML=[ {n:'+'+coins.toLocaleString(),l:'Coins',c:'var(--gold)'},{n:'+'+xp,l:'XP',c:'var(--green-lt)'}, {n:'+'+rep,l:'Reputation',c:'var(--gold)'},{n:'+'+ dp+' DP',l:'Dev Points',c:'#b080e8'}, ].map(r=>`
    ${r.n}${r.l}
    `).join(''); opn('resOvl');if(won)spawnP(innerWidth/2,innerHeight/2,55,'#c9942a'); } function doLvlCheck(){ if(P.level>=15)return false; if(P.xp>=(CAREER[P.level]?.xp||50000)){P.level++;return true;} return false; } // ═══════════════════════════════════════════════════ // ACHIEVEMENTS // ═══════════════════════════════════════════════════ function achCheck(){ const newly=[]; ACHIEVEMENTS_D.forEach(a=>{ if(P.achievements.includes(a.id)||P.claimedAchievements.includes(a.id))return; let e=false;const c=a.cond; if(c.t==='wins'&&P.wonCases.length>=c.v)e=true; if(c.t==='type'&&P.wonTypes.includes(c.v))e=true; if(c.t==='case_id'&&P.wonCases.includes(c.v))e=true; if(c.t==='level'&&P.level>=c.v)e=true; if(c.t==='office'&&P.officeGrid.length>=c.v)e=true; if(c.t==='daily'&&P.dailyStreak>=c.v)e=true; if(c.t==='mp'&&P.mpWins>=c.v)e=true; if(c.t==='chambers'&&P.chambers)e=true; if(c.t==='skill'&&Object.values(P.skills).some(v=>v>=c.v))e=true; if(c.t==='all_types'){const types=["criminal","land","contract","family","environmental","company","international"];e=types.every(t=>P.wonTypes.includes(t));} if(e){P.achievements.push(a.id);newly.push(a);} }); if(newly.length)updateHUD();return newly; } function showAchNotif(a){ pendingAch=a; document.getElementById('an-ico').textContent=a.icon; document.getElementById('an-title').textContent=a.n; document.getElementById('an-desc').textContent=a.desc; const rws=document.getElementById('an-rws');rws.innerHTML=''; [{ico:'🪙',v:`+${a.reward.coins.toLocaleString()}`,l:'Coins'},{ico:'⚡',v:`+${a.reward.dp}`,l:'Dev Points'},{ico:'💎',v:`+${a.reward.gems}`,l:'Gems'},{ico:'⭐',v:`+${a.reward.xp}`,l:'XP'}] .filter(r=>r.v!=='+0').forEach(r=>{rws.innerHTML+=`
    ${r.ico}
    ${r.l}
    ${r.v}
    `;}); opn('achNotifyOvl');spawnP(innerWidth/2,innerHeight/2,38,'#c9942a'); } function claimAch(){ if(!pendingAch)return; P.coins+=pendingAch.reward.coins||0;P.xp+=pendingAch.reward.xp||0; P.gems+=pendingAch.reward.gems||0;P.devPoints+=pendingAch.reward.dp||0; P.claimedAchievements.push(pendingAch.id); toast(`Achievement "${pendingAch.n}" claimed! +⚡${pendingAch.reward.dp} DP`,'dp'); logAct(`Achievement claimed: ${pendingAch.n}`); pendingAch=null;cls('achNotifyOvl');updateHUD();renderAchievements();doLvlCheck(); } function renderAchievements(){ const tierLbl={1:'Silver',2:'Gold',3:'Emerald',4:'Elite'}; const earned=P.achievements.length; document.getElementById('ach-summary').textContent=`${earned}/19 earned`; document.getElementById('ach-grid').innerHTML=ACHIEVEMENTS_D.map(a=>{ const isE=P.achievements.includes(a.id),isC=P.claimedAchievements.includes(a.id); return`
    ${a.icon}
    ${a.n}
    ${a.desc}
    🪙${a.reward.coins} ⚡${a.reward.dp}DP 💎${a.reward.gems}
    ${isE&&!isC?``:''} ${isC?'
    ✓ Claimed
    ':''}
    ${tierLbl[a.tier]}
    `; }).join(''); } // ═══════════════════════════════════════════════════ // OFFICE BUILDER // ═══════════════════════════════════════════════════ function renderOB(){ const cats=[...new Set(OB_ITEMS.map(i=>i.cat))]; const sb=document.getElementById('ob-sidebar');sb.innerHTML=''; cats.forEach(cat=>{ const hd=document.createElement('div'); hd.style.cssText='font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--sienna);margin:8px 0 4px;'; hd.textContent=cat;sb.appendChild(hd); OB_ITEMS.filter(i=>i.cat===cat).forEach(item=>{ const placed=P.officeGrid.some(g=>g.id===item.id); const sel=obSelItem===item.id; const d=document.createElement('div'); d.style.cssText=`display:flex;align-items:center;gap:6px;padding:7px 8px;border:1px solid ${sel?'var(--green)':'#c8bfa0'};border-radius:3px;background:${sel?'rgba(26,74,46,.07)':'#fffdf5'};margin-bottom:4px;cursor:${placed?'not-allowed':'pointer'};opacity:${placed?.5:1};`; d.innerHTML=`${item.ico}
    ${item.n}
    ${placed?'✓ Placed':'🪙'+item.price.toLocaleString()}
    `; if(!placed)d.onclick=()=>{ if(P.coinsg.x===c&&g.y===r); const item=placed?OB_ITEMS.find(i=>i.id===placed.id):null; const cell=document.createElement('div'); const sz=Math.min(56,Math.max(34,(330-w*4)/w)); cell.style.cssText=`width:${sz}px;height:${sz}px;border:1px solid ${placed?'var(--green-lt)':'#e0d8c0'};background:${placed?'rgba(26,74,46,.07)':'rgba(240,232,208,.3)'};display:flex;align-items:center;justify-content:center;font-size:${sz>44?21:17}px;cursor:pointer;transition:background .1s;`; cell.textContent=item?.ico||''; cell.title=item?`${item.n} (click to remove, 50% refund)`:'Click to place selected item'; cell.onclick=()=>{ if(placed){ const it=OB_ITEMS.find(i=>i.id===placed.id);const refund=Math.floor(it.price*.5); P.coins+=refund;P.officeGrid=P.officeGrid.filter(g=>!(g.x===c&&g.y===r)); toast(`Removed ${it.n}. Refund: 🪙${refund.toLocaleString()}`);renderOB();updateHUD(); } else { if(!obSelItem){toast('Select an item from the sidebar first.');return;} const it=OB_ITEMS.find(i=>i.id===obSelItem);if(!it)return; P.coins-=it.price;P.officeGrid.push({id:it.id,x:c,y:r}); logAct(`Placed ${it.n} in office`);achCheck();obSelItem=null; renderOB();updateHUD();toast(`${it.n} placed! +🪙${it.income}/day`); } }; floor.appendChild(cell); } } function updateOBStats(){ const income=P.officeGrid.reduce((s,g)=>s+(OB_ITEMS.find(i=>i.id===g.id)?.income||0),0); const pres=P.officeGrid.reduce((s,g)=>s+(OB_ITEMS.find(i=>i.id===g.id)?.prestige||0),0); document.getElementById('ob-inc-hd').textContent=`🪙${income}/day`; document.getElementById('ob-pres-hd').textContent=pres; document.getElementById('ob-cnt').textContent=P.officeGrid.length; document.getElementById('ob-inc').textContent=`🪙${income}`; document.getElementById('ob-pr').textContent=pres; } function changeFloor(val){ const [w,h]=val.split(',').map(Number); const costs={'4,3':0,'6,4':15000,'8,6':40000,'10,8':100000}; const cost=costs[val]||0; if(cost>0&&P.coins0){P.coins-=cost;toast(`Floor upgraded! 🪙${cost.toLocaleString()} spent.`);updateHUD();} obFloor={w,h};P.officeGrid=P.officeGrid.filter(g=>g.x `
    ${ch.icon}
    ${ch.n}
    ${ch.focus}
    ${ch.bonus}
    ${P.chambers===ch.n?'
    ✓ Current Chambers
    ':''}
    `).join(''); document.getElementById('help-grid').innerHTML=HELP_D.map(h=> `
    ${h.n}
    ✓ ${h.benefit}
    🪙 ${h.cost.toLocaleString()}
    `).join(''); } function joinChamber(name){ P.chambers=name;achCheck();logAct(`Joined ${name}`);toast(`Joined ${name}!`); renderChambers();renderDash();updateHUD(); } function buyHelp(id,name,cost,benefit){ if(P.coins${DP_PKGS.map(p=>`
    ${p.tag?`
    ${p.tag}
    `:''}
    ${p.ico}
    ${p.n}
    ⚡ ${p.dp.toLocaleString()}
    ${p.gems?`
    + 💎 ${p.gems}
    `:''} ${p.coins?`
    + 🪙 ${p.coins.toLocaleString()}
    `:''}
    ${p.usd}
    ${p.sll}
    `).join('')}
    Fair Play Commitment: Dev Points give faster growth only. Free players can always reach Level 15 and win every case.
    `; }else if(tab==='coins'){ body.innerHTML=`
    ${COIN_PKGS.map(p=>`
    ${p.tag?`
    ${p.tag}
    `:''}
    ${p.ico}
    ${p.n}
    🪙 ${p.coins.toLocaleString()}
    ${p.gems?`
    + 💎 ${p.gems}
    `:''}
    ${p.usd}
    ${p.sll}
    `).join('')}
    `; }else{ body.innerHTML=`
    ${FREE_EARN.map(e=>`
    ${e.ico}
    ${e.n}
    ${e.desc}
    +${e.dp} DP
    `).join('')}
    🆓 All Dev Points above are earnable completely free through regular gameplay. No purchase needed to enjoy the full game.
    `; } } function shopTab(tab,btn){ document.querySelectorAll('.sh-tab').forEach(t=>t.classList.remove('on'));btn.classList.add('on');renderShop(tab); } function simPurchase(name,dp,coins,gems){ toast(`🔒 "${name}" — Payment gateway coming soon. lawempire.xyz`); } // ═══════════════════════════════════════════════════ // GIFT CODES // ═══════════════════════════════════════════════════ function gcFormat(el){ let raw=el.value.toUpperCase().replace(/[^A-Z0-9]/g,'').slice(0,14); let fmt=''; if(raw.length>2)fmt=raw.slice(0,2)+'-'+raw.slice(2);else fmt=raw; if(raw.length>6)fmt=raw.slice(0,2)+'-'+raw.slice(2,6)+'-'+raw.slice(6); if(raw.length>10)fmt=raw.slice(0,2)+'-'+raw.slice(2,6)+'-'+raw.slice(6,10)+'-'+raw.slice(10); el.value=fmt; document.getElementById('gc-btn').disabled=raw.length<14; } function redeemGC(){ const raw=document.getElementById('gc-input').value.toUpperCase().replace(/[^A-Z0-9]/g,''); if(raw.length!==14||!raw.startsWith('LE')){toast('Invalid code. Format: LE-XXXX-XXXX-XXXX');return;} if(P.redeemedCodes.includes(raw)){toast('This code has already been used on your account.');return;} P.redeemedCodes.push(raw); const reward={coins:1000,dp:10,gems:0,xp:200}; P.coins+=reward.coins;P.devPoints+=reward.dp;P.xp+=reward.xp; logAct(`Gift code redeemed: ${document.getElementById('gc-input').value} (+🪙${reward.coins},+⚡${reward.dp}DP)`); updateHUD();cls('gcOvl'); toast(`🎁 Code redeemed! +🪙${reward.coins} Coins, +⚡${reward.dp} Dev Points`,'gold'); spawnP(innerWidth/2,innerHeight/2,42,'#c9942a'); document.getElementById('gc-input').value=''; document.getElementById('gc-btn').disabled=true; } // ═══════════════════════════════════════════════════ // MULTIPLAYER // ═══════════════════════════════════════════════════ function renderMP(){ document.getElementById('mp-elo').textContent=P.elo; const rank=P.elo>=2000?'🏆 Grandmaster':P.elo>=1800?'💎 Diamond':P.elo>=1600?'🥇 Platinum':P.elo>=1400?'🥈 Gold':P.elo>=1200?'🥉 Silver':'🎓 Bronze'; document.getElementById('mp-rank').textContent=rank; document.getElementById('mp-rooms').innerHTML=ROOMS_D.map(r=>`
    ${r.ico}
    ${r.n}
    ${r.desc}
    ${r.lvRange} Prize: ${r.prize} ${r.entry!=='Free'?`Entry: ${r.entry}`:''}
    `).join(''); } function enterRoom(roomName){ cls('mpOvl'); const opp=AI_OPP[Math.min(Math.floor(P.level/2),AI_OPP.length-1)]; const avail=CASES.filter(c=>c.minLv<=Math.max(1,P.level)); if(!avail.length){toast('No cases available at your level yet.');return;} const randomCase=avail[Math.floor(Math.random()*avail.length)]; toast(`⚔️ Matched vs ${opp.av} ${opp.n} in ${roomName}!`); logAct(`Entered ${roomName} vs ${opp.n}`); setTimeout(()=>{ curCase=randomCase;chosenSide=null; openSide(randomCase.id); setTimeout(()=>{document.getElementById('opp-side-tag').textContent=`🆚 ${opp.av} ${opp.n}`;},600); },800); } // ═══════════════════════════════════════════════════ // SKILLS // ═══════════════════════════════════════════════════ function renderSkills(){ document.getElementById('sklist').innerHTML=SKILLS_D.map(s=>{ const lv=P.skills[s.k]||1; return`
    ${s.ico} ${s.n}
    ${lv}/10
    `; }).join(''); } function upgSkill(k){ if(P.coins<200){toast('Need 🪙 200!');return;} if(P.skills[k]>=10){toast('Already maxed!');return;} P.coins-=200;P.skills[k]++; const s=SKILLS_D.find(x=>x.k===k); toast(`${s.n} upgraded to Lv${P.skills[k]}!`);logAct(`Upgraded ${s.n} to Lv${P.skills[k]}`); achCheck();updateHUD();renderSkills(); } // ═══════════════════════════════════════════════════ // PROFILE // ═══════════════════════════════════════════════════ function renderProfile(){ const cp=CAREER_PATHS.find(c=>c.id===P.careerPath); document.getElementById('pname').textContent=P.name; document.getElementById('pcar').textContent=`${careerTitle()} · ${P.city} · ${cp?.n||'General Practitioner'}`; document.getElementById('pelo').textContent=`ELO ${P.elo}`; document.getElementById('pdp').textContent=`⚡ ${P.devPoints} Dev Points`; const attrKeys=['intelligence','communication','negotiation','ethics','research','advocacy','leadership']; document.getElementById('patgrid').innerHTML=attrKeys.map(k=> `
    ${k[0].toUpperCase()+k.slice(1)}${P.attrs[k]||5}
    `).join(''); document.getElementById('pladder').innerHTML=CAREER.map((c,i)=>{ const done=i
    ${c.t} ${cur?'CURRENT':''} `; }).join(''); } // ═══════════════════════════════════════════════════ // LEADERBOARD // ═══════════════════════════════════════════════════ function renderLB(){ const all=[...LB_SEED.map(e=>({n:e.n,lv:e.lv,co:e.co,elo:e.elo,ch:e.ch,me:false})), {n:`${P.name} (You)`,lv:P.level,co:P.coins,elo:P.elo,ch:P.chambers||'—',me:true}] .sort((a,b)=>b.elo-a.elo); document.getElementById('lbcont').innerHTML=all.map((e,i)=>`
    ${['🥇','🥈','🥉'][i]||('#'+(i+1))}
    ${e.n}
    ${e.ch}
    🪙${e.co.toLocaleString()}
    ⚔️${e.elo}
    `).join(''); } // ═══════════════════════════════════════════════════ // BOOT // ═══════════════════════════════════════════════════ window.addEventListener('DOMContentLoaded',()=>{ document.addEventListener('keydown',e=>{if(e.key==='Escape')document.querySelectorAll('.ovl').forEach(o=>o.classList.remove('on'));}); });