MosswartOverlord/static/v2/assets/RadarWindow-zG0PV0Ia.js
Erik d2c30b610b fix(v2): character window now updates live from WebSocket
The CharacterWindow only fetched once from API on mount and never
updated. Now:
- character_stats WS messages are tracked in useLiveData via ref
- Passed through WindowRenderer to CharacterWindow as liveStats prop
- Window uses live WS data when available, falls back to API fetch
- Attributes, skills, vitals base values, properties (augmentations,
  ratings, etc.), allegiance all update in real-time

Also: vitals bars in the character window use live WS vitals data
(health_percentage etc.) for real-time HP/Stamina/Mana display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:02:49 +02:00

1 line
7.5 KiB
JavaScript

import{r as x,j as c,D as ne}from"./index-fhoLMRWN.js";import"./react-yfL0ty4i.js";const q=300,H=.5,se={walls:{r:0,g:0,b:255},innerWalls:{r:127,g:127,b:255},rampedWalls:{r:77,g:255,b:255},floors:{r:0,g:127,b:255},stairs:{r:0,g:63,b:255}},le={walls:{r:140,g:140,b:180},innerWalls:{r:100,g:100,b:140},rampedWalls:{r:120,g:160,b:120},floors:{r:60,g:80,b:60},stairs:{r:180,g:160,b:80}};function oe(d){const g=document.createElement("canvas");g.width=10,g.height=10;const w=g.getContext("2d");w.drawImage(d,0,0,10,10);const a=w.getImageData(0,0,10,10),t=a.data;for(let p=0;p<t.length;p+=4){const _=t[p],R=t[p+1],k=t[p+2];if(_>240&&R>240&&k>240){t[p+3]=0;continue}let E=!1;for(const[L,I]of Object.entries(se))if(Math.abs(_-I.r)<15&&Math.abs(R-I.g)<15&&Math.abs(k-I.b)<15){const W=le[L];t[p]=W.r,t[p+1]=W.g,t[p+2]=W.b,E=!0;break}!E&&_<15&&R<15&&k<15&&(t[p+3]=100)}return w.putImageData(a,0,0),g}function ie(d){return d===1?Math.PI:d<-.7&&d>-.8?Math.PI/2:d>.7&&d<.8?-Math.PI/2:0}let A=null;function ce(){A||(A={},fetch("/dungeon_tiles.json").then(d=>d.json()).then(d=>{Object.entries(d).forEach(([g,w])=>{const a=new Image;a.onload=()=>{A[g]=oe(a)},a.src=w})}).catch(()=>{}))}const K={Monster:"#ff4444",Player:"#4488ff",NPC:"#44cc44",Vendor:"#44cc44",Portal:"#aa44ff",Corpse:"#ff8800",Container:"#cccc44",Door:"#888888"};function re(d){const g=(d%360+360)%360;return["N","NE","E","SE","S","SW","W","NW"][Math.round(g/45)%8]}const fe=({id:d,charName:g,zIndex:w,socket:a,radarData:t})=>{const p=x.useRef(null),_=x.useRef(H),[R,k]=x.useState(H),[E,L]=x.useState(null),I=x.useRef(null),W=x.useRef([]);x.useEffect(()=>{const n=new Image;n.src="/dereth.png",n.onload=()=>{I.current=n},ce()},[]),x.useEffect(()=>((a==null?void 0:a.readyState)===WebSocket.OPEN&&a.send(JSON.stringify({player_name:g,command:"start_radar"})),()=>{(a==null?void 0:a.readyState)===WebSocket.OPEN&&a.send(JSON.stringify({player_name:g,command:"stop_radar"}))}),[g,a]);const Q=x.useCallback(n=>{n.preventDefault();const e=n.deltaY>0?1.25:.8;_.current=Math.max(.02,Math.min(5,_.current*e)),k(_.current)},[]),D=x.useCallback(n=>{const e=p.current;if(!e)return;const o=e.getBoundingClientRect(),s=(n.clientX-o.left)*(e.width/o.width),r=(n.clientY-o.top)*(e.height/o.height);let M=null,u=20;W.current.forEach(f=>{if(f._px===void 0)return;const S=Math.sqrt((s-f._px)**2+(r-f._py)**2);S<u&&(u=S,M=f)}),L(M?M.id:null)},[]);x.useEffect(()=>{var G;const n=p.current;if(!n||!t)return;const e=n.getContext("2d");if(!e)return;const o=q,s=o/2,r=o/2,M=t.objects??[],u=t.player_ew??0,f=t.player_ns??0,S=t.player_heading??0,b=t.is_dungeon??!1,V=t.player_x??0,Z=t.player_y??0,Y=_.current,z=b?o/2/(Y*240):o/2/Y,j=S*Math.PI/180;e.clearRect(0,0,o,o),e.fillStyle="#111",e.beginPath(),e.arc(s,r,s,0,Math.PI*2),e.fill(),e.save(),e.beginPath(),e.arc(s,r,s-1,0,Math.PI*2),e.clip();const B=t.landblock??null,te=t.player_raw_z??0;if(b&&B&&((G=window.__dungeonMapCache)!=null&&G[B])){const l=window.__dungeonMapCache[B],h=Math.floor((te+3)/6)*6;e.translate(s,r),e.rotate(-(S-180)*Math.PI/180);const i=10*z,O=A&&Object.keys(A).length>0;(l.z_levels||[]).slice().sort((y,m)=>(y.z===h?1:0)-(m.z===h?1:0)).forEach(y=>{const m=y.z===h;e.globalAlpha=m?.85:.12,(y.cells||[]).forEach(T=>{const P=-(T.x-V)*z,N=(T.y-Z)*z,C=O?A[String(T.env_id)]:null;C?(e.save(),e.translate(P,N),e.rotate(ie(T.rotation)),e.drawImage(C,-i/2,-i/2,i,i),e.restore()):(e.fillStyle=m?"#3a5a3a":"#1a2a1a",e.fillRect(P-i/2,N-i/2,i,i))})}),e.globalAlpha=1,e.setTransform(1,0,0,1,0,0)}else if(!b&&I.current){const l=I.current,h=l.naturalWidth/204.2,i=(u+102.1)*h,O=(102.1-f)*h;e.globalAlpha=.4,e.save(),e.translate(s,r),e.rotate(-j);const v=Y*h*2;e.drawImage(l,i-v/2,O-v/2,v,v,-s,-r,o,o),e.restore(),e.globalAlpha=1}e.restore(),e.strokeStyle="#333",e.lineWidth=1;for(let l=1;l<=4;l++)e.beginPath(),e.arc(s,r,s/4*l,0,Math.PI*2),e.stroke();e.beginPath(),e.moveTo(s,0),e.lineTo(s,o),e.moveTo(0,r),e.lineTo(o,r),e.stroke(),e.font="bold 12px monospace",e.textAlign="center",e.textBaseline="middle",[{l:"N",a:0},{l:"E",a:Math.PI/2},{l:"S",a:Math.PI},{l:"W",a:-Math.PI/2}].forEach(({l,a:h})=>{const i=h-j;e.fillStyle=l==="N"?"#cc4444":"#888",e.fillText(l,s+Math.sin(i)*(s-12),r-Math.cos(i)*(s-12))}),e.strokeStyle="#666",e.lineWidth=1,e.beginPath(),e.moveTo(s,r),e.lineTo(s,r-s*.85),e.stroke();const $=b?Math.PI-j:j,X=Math.cos($),F=Math.sin($);M.forEach(l=>{let h,i;b&&l.raw_x!==void 0?(h=-(l.raw_x-V),i=l.raw_y-Z):(h=(l.ew??0)-u,i=(l.ns??0)-f);const O=h*X-i*F,v=b?h*F+i*X:-(h*F+i*X),y=s+O*z,m=r+v*z;if(Math.sqrt((y-s)**2+(m-r)**2)>s-4)return;l._px=y,l._py=m;const P=l.object_class??l.type??"",N=K[P]??"#888",C=l.id===E,J=C?6:P==="Monster"||P==="Player"?4:3;C&&(e.strokeStyle="#fff",e.lineWidth=2,e.beginPath(),e.arc(y,m,J+3,0,Math.PI*2),e.stroke()),e.fillStyle=N,e.beginPath(),e.arc(y,m,J,0,Math.PI*2),e.fill(),(P==="Player"||P==="Portal"||C)&&(e.fillStyle=C?"#fff":N,e.font="9px monospace",e.textAlign="left",e.fillText(l.name,y+6,m+3))}),W.current=M,e.fillStyle="#ffcc00",e.beginPath(),e.arc(s,r,5,0,Math.PI*2),e.fill(),e.strokeStyle="#fff",e.lineWidth=1,e.stroke()},[t,R,E]);const U=((t==null?void 0:t.objects)??[]).map(n=>{const e=(t==null?void 0:t.player_ew)??0,o=(t==null?void 0:t.player_ns)??0,s=(t==null?void 0:t.is_dungeon)??!1,r=(t==null?void 0:t.player_x)??0,M=(t==null?void 0:t.player_y)??0;let u,f,S;s&&n.raw_x!==void 0?(u=-(n.raw_x-r),f=n.raw_y-M,S=Math.sqrt(u*u+f*f)):(u=(n.ew??0)-e,f=(n.ns??0)-o,S=Math.sqrt(u*u+f*f)*240);const b=Math.atan2(u,f)*180/Math.PI;return{...n,dist:S,dir:re(b)}}).sort((n,e)=>n.dist-e.dist),ee=Math.round(R*240);return c.jsxs(ne,{id:d,title:`Radar: ${g}`,zIndex:w,width:360,height:560,children:[c.jsxs("div",{style:{padding:"4px 8px",display:"flex",justifyContent:"space-between",fontSize:"0.75rem",color:"#888",borderBottom:"1px solid #333",background:"#1a1a1a"},children:[c.jsxs("span",{children:["Range: ~",ee,"m"]}),c.jsx("span",{style:{fontSize:"0.65rem",color:"#555"},children:"Scroll to zoom"})]}),c.jsx("canvas",{ref:p,width:q,height:q,style:{display:"block",margin:"0 auto",borderBottom:"1px solid #333",cursor:"crosshair",flexShrink:0},onWheel:Q,onClick:D}),c.jsxs("div",{style:{flex:1,overflowY:"auto",fontSize:"0.72rem",minHeight:0},children:[c.jsxs("div",{style:{display:"flex",padding:"3px 6px",borderBottom:"1px solid #333",color:"#666",fontSize:"0.65rem",fontWeight:600},children:[c.jsx("span",{style:{width:8}}),c.jsx("span",{style:{flex:1,marginLeft:6},children:"Name"}),c.jsx("span",{style:{width:55,textAlign:"left"},children:"Type"}),c.jsx("span",{style:{width:40,textAlign:"right"},children:"Dist"}),c.jsx("span",{style:{width:24,textAlign:"center"},children:"Dir"})]}),U.length===0&&c.jsx("div",{style:{padding:12,color:"#555",textAlign:"center",fontSize:"0.7rem"},children:"Waiting for radar data..."}),U.map(n=>{const e=n.object_class??n.type??"",o=K[e]??"#888",s=n.id===E;return c.jsxs("div",{onClick:()=>L(s?null:n.id),style:{display:"flex",alignItems:"center",padding:"2px 6px",borderBottom:"1px solid #1a1a1a",cursor:"pointer",color:"#ccc",background:s?"#1a2a3a":"",borderLeft:s?"2px solid #4488ff":"2px solid transparent"},children:[c.jsx("span",{style:{width:8,height:8,borderRadius:"50%",background:o,flexShrink:0}}),c.jsx("span",{style:{flex:1,marginLeft:6,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:n.name}),c.jsx("span",{style:{width:55,color:"#888",fontSize:"0.65rem"},children:e}),c.jsx("span",{style:{width:40,textAlign:"right",fontVariantNumeric:"tabular-nums"},children:n.dist<1e3?`${Math.round(n.dist)}m`:`${(n.dist/1e3).toFixed(1)}km`}),c.jsx("span",{style:{width:24,textAlign:"center",color:"#666"},children:n.dir})]},n.id)})]})]})};export{fe as RadarWindow};