tycono 0.1.94-beta.5 → 0.1.94-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.94-beta.5",
3
+ "version": "0.1.94-beta.6",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -743,8 +743,13 @@ function buildSupervisionSection(node: OrgNode): string {
743
743
  - ✅ **Peer consult**: Unsure about business/market direction? → \`python3 "$CONSULT_CMD" cbo "question"\`
744
744
  - ⚠️ **Course correct**: Wrong direction → \`python3 "$SUPERVISION_CMD" amend <ses-id> "new instruction"\`
745
745
  - 🛑 **Abort**: Seriously wrong → \`python3 "$SUPERVISION_CMD" abort <ses-id> --reason "why"\`
746
- - ✅ **All done**: Compile results and report to your superior
746
+ - ✅ **All done?** Before reporting done, **verify deliverables** (see Quality Gate below)
747
747
  4. **Repeat** watch until all subordinates complete. Do NOT stop after one tick.
748
+ 5. **Quality Gate**: When subordinates report done, **read their actual output**:
749
+ - Check files exist and are non-trivial
750
+ - Verify key requirements from your task are met
751
+ - If gaps found → re-dispatch with specific feedback: "Missing X, Y, Z. Continue."
752
+ - There is NO time limit. Iterate until the work truly meets the requirements.
748
753
 
749
754
  ## Supervision Commands
750
755
 
@@ -56,6 +56,7 @@ sessionsRouter.patch('/:id', (req, res) => {
56
56
 
57
57
  /* DELETE /api/sessions — bulk delete (body: { ids }) or ?empty=true */
58
58
  sessionsRouter.delete('/', (req, res) => {
59
+ console.log(`[Sessions] DELETE / called (empty=${req.query.empty}, origin=${req.headers.origin ?? req.headers.referer ?? 'unknown'})`);
59
60
  if (req.query.empty === 'true') {
60
61
  const result = deleteEmpty();
61
62
  res.json(result);
@@ -72,6 +73,7 @@ sessionsRouter.delete('/', (req, res) => {
72
73
 
73
74
  /* DELETE /api/sessions/:id — delete session */
74
75
  sessionsRouter.delete('/:id', (req, res) => {
76
+ console.log(`[Sessions] DELETE /${req.params.id} called (origin=${req.headers.origin ?? req.headers.referer ?? 'unknown'})`);
75
77
  const ok = deleteSession(req.params.id);
76
78
  if (!ok) {
77
79
  res.status(404).json({ error: 'Session not found' });
@@ -254,10 +254,16 @@ export function updateSession(id: string, updates: Partial<Pick<Session, 'title'
254
254
  return session;
255
255
  }
256
256
 
257
- export function deleteSession(id: string): boolean {
257
+ export function deleteSession(id: string, force = false): boolean {
258
258
  const session = cache.get(id);
259
259
  if (!session) return false;
260
260
 
261
+ // BUG-008 fix: protect wave sessions from accidental deletion
262
+ if (session.waveId && !force) {
263
+ console.warn(`[SessionStore] BLOCKED deletion of wave session ${id} (waveId=${session.waveId}, roleId=${session.roleId}). Use force=true to override.`);
264
+ return false;
265
+ }
266
+
261
267
  console.log(`[SessionStore] Deleting session ${id} (roleId=${session.roleId}, waveId=${session.waveId ?? 'none'}, messages=${session.messages.length})`);
262
268
  cache.delete(id);
263
269
  const p = sessionPath(id);
@@ -294,12 +294,32 @@ If new CEO directives arrive mid-execution, they will appear in your supervision
294
294
  marked as [CEO DIRECTIVE]. These are PRIORITY 1 — process before anything else.
295
295
  ${recoveryContext}
296
296
 
297
+ ## Quality Gate (CRITICAL — G-09)
298
+ ⛔ **"Subordinate said done" ≠ "Work is actually done."**
299
+ Before declaring yourself done, you MUST verify the deliverables meet the directive's requirements:
300
+
301
+ 1. **Read the actual output files** — don't trust status reports. Check the code, docs, or artifacts yourself.
302
+ 2. **Test if it works** — if the directive asks for a working game/app, check if it actually runs.
303
+ 3. **Count against requirements** — if the directive says "15 monsters, 7 maps", count them.
304
+ 4. **If quality is insufficient → re-dispatch** with specific feedback:
305
+ - "You implemented 4/11 systems. Still missing: NPC dialogue, inventory, capture. Continue."
306
+ - "The game doesn't load in browser. Fix the entry point and test."
307
+ 5. **Iterate until the directive is truly fulfilled.** There is NO time limit.
308
+ A half-finished deliverable is worse than taking 2-3 hours to get it right.
309
+
310
+ Re-dispatch pattern:
311
+ - dispatch same C-Level with specific gaps identified
312
+ - Each iteration should close specific gaps, not redo everything
313
+ - Maximum 5 iterations per C-Level before escalating
314
+
297
315
  ## Instructions
298
316
  1. Analyze the directive and decide which C-Level roles to dispatch (not necessarily all)
299
317
  2. Dispatch them with clear tasks
300
318
  3. Enter supervision watch loop
301
- 4. Monitor, **actively relay results between teams**, course-correct, until all done
302
- 5. Compile results and report`;
319
+ 4. Monitor, **actively relay results between teams**, course-correct
320
+ 5. When subordinates report done → **verify deliverables against requirements (G-09)**
321
+ 6. If gaps exist → re-dispatch with specific feedback. Repeat 3-5.
322
+ 7. Only when ALL requirements are met → compile results and report`;
303
323
 
304
324
  // Create supervisor session
305
325
  const session = createSession('ceo', {
@@ -1 +1 @@
1
- import{i as $,k as T,l as R,m as F,n as S,o as A,p as P,r as i,j as a}from"./index-DyFUdV3e.js";const B=[{x:1,y:19,w:10,h:2,c:"#100A06",a:.15},{x:2,y:15,w:3,h:4,c:"$pants"},{x:7,y:15,w:3,h:4,c:"$pants"},{x:2,y:19,w:3,h:2,c:"$shoes"},{x:7,y:19,w:3,h:2,c:"$shoes"},{x:2,y:19,w:3,h:1,c:"lighten($shoes, 20)",a:.4},{x:7,y:19,w:3,h:1,c:"lighten($shoes, 20)",a:.4}],L=[{x:4,y:8,w:4,h:3,c:"$skin"},{x:1,y:1,w:10,h:8,c:"$skin"},{x:3,y:4,w:2,h:2,c:"#1A1A2E"},{x:7,y:4,w:2,h:2,c:"#1A1A2E"},{x:3,y:4,w:1,h:1,c:"#FFF",a:.35},{x:7,y:4,w:1,h:1,c:"#FFF",a:.35},{x:5,y:7,w:2,h:1,c:"darken($skin, 25)",a:.4},{x:0,y:4,w:1,h:1,c:"$skin"},{x:11,y:4,w:1,h:1,c:"$skin"}];function y(s,t,e,c,r,o){for(const l of t){const d=F(l.c,o);l.a!==void 0&&l.a!==1&&(s.globalAlpha=l.a),s.fillStyle=d,s.fillRect((l.x+c)*e,(l.y+r)*e,l.w*e,l.h*e),l.a!==void 0&&l.a!==1&&(s.globalAlpha=1)}}function O(s){var e;const t=A(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:A("short").layer.pixels}function D(s){var e;const t=S(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:S("tshirt").layer.pixels}function I(s){var e;const t=P(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:[]}function k(s,t){const e=(t==null?void 0:t.scale)??3,c=(t==null?void 0:t.padX)??2,r=(t==null?void 0:t.padY)??4,o=1,l=12+c*2,d=22+r+o,n=document.createElement("canvas");n.width=l*e,n.height=d*e,n.style.imageRendering="pixelated";const u=n.getContext("2d"),p=c,x=r;y(u,B,e,p,x,s);const h=s.outfitStyle||"tshirt";y(u,D(h),e,p,x,s),y(u,L,e,p,x,s);const f=s.hairStyle||"short";y(u,O(f),e,p,x,s);const j=s.accessory||"none";return y(u,I(j),e,p,x,s),n}const w=$().map(s=>s.id),_=T().map(s=>s.id),E=_,H=R().map(s=>s.id),C=["#FFE0BD","#F1C27D","#E0AC69","#C68642","#8D5524","#6B4423"],b=["#2C1B18","#724133","#C68642","#E6BE8A","#D4AF37","#B94E48"];function N(){return"#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0")}function g(s){return s[Math.floor(Math.random()*s.length)]}function G({onComplete:s}){const t=i.useRef(null),[e,c]=i.useState("플레이어"),[r,o]=i.useState({skinColor:C[0],hairColor:b[0],shirtColor:"#3498db",pantsColor:"#2C3E50",shoeColor:"#34495E",hairStyle:"short",outfitStyle:"tshirt",accessory:"none"});i.useEffect(()=>{if(!t.current)return;t.current.innerHTML="";const n=k(r,{scale:6});t.current.appendChild(n)},[r]);const l=()=>{o({skinColor:g(C),hairColor:g(b),shirtColor:N(),pantsColor:N(),shoeColor:N(),hairStyle:g(w),outfitStyle:g(E),accessory:g(H)})},d=()=>{s({appearance:r,stats:{name:e,level:1,hp:100,maxHp:100,mp:80,maxMp:80,attack:20,defense:10}})};return a.jsxs("div",{className:"character-creator",children:[a.jsx("h1",{children:"캐릭터 생성"}),a.jsxs("div",{className:"creator-layout",children:[a.jsx("div",{className:"preview-section",children:a.jsx("div",{className:"preview-canvas",ref:t})}),a.jsxs("div",{className:"customization-section",children:[a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"이름"}),a.jsx("input",{type:"text",value:e,onChange:n=>c(n.target.value),maxLength:10})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"피부색"}),a.jsx("div",{className:"color-palette",children:C.map(n=>a.jsx("button",{className:`color-btn ${r.skinColor===n?"active":""}`,style:{backgroundColor:n},onClick:()=>o({...r,skinColor:n})},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"헤어스타일"}),a.jsx("select",{value:r.hairStyle,onChange:n=>o({...r,hairStyle:n.target.value}),children:w.map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"헤어 색상"}),a.jsx("div",{className:"color-palette",children:b.map(n=>a.jsx("button",{className:`color-btn ${r.hairColor===n?"active":""}`,style:{backgroundColor:n},onClick:()=>o({...r,hairColor:n})},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"의상"}),a.jsx("select",{value:r.outfitStyle,onChange:n=>o({...r,outfitStyle:n.target.value}),children:E.map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"액세서리"}),a.jsx("select",{value:r.accessory,onChange:n=>o({...r,accessory:n.target.value}),children:H.slice(0,15).map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"button-group",children:[a.jsx("button",{onClick:l,className:"btn-secondary",children:"🎲 랜덤 생성"}),a.jsx("button",{onClick:d,className:"btn-primary",children:"⚔️ 게임 시작!"})]})]})]})]})}const m={attack:{name:"공격",emoji:"⚔️"},defend:{name:"방어",emoji:"🛡️"},heal:{name:"회복",mpCost:20,emoji:"💚"},flee:{name:"도망",emoji:"🏃"}};function Y(){const s=[{name:"슬라임",hp:50,attack:8,defense:3},{name:"고블린",hp:70,attack:12,defense:5},{name:"해골",hp:60,attack:15,defense:2},{name:"오크",hp:100,attack:18,defense:8}],t=s[Math.floor(Math.random()*s.length)];return{name:t.name,level:1,hp:t.hp,maxHp:t.hp,mp:0,maxMp:0,attack:t.attack,defense:t.defense}}function M(s,t,e){const c=s.attack-t.defense/2,r=Math.max(1,Math.floor(c));return e?Math.floor(r*.5):r}function z(s,t){const e={...s};if(t==="heal"&&e.player.mp<m.heal.mpCost)return{newState:e,continueToEnemyTurn:!1};let c;switch(t){case"attack":{const r=M(e.player,e.enemy,!1);e.enemy.hp=Math.max(0,e.enemy.hp-r),c={actorName:e.player.name,skill:"attack",damage:r,message:`${e.player.name}의 공격! ${r} 데미지!`},e.defendActive=!1;break}case"defend":{e.defendActive=!0,c={actorName:e.player.name,skill:"defend",message:`${e.player.name}이(가) 방어 태세를 취했다!`};break}case"heal":{const o=Math.min(30,e.player.maxHp-e.player.hp);e.player.hp=Math.min(e.player.maxHp,e.player.hp+30),e.player.mp-=m.heal.mpCost,c={actorName:e.player.name,skill:"heal",heal:o,message:`${e.player.name}이(가) 회복! HP +${o}`},e.defendActive=!1;break}case"flee":{Math.random()<.5?(e.status="fled",c={actorName:e.player.name,skill:"flee",message:`${e.player.name}이(가) 도망쳤다!`}):c={actorName:e.player.name,skill:"flee",message:"도망에 실패했다!"},e.defendActive=!1;break}}return e.log=[...e.log,c],e.enemy.hp<=0?(e.status="victory",{newState:e,continueToEnemyTurn:!1}):e.status==="fled"?{newState:e,continueToEnemyTurn:!1}:{newState:e,continueToEnemyTurn:!0}}function K(s){const t={...s},e=M(t.enemy,t.player,t.defendActive);t.player.hp=Math.max(0,t.player.hp-e);const c={actorName:t.enemy.name,skill:"attack",damage:e,message:`${t.enemy.name}의 공격! ${e} 데미지!`};return t.log=[...t.log,c],t.defendActive=!1,t.player.hp<=0&&(t.status="defeat"),t}function U(s,t){return{player:s,enemy:t,turn:"player",log:[],defendActive:!1,status:"ongoing"}}function W(){return{skinColor:"#6B8E23",hairColor:"#2F4F2F",shirtColor:"#8B4513",pantsColor:"#654321",shoeColor:"#3E2723",hairStyle:"messy",outfitStyle:"vest",accessory:"horns"}}function X({player:s,onRestart:t}){const e=i.useRef(null),c=i.useRef(null),r=i.useRef(null),[o]=i.useState(W()),[l,d]=i.useState(()=>U(s.stats,Y()));i.useEffect(()=>{if(e.current){e.current.innerHTML="";const h=k(s.appearance,{scale:6});e.current.appendChild(h)}if(c.current){c.current.innerHTML="";const h=k(o,{scale:6});c.current.appendChild(h)}},[s.appearance,o]),i.useEffect(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[l.log]);const n=h=>{if(l.status!=="ongoing")return;const{newState:f,continueToEnemyTurn:j}=z(l,h);d(f),j&&f.status==="ongoing"&&setTimeout(()=>{d(v=>v.status!=="ongoing"?v:K(v))},800)},u=l.player.hp/l.player.maxHp*100,p=l.enemy.hp/l.enemy.maxHp*100,x=l.player.mp/l.player.maxMp*100;return a.jsxs("div",{className:"battle-screen",children:[a.jsx("h1",{children:"⚔️ 배틀!"}),a.jsxs("div",{className:"battle-characters",children:[a.jsxs("div",{className:"character-box",children:[a.jsx("div",{className:"character-canvas",ref:e}),a.jsx("div",{className:"character-name",children:l.player.name}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["HP: ",l.player.hp,"/",l.player.maxHp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill hp",style:{width:`${u}%`}})})]}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["MP: ",l.player.mp,"/",l.player.maxMp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill mp",style:{width:`${x}%`}})})]})]}),a.jsx("div",{className:"vs-text",children:"VS"}),a.jsxs("div",{className:"character-box",children:[a.jsx("div",{className:"character-canvas",ref:c}),a.jsx("div",{className:"character-name",children:l.enemy.name}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["HP: ",l.enemy.hp,"/",l.enemy.maxHp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill hp",style:{width:`${p}%`}})})]})]})]}),a.jsxs("div",{className:"battle-log",ref:r,children:[l.log.length===0&&a.jsx("div",{className:"log-entry",children:"배틀 시작!"}),l.log.map((h,f)=>a.jsxs("div",{className:"log-entry",children:["> ",h.message]},f))]}),l.status!=="ongoing"&&a.jsxs("div",{className:"battle-result",children:[l.status==="victory"&&a.jsx("h2",{children:"🎉 승리!"}),l.status==="defeat"&&a.jsx("h2",{children:"💀 패배..."}),l.status==="fled"&&a.jsx("h2",{children:"🏃 도망쳤다!"}),a.jsx("button",{onClick:t,className:"btn-primary",children:"다시 시작"})]}),l.status==="ongoing"&&a.jsxs("div",{className:"skill-buttons",children:[a.jsxs("button",{onClick:()=>n("attack"),className:"skill-btn",children:[m.attack.emoji," ",m.attack.name]}),a.jsxs("button",{onClick:()=>n("defend"),className:"skill-btn",children:[m.defend.emoji," ",m.defend.name]}),a.jsxs("button",{onClick:()=>n("heal"),className:"skill-btn",disabled:l.player.mp<m.heal.mpCost,children:[m.heal.emoji," ",m.heal.name,a.jsxs("span",{className:"mp-cost",children:["(MP ",m.heal.mpCost,")"]})]}),a.jsxs("button",{onClick:()=>n("flee"),className:"skill-btn",children:[m.flee.emoji," ",m.flee.name]})]})]})}function q(){const[s,t]=i.useState("create"),[e,c]=i.useState(null),r=l=>{c(l),t("battle")},o=()=>{c(null),t("create")};return a.jsxs("div",{className:"rpg-game",children:[s==="create"&&a.jsx(G,{onComplete:r}),s==="battle"&&e&&a.jsx(X,{player:e,onRestart:o})]})}export{q as default};
1
+ import{i as $,k as T,l as R,m as F,n as S,o as A,p as P,r as i,j as a}from"./index-BkPv-nh1.js";const B=[{x:1,y:19,w:10,h:2,c:"#100A06",a:.15},{x:2,y:15,w:3,h:4,c:"$pants"},{x:7,y:15,w:3,h:4,c:"$pants"},{x:2,y:19,w:3,h:2,c:"$shoes"},{x:7,y:19,w:3,h:2,c:"$shoes"},{x:2,y:19,w:3,h:1,c:"lighten($shoes, 20)",a:.4},{x:7,y:19,w:3,h:1,c:"lighten($shoes, 20)",a:.4}],L=[{x:4,y:8,w:4,h:3,c:"$skin"},{x:1,y:1,w:10,h:8,c:"$skin"},{x:3,y:4,w:2,h:2,c:"#1A1A2E"},{x:7,y:4,w:2,h:2,c:"#1A1A2E"},{x:3,y:4,w:1,h:1,c:"#FFF",a:.35},{x:7,y:4,w:1,h:1,c:"#FFF",a:.35},{x:5,y:7,w:2,h:1,c:"darken($skin, 25)",a:.4},{x:0,y:4,w:1,h:1,c:"$skin"},{x:11,y:4,w:1,h:1,c:"$skin"}];function y(s,t,e,c,r,o){for(const l of t){const d=F(l.c,o);l.a!==void 0&&l.a!==1&&(s.globalAlpha=l.a),s.fillStyle=d,s.fillRect((l.x+c)*e,(l.y+r)*e,l.w*e,l.h*e),l.a!==void 0&&l.a!==1&&(s.globalAlpha=1)}}function O(s){var e;const t=A(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:A("short").layer.pixels}function D(s){var e;const t=S(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:S("tshirt").layer.pixels}function I(s){var e;const t=P(s);return t?(e=t.directions)!=null&&e.down?t.directions.down.pixels:t.layer.pixels:[]}function k(s,t){const e=(t==null?void 0:t.scale)??3,c=(t==null?void 0:t.padX)??2,r=(t==null?void 0:t.padY)??4,o=1,l=12+c*2,d=22+r+o,n=document.createElement("canvas");n.width=l*e,n.height=d*e,n.style.imageRendering="pixelated";const u=n.getContext("2d"),p=c,x=r;y(u,B,e,p,x,s);const h=s.outfitStyle||"tshirt";y(u,D(h),e,p,x,s),y(u,L,e,p,x,s);const f=s.hairStyle||"short";y(u,O(f),e,p,x,s);const j=s.accessory||"none";return y(u,I(j),e,p,x,s),n}const w=$().map(s=>s.id),_=T().map(s=>s.id),E=_,H=R().map(s=>s.id),C=["#FFE0BD","#F1C27D","#E0AC69","#C68642","#8D5524","#6B4423"],b=["#2C1B18","#724133","#C68642","#E6BE8A","#D4AF37","#B94E48"];function N(){return"#"+Math.floor(Math.random()*16777215).toString(16).padStart(6,"0")}function g(s){return s[Math.floor(Math.random()*s.length)]}function G({onComplete:s}){const t=i.useRef(null),[e,c]=i.useState("플레이어"),[r,o]=i.useState({skinColor:C[0],hairColor:b[0],shirtColor:"#3498db",pantsColor:"#2C3E50",shoeColor:"#34495E",hairStyle:"short",outfitStyle:"tshirt",accessory:"none"});i.useEffect(()=>{if(!t.current)return;t.current.innerHTML="";const n=k(r,{scale:6});t.current.appendChild(n)},[r]);const l=()=>{o({skinColor:g(C),hairColor:g(b),shirtColor:N(),pantsColor:N(),shoeColor:N(),hairStyle:g(w),outfitStyle:g(E),accessory:g(H)})},d=()=>{s({appearance:r,stats:{name:e,level:1,hp:100,maxHp:100,mp:80,maxMp:80,attack:20,defense:10}})};return a.jsxs("div",{className:"character-creator",children:[a.jsx("h1",{children:"캐릭터 생성"}),a.jsxs("div",{className:"creator-layout",children:[a.jsx("div",{className:"preview-section",children:a.jsx("div",{className:"preview-canvas",ref:t})}),a.jsxs("div",{className:"customization-section",children:[a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"이름"}),a.jsx("input",{type:"text",value:e,onChange:n=>c(n.target.value),maxLength:10})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"피부색"}),a.jsx("div",{className:"color-palette",children:C.map(n=>a.jsx("button",{className:`color-btn ${r.skinColor===n?"active":""}`,style:{backgroundColor:n},onClick:()=>o({...r,skinColor:n})},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"헤어스타일"}),a.jsx("select",{value:r.hairStyle,onChange:n=>o({...r,hairStyle:n.target.value}),children:w.map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"헤어 색상"}),a.jsx("div",{className:"color-palette",children:b.map(n=>a.jsx("button",{className:`color-btn ${r.hairColor===n?"active":""}`,style:{backgroundColor:n},onClick:()=>o({...r,hairColor:n})},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"의상"}),a.jsx("select",{value:r.outfitStyle,onChange:n=>o({...r,outfitStyle:n.target.value}),children:E.map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"form-group",children:[a.jsx("label",{children:"액세서리"}),a.jsx("select",{value:r.accessory,onChange:n=>o({...r,accessory:n.target.value}),children:H.slice(0,15).map(n=>a.jsx("option",{value:n,children:n},n))})]}),a.jsxs("div",{className:"button-group",children:[a.jsx("button",{onClick:l,className:"btn-secondary",children:"🎲 랜덤 생성"}),a.jsx("button",{onClick:d,className:"btn-primary",children:"⚔️ 게임 시작!"})]})]})]})]})}const m={attack:{name:"공격",emoji:"⚔️"},defend:{name:"방어",emoji:"🛡️"},heal:{name:"회복",mpCost:20,emoji:"💚"},flee:{name:"도망",emoji:"🏃"}};function Y(){const s=[{name:"슬라임",hp:50,attack:8,defense:3},{name:"고블린",hp:70,attack:12,defense:5},{name:"해골",hp:60,attack:15,defense:2},{name:"오크",hp:100,attack:18,defense:8}],t=s[Math.floor(Math.random()*s.length)];return{name:t.name,level:1,hp:t.hp,maxHp:t.hp,mp:0,maxMp:0,attack:t.attack,defense:t.defense}}function M(s,t,e){const c=s.attack-t.defense/2,r=Math.max(1,Math.floor(c));return e?Math.floor(r*.5):r}function z(s,t){const e={...s};if(t==="heal"&&e.player.mp<m.heal.mpCost)return{newState:e,continueToEnemyTurn:!1};let c;switch(t){case"attack":{const r=M(e.player,e.enemy,!1);e.enemy.hp=Math.max(0,e.enemy.hp-r),c={actorName:e.player.name,skill:"attack",damage:r,message:`${e.player.name}의 공격! ${r} 데미지!`},e.defendActive=!1;break}case"defend":{e.defendActive=!0,c={actorName:e.player.name,skill:"defend",message:`${e.player.name}이(가) 방어 태세를 취했다!`};break}case"heal":{const o=Math.min(30,e.player.maxHp-e.player.hp);e.player.hp=Math.min(e.player.maxHp,e.player.hp+30),e.player.mp-=m.heal.mpCost,c={actorName:e.player.name,skill:"heal",heal:o,message:`${e.player.name}이(가) 회복! HP +${o}`},e.defendActive=!1;break}case"flee":{Math.random()<.5?(e.status="fled",c={actorName:e.player.name,skill:"flee",message:`${e.player.name}이(가) 도망쳤다!`}):c={actorName:e.player.name,skill:"flee",message:"도망에 실패했다!"},e.defendActive=!1;break}}return e.log=[...e.log,c],e.enemy.hp<=0?(e.status="victory",{newState:e,continueToEnemyTurn:!1}):e.status==="fled"?{newState:e,continueToEnemyTurn:!1}:{newState:e,continueToEnemyTurn:!0}}function K(s){const t={...s},e=M(t.enemy,t.player,t.defendActive);t.player.hp=Math.max(0,t.player.hp-e);const c={actorName:t.enemy.name,skill:"attack",damage:e,message:`${t.enemy.name}의 공격! ${e} 데미지!`};return t.log=[...t.log,c],t.defendActive=!1,t.player.hp<=0&&(t.status="defeat"),t}function U(s,t){return{player:s,enemy:t,turn:"player",log:[],defendActive:!1,status:"ongoing"}}function W(){return{skinColor:"#6B8E23",hairColor:"#2F4F2F",shirtColor:"#8B4513",pantsColor:"#654321",shoeColor:"#3E2723",hairStyle:"messy",outfitStyle:"vest",accessory:"horns"}}function X({player:s,onRestart:t}){const e=i.useRef(null),c=i.useRef(null),r=i.useRef(null),[o]=i.useState(W()),[l,d]=i.useState(()=>U(s.stats,Y()));i.useEffect(()=>{if(e.current){e.current.innerHTML="";const h=k(s.appearance,{scale:6});e.current.appendChild(h)}if(c.current){c.current.innerHTML="";const h=k(o,{scale:6});c.current.appendChild(h)}},[s.appearance,o]),i.useEffect(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[l.log]);const n=h=>{if(l.status!=="ongoing")return;const{newState:f,continueToEnemyTurn:j}=z(l,h);d(f),j&&f.status==="ongoing"&&setTimeout(()=>{d(v=>v.status!=="ongoing"?v:K(v))},800)},u=l.player.hp/l.player.maxHp*100,p=l.enemy.hp/l.enemy.maxHp*100,x=l.player.mp/l.player.maxMp*100;return a.jsxs("div",{className:"battle-screen",children:[a.jsx("h1",{children:"⚔️ 배틀!"}),a.jsxs("div",{className:"battle-characters",children:[a.jsxs("div",{className:"character-box",children:[a.jsx("div",{className:"character-canvas",ref:e}),a.jsx("div",{className:"character-name",children:l.player.name}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["HP: ",l.player.hp,"/",l.player.maxHp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill hp",style:{width:`${u}%`}})})]}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["MP: ",l.player.mp,"/",l.player.maxMp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill mp",style:{width:`${x}%`}})})]})]}),a.jsx("div",{className:"vs-text",children:"VS"}),a.jsxs("div",{className:"character-box",children:[a.jsx("div",{className:"character-canvas",ref:c}),a.jsx("div",{className:"character-name",children:l.enemy.name}),a.jsxs("div",{className:"stat-bar",children:[a.jsxs("div",{className:"stat-label",children:["HP: ",l.enemy.hp,"/",l.enemy.maxHp]}),a.jsx("div",{className:"stat-bar-bg",children:a.jsx("div",{className:"stat-bar-fill hp",style:{width:`${p}%`}})})]})]})]}),a.jsxs("div",{className:"battle-log",ref:r,children:[l.log.length===0&&a.jsx("div",{className:"log-entry",children:"배틀 시작!"}),l.log.map((h,f)=>a.jsxs("div",{className:"log-entry",children:["> ",h.message]},f))]}),l.status!=="ongoing"&&a.jsxs("div",{className:"battle-result",children:[l.status==="victory"&&a.jsx("h2",{children:"🎉 승리!"}),l.status==="defeat"&&a.jsx("h2",{children:"💀 패배..."}),l.status==="fled"&&a.jsx("h2",{children:"🏃 도망쳤다!"}),a.jsx("button",{onClick:t,className:"btn-primary",children:"다시 시작"})]}),l.status==="ongoing"&&a.jsxs("div",{className:"skill-buttons",children:[a.jsxs("button",{onClick:()=>n("attack"),className:"skill-btn",children:[m.attack.emoji," ",m.attack.name]}),a.jsxs("button",{onClick:()=>n("defend"),className:"skill-btn",children:[m.defend.emoji," ",m.defend.name]}),a.jsxs("button",{onClick:()=>n("heal"),className:"skill-btn",disabled:l.player.mp<m.heal.mpCost,children:[m.heal.emoji," ",m.heal.name,a.jsxs("span",{className:"mp-cost",children:["(MP ",m.heal.mpCost,")"]})]}),a.jsxs("button",{onClick:()=>n("flee"),className:"skill-btn",children:[m.flee.emoji," ",m.flee.name]})]})]})}function q(){const[s,t]=i.useState("create"),[e,c]=i.useState(null),r=l=>{c(l),t("battle")},o=()=>{c(null),t("create")};return a.jsxs("div",{className:"rpg-game",children:[s==="create"&&a.jsx(G,{onComplete:r}),s==="battle"&&e&&a.jsx(X,{player:e,onRestart:o})]})}export{q as default};