mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 16:05:12 +08:00
feat: 重构战报弹窗与模拟器视图,优化UI与逻辑
重构BattleReportDialog和BattleSimulatorView相关静态资源,替换旧版JS/CSS文件,提升界面一致性和交互体验。新增和优化空状态、滚动区域等通用UI组件,移除部分冗余composable,完善多语言内容。引入导弹逻辑,补充版本检测工具,提升整体代码结构和可维护性。
This commit is contained in:
1
docs/assets/BattleReportDialog-BNULWr3T.js
Normal file
1
docs/assets/BattleReportDialog-BNULWr3T.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/BattleSimulatorView-BMgbGRUI.js
Normal file
1
docs/assets/BattleSimulatorView-BMgbGRUI.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,7 +0,0 @@
|
||||
import{$t as e,At as t,Ct as n,Dn as r,En as i,Gn as a,Gt as o,Jt as s,Ln as c,Ot as ee,Qt as l,Vt as te,Xn as u,Xt as d,Yn as ne,Yt as f,Zt as p,_ as m,b as h,en as g,g as re,gn as _,jt as ie,kt as ae,m as oe,mn as v,p as se,qn as y,tn as ce,v as le,y as b}from"./game-logic-Bi1l7y4K.js";import"./vendor-pinia-DqhKuBjp.js";import"./vendor-crypto-CQM8pryk.js";import"./game-i18n-Dr0JspcV.js";import"./vendor-others-DTUzJ7S-.js";import"./vendor-reka-ui-BEHIKScj.js";import"./vendor-utils-BlvnUqQX.js";import"./vendor-vueuse-CJcfYqoW.js";import{B as ue,M as x}from"./vendor-icons-z9V6Jdbh.js";import{t as S}from"./CardDescription-BSLS07AU.js";import{d as C,v as de}from"./game-config-CG6z6nnH.js";import{D as fe,H as pe,K as w,N as T,W as E,_ as D,a as O,b as me,c as he,f as k,h as ge,i as A,l as j,o as M,p as N,q as P,r as F,s as I,u as L,v as _e,y as ve}from"./index-BT5w6et-.js";import{t as ye}from"./useGameConfig-b98LOtBY.js";import{t as be}from"./CardUnlockOverlay-DYfgGY6J.js";var xe={key:0,class:`container mx-auto p-4 sm:p-6`},Se={class:`text-2xl sm:text-3xl font-bold mb-4 sm:mb-6`},Ce={class:`mb-4 sm:mb-6 p-3 sm:p-4 bg-muted/50 rounded-lg border`},we={class:`flex items-center justify-between`},Te={class:`text-sm sm:text-base font-medium flex items-center gap-2`},Ee={class:`text-sm sm:text-base font-bold`},De={class:`mt-2`},Oe={class:`w-full bg-background rounded-full h-2.5 sm:h-3 overflow-hidden`},ke={class:`grid grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4`},Ae={class:`mb-2`},je={class:`flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2`},Me={class:`space-y-3`},R={class:`text-xs sm:text-sm space-y-1.5 sm:space-y-2`},Ne={class:`text-muted-foreground mb-1 sm:mb-2`},Pe={class:`space-y-1 sm:space-y-1.5`},Fe={class:`text-xs`},Ie={class:`text-xs sm:text-sm space-y-0.5 sm:space-y-1`},Le={class:`flex items-center gap-1.5 text-muted-foreground`},Re={class:`flex items-center gap-1.5 text-muted-foreground`},ze={key:1,class:`text-xs text-muted-foreground`},Be={class:`flex gap-2 flex-wrap`},Ve={key:0},z=ce({__name:`BuildingsView`,setup(ce){let z=P(),He=fe(),{t:B}=w(),{BUILDINGS:V,TECHNOLOGIES:Ue}=ye(),H=s(()=>z.currentPlanet),U=c(!1),W=c(``),G=c(``),K=c(!1),q=c(``),J=c(null),We=[{key:`metal`},{key:`crystal`},{key:`deuterium`},{key:`darkMatter`}],Ge=s(()=>H.value?Object.values(C).filter(e=>{let t=V.value[e];return H.value.isMoon?t.moonOnly===!0:t.moonOnly!==!0}):[]),Ke=e=>{if(!z.currentPlanet||!m(z.currentPlanet,e,z.player.technologies,z.player.officers).valid)return!1;let t=oe(z.currentPlanet,e,z.player.officers);return z.currentPlanet.buildQueue.push(t),!0},Y=e=>ie(e),qe=e=>{if(!Z(e)){W.value=B(`common.requirementsNotMet`),G.value=Ye(e),U.value=!0;return}Ke(e)||(W.value=B(`buildingsView.upgradeFailed`),G.value=B(`buildingsView.upgradeFailedMessage`),U.value=!0)},X=e=>H.value?.buildings[e]||0,Z=e=>{if(!H.value)return!1;let t=V.value[e],n=b(t,X(e)+1);return!n||Object.keys(n).length===0?!0:le(H.value,z.player.technologies,n)},Je=e=>{if(!H.value)return B(`buildingsView.upgrade`);let t=V.value[e],n=X(e);return t.maxLevel!==void 0&&n>=t.maxLevel?B(`buildingsView.maxLevelReached`):H.value.buildQueue.length>0||Z(e)?B(`buildingsView.upgrade`):B(`buildingsView.requirementsNotMet`)},Ye=e=>{let t=V.value[e],n=b(t,X(e)+1);if(!n||!H.value)return``;let r=[];for(let[e,t]of Object.entries(n))if(Object.values(C).includes(e)){let n=e,i=H.value.buildings[n]||0,a=V.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${B(`common.current`)}: Lv ${i})`)}else if(Object.values(de).includes(e)){let n=e,i=z.player.technologies[n]||0,a=Ue.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${B(`common.current`)}: Lv ${i})`)}return r.join(`
|
||||
`)},Xe=e=>{if(!H.value)return!1;let t=V.value[e],r=X(e);if(t.maxLevel!==void 0&&r>=t.maxLevel)return!1;let i=n(z.player.officers,Date.now()),a=h(H.value,i.additionalBuildQueue);if(H.value.buildQueue.filter(e=>e.type===`building`||e.type===`demolish`).length>=a||!m(H.value,e,z.player.technologies,z.player.officers).valid)return!1;let o=Q(e,r+1);return H.value.resources.metal>=o.metal&&H.value.resources.crystal>=o.crystal&&H.value.resources.deuterium>=o.deuterium&&H.value.resources.darkMatter>=o.darkMatter},Q=(e,t)=>ee(e,t),Ze=(e,t)=>{if(!H.value)return 0;let r=n(z.player.officers,Date.now()),i=H.value.buildings[C.RoboticsFactory]||0,a=H.value.buildings[C.NaniteFactory]||0;return ae(e,t,r.buildingSpeedBonus,i,a)},Qe=e=>{if(!z.currentPlanet||!re(z.currentPlanet,e,z.player.officers).valid)return!1;let t=se(z.currentPlanet,e,z.player.officers);return z.currentPlanet.buildQueue.push(t),!0},$e=e=>{let t=V.value[e].name,n=$(e);q.value=`${B(`buildingsView.confirmDemolishMessage`)}: ${t}
|
||||
|
||||
${B(`buildingsView.demolishRefund`)}:
|
||||
${B(`resources.metal`)}: ${k(n.metal)}
|
||||
${B(`resources.crystal`)}: ${k(n.crystal)}
|
||||
${B(`resources.deuterium`)}: ${k(n.deuterium)}${n.darkMatter>0?`\n${B(`resources.darkMatter`)}: ${k(n.darkMatter)}`:``}`,J.value=e,K.value=!0},et=()=>{J.value&&(Qe(J.value)||(W.value=B(`buildingsView.demolishFailed`),G.value=B(`buildingsView.demolishFailedMessage`),U.value=!0)),K.value=!1,J.value=null},tt=e=>{if(!H.value||X(e)<=0)return!1;let t=n(z.player.officers,Date.now()),r=h(H.value,t.additionalBuildQueue);return!(H.value.buildQueue.filter(e=>e.type===`building`||e.type===`demolish`).length>=r)},$=e=>t(e,X(e));return(t,n)=>H.value?(v(),l(`div`,xe,[f(`h1`,Se,u(a(B)(`buildingsView.title`)),1),f(`div`,Ce,[f(`div`,we,[f(`div`,Te,[g(a(x),{size:16}),e(` `+u(a(B)(`buildingsView.spaceUsage`))+`: `,1)]),f(`div`,Ee,[f(`span`,{class:y(Y(H.value)>H.value.maxSpace?`text-destructive`:`text-primary`)},u(a(k)(Y(H.value))),3),n[2]||=f(`span`,{class:`text-muted-foreground mx-1`},`/`,-1),f(`span`,null,u(a(k)(H.value.maxSpace)),1)])]),f(`div`,De,[f(`div`,Oe,[f(`div`,{class:y([`h-full transition-all duration-300`,Y(H.value)>H.value.maxSpace?`bg-destructive`:`bg-primary`]),style:ne({width:`${Math.min(Y(H.value)/H.value.maxSpace*100,100)}%`})},null,6)])])]),f(`div`,ke,[(v(!0),l(o,null,_(Ge.value,t=>(v(),d(a(me),{key:t,class:`relative`},{default:i(()=>[g(be,{requirements:a(V)[t].requirements,currentLevel:X(t)},null,8,[`requirements`,`currentLevel`]),g(a(_e),null,{default:i(()=>[f(`div`,Ae,[f(`div`,je,[g(a(D),{class:`text-sm sm:text-base lg:text-lg cursor-pointer hover:text-primary transition-colors underline decoration-dotted underline-offset-4 order-2 sm:order-1`,onClick:e=>a(He).openBuilding(t,X(t))},{default:i(()=>[e(u(a(V)[t].name),1)]),_:2},1032,[`onClick`]),g(a(pe),{variant:`secondary`,class:`text-xs whitespace-nowrap self-start order-1 sm:order-2`},{default:i(()=>[e(` Lv `+u(X(t)),1)]),_:2},1024)])]),g(a(S),{class:`text-xs sm:text-sm`},{default:i(()=>[e(u(a(V)[t].description),1)]),_:2},1024)]),_:2},1024),g(a(ve),null,{default:i(()=>[f(`div`,Me,[f(`div`,R,[f(`p`,Ne,u(a(B)(`buildingsView.upgradeCost`))+`:`,1),f(`div`,Pe,[(v(),l(o,null,_(We,e=>r(f(`div`,{key:e.key,class:`flex items-center gap-1.5 sm:gap-2`},[g(T,{type:e.key,size:`sm`},null,8,[`type`]),f(`span`,Fe,u(a(B)(`resources.${e.key}`))+`:`,1),f(`span`,{class:y([`font-medium text-xs sm:text-sm`,a(ge)(H.value.resources[e.key],Q(t,X(t)+1)[e.key])])},u(a(k)(Q(t,X(t)+1)[e.key])),3)]),[[te,e.key!==`darkMatter`||Q(t,X(t)+1).darkMatter>0]])),64))])]),f(`div`,Ie,[f(`div`,Le,[g(a(ue),{size:14,class:`flex-shrink-0`}),f(`span`,null,u(a(N)(Ze(t,X(t)+1))),1)]),f(`div`,Re,[g(a(x),{size:14,class:`flex-shrink-0`}),f(`span`,null,u(a(V)[t].spaceUsage),1)])]),g(a(E),{onClick:e=>qe(t),disabled:!Xe(t),class:`w-full`},{default:i(()=>[e(u(Je(t)),1)]),_:2},1032,[`onClick`,`disabled`]),X(t)>0?(v(),d(a(E),{key:0,onClick:e=>$e(t),disabled:!tt(t),variant:`destructive`,class:`w-full`},{default:i(()=>[e(u(a(B)(`buildingsView.demolish`)),1)]),_:1},8,[`onClick`,`disabled`])):p(``,!0),X(t)>0?(v(),l(`div`,ze,[f(`p`,null,u(a(B)(`buildingsView.demolishRefund`))+`:`,1),f(`div`,Be,[f(`span`,null,u(a(k)($(t).metal))+` `+u(a(B)(`resources.metal`)),1),f(`span`,null,u(a(k)($(t).crystal))+` `+u(a(B)(`resources.crystal`)),1),f(`span`,null,u(a(k)($(t).deuterium))+` `+u(a(B)(`resources.deuterium`)),1),$(t).darkMatter>0?(v(),l(`span`,Ve,u(a(k)($(t).darkMatter))+` `+u(a(B)(`resources.darkMatter`)),1)):p(``,!0)])])):p(``,!0)])]),_:2},1024)]),_:2},1024))),128))]),g(a(L),{open:U.value,"onUpdate:open":n[0]||=e=>U.value=e},{default:i(()=>[g(a(I),null,{default:i(()=>[g(a(A),null,{default:i(()=>[g(a(F),null,{default:i(()=>[e(u(W.value),1)]),_:1}),g(a(M),{class:`whitespace-pre-line`},{default:i(()=>[e(u(G.value),1)]),_:1})]),_:1}),g(a(O),null,{default:i(()=>[g(a(j),null,{default:i(()=>[e(u(a(B)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`]),g(a(L),{open:K.value,"onUpdate:open":n[1]||=e=>K.value=e},{default:i(()=>[g(a(I),null,{default:i(()=>[g(a(A),null,{default:i(()=>[g(a(F),null,{default:i(()=>[e(u(a(B)(`buildingsView.confirmDemolish`)),1)]),_:1}),g(a(M),{class:`whitespace-pre-line`},{default:i(()=>[e(u(q.value),1)]),_:1})]),_:1}),g(a(O),null,{default:i(()=>[g(a(he),null,{default:i(()=>[e(u(a(B)(`common.cancel`)),1)]),_:1}),g(a(j),{onClick:et},{default:i(()=>[e(u(a(B)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])])):p(``,!0)}});export{z as default};
|
||||
7
docs/assets/BuildingsView-BwpaZTPf.js
Normal file
7
docs/assets/BuildingsView-BwpaZTPf.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import{$ as e,$t as t,Bt as n,En as r,Ht as i,On as a,Pt as o,Rn as s,Vt as ee,X as te,Yn as c,Z as ne,ar as l,cn as u,cr as d,dn as f,et as p,fn as m,ln as h,nt as g,on as _,pn as re,rn as v,rr as y,rt as b,sn as x,sr as ie,tt as ae,un as S,zn as oe,zt as se}from"./game-logic-BBFCPVtv.js";import"./vendor-pinia-L_8CmDWM.js";import"./vendor-crypto-CQM8pryk.js";import"./game-i18n-BC9uiVvf.js";import"./vendor-others-Bce0hM6h.js";import"./vendor-reka-ui-DR3m_AkV.js";import"./vendor-utils-BlvnUqQX.js";import"./vendor-vueuse-Djj-oRw9.js";import{F as C,U as ce}from"./vendor-icons-Ccm2_GPI.js";import{t as le}from"./CardDescription-DduF5EVY.js";import{d as w,v as T}from"./game-config-DRP-gbpS.js";import{$ as ue,A as E,F as D,I as O,J as k,K as de,L as fe,M as A,N as j,P as pe,X as me,d as he,f as ge,j as M,k as N,l as P,m as F,o as I,p as L,s as _e,x as ve}from"./index-B4KMqZ_y.js";import{t as ye}from"./useGameConfig-DLqfPHtW.js";import{t as be}from"./CardUnlockOverlay-CspXlcWR.js";var xe={key:0,class:`container mx-auto p-4 sm:p-6`},Se={class:`text-2xl sm:text-3xl font-bold mb-4 sm:mb-6`},Ce={class:`mb-4 sm:mb-6 p-3 sm:p-4 bg-muted/50 rounded-lg border`},we={class:`flex items-center justify-between`},Te={class:`text-sm sm:text-base font-medium flex items-center gap-2`},Ee={class:`text-sm sm:text-base font-bold`},De={class:`mt-2`},Oe={class:`w-full bg-background rounded-full h-2.5 sm:h-3 overflow-hidden`},ke={class:`grid grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4`},Ae={class:`mb-2`},je={class:`flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2`},Me={class:`space-y-3`},Ne={class:`text-xs sm:text-sm space-y-1.5 sm:space-y-2`},Pe={class:`text-muted-foreground mb-1 sm:mb-2`},Fe={class:`space-y-1 sm:space-y-1.5`},Ie={class:`text-xs`},Le={class:`text-xs sm:text-sm space-y-0.5 sm:space-y-1`},Re={class:`flex items-center gap-1.5 text-muted-foreground`},ze={class:`flex items-center gap-1.5 text-muted-foreground`},Be={key:1,class:`text-xs text-muted-foreground`},Ve={class:`flex gap-2 flex-wrap`},He={key:0},R=re({__name:`BuildingsView`,setup(re){let R=ue(),Ue=ve(),{t:z}=me(),{BUILDINGS:B,TECHNOLOGIES:We}=ye(),V=_(()=>R.currentPlanet),H=c(!1),U=c(``),W=c(``),G=c(!1),K=c(``),q=c(null),Ge=[{key:`metal`},{key:`crystal`},{key:`deuterium`},{key:`darkMatter`}],Ke=_(()=>V.value?Object.values(w).filter(e=>{let t=B.value[e];return V.value.isMoon?t.moonOnly===!0:t.moonOnly!==!0}):[]),qe=e=>{if(!R.currentPlanet||!p(R.currentPlanet,e,R.player.technologies,R.player.officers).valid)return!1;let t=ne(R.currentPlanet,e,R.player.officers);return R.currentPlanet.buildQueue.push(t),!0},J=e=>i(e),Je=e=>{if(!X(e)){U.value=z(`common.requirementsNotMet`),W.value=Ye(e),H.value=!0;return}qe(e)||(U.value=z(`buildingsView.upgradeFailed`),W.value=z(`buildingsView.upgradeFailedMessage`),H.value=!0)},Y=e=>V.value?.buildings[e]||0,X=e=>{if(!V.value)return!1;let t=B.value[e],n=g(t,Y(e)+1);return!n||Object.keys(n).length===0?!0:ae(V.value,R.player.technologies,n)},Z=e=>{if(!V.value)return z(`buildingsView.upgrade`);let t=B.value[e],n=Y(e);return t.maxLevel!==void 0&&n>=t.maxLevel?z(`buildingsView.maxLevelReached`):V.value.buildQueue.length>0||X(e)?z(`buildingsView.upgrade`):z(`buildingsView.requirementsNotMet`)},Ye=e=>{let t=B.value[e],n=g(t,Y(e)+1);if(!n||!V.value)return``;let r=[];for(let[e,t]of Object.entries(n))if(Object.values(w).includes(e)){let n=e,i=V.value.buildings[n]||0,a=B.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${z(`common.current`)}: Lv ${i})`)}else if(Object.values(T).includes(e)){let n=e,i=R.player.technologies[n]||0,a=We.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${z(`common.current`)}: Lv ${i})`)}return r.join(`
|
||||
`)},Xe=e=>{if(!V.value)return!1;let t=B.value[e],n=Y(e);if(t.maxLevel!==void 0&&n>=t.maxLevel)return!1;let r=o(R.player.officers,Date.now()),i=b(V.value,r.additionalBuildQueue);if(V.value.buildQueue.filter(e=>e.type===`building`||e.type===`demolish`).length>=i||!p(V.value,e,R.player.technologies,R.player.officers).valid)return!1;let a=Q(e,n+1);return V.value.resources.metal>=a.metal&&V.value.resources.crystal>=a.crystal&&V.value.resources.deuterium>=a.deuterium&&V.value.resources.darkMatter>=a.darkMatter},Q=(e,t)=>se(e,t),Ze=(e,t)=>{if(!V.value)return 0;let r=o(R.player.officers,Date.now()),i=V.value.buildings[w.RoboticsFactory]||0,a=V.value.buildings[w.NaniteFactory]||0;return n(e,t,r.buildingSpeedBonus,i,a)},Qe=t=>{if(!R.currentPlanet||!e(R.currentPlanet,t,R.player.officers).valid)return!1;let n=te(R.currentPlanet,t,R.player.officers);return R.currentPlanet.buildQueue.push(n),!0},$e=e=>{let t=B.value[e].name,n=$(e);K.value=`${z(`buildingsView.confirmDemolishMessage`)}: ${t}
|
||||
|
||||
${z(`buildingsView.demolishRefund`)}:
|
||||
${z(`resources.metal`)}: ${I(n.metal)}
|
||||
${z(`resources.crystal`)}: ${I(n.crystal)}
|
||||
${z(`resources.deuterium`)}: ${I(n.deuterium)}${n.darkMatter>0?`\n${z(`resources.darkMatter`)}: ${I(n.darkMatter)}`:``}`,q.value=e,G.value=!0},et=()=>{q.value&&(Qe(q.value)||(U.value=z(`buildingsView.demolishFailed`),W.value=z(`buildingsView.demolishFailedMessage`),H.value=!0)),G.value=!1,q.value=null},tt=e=>{if(!V.value||Y(e)<=0)return!1;let t=o(R.player.officers,Date.now()),n=b(V.value,t.additionalBuildQueue);return!(V.value.buildQueue.filter(e=>e.type===`building`||e.type===`demolish`).length>=n)},$=e=>ee(e,Y(e));return(e,n)=>V.value?(r(),S(`div`,xe,[x(`h1`,Se,d(y(z)(`buildingsView.title`)),1),x(`div`,Ce,[x(`div`,we,[x(`div`,Te,[m(y(C),{size:16}),f(` `+d(y(z)(`buildingsView.spaceUsage`))+`: `,1)]),x(`div`,Ee,[x(`span`,{class:l(J(V.value)>V.value.maxSpace?`text-destructive`:`text-primary`)},d(y(I)(J(V.value))),3),n[2]||=x(`span`,{class:`text-muted-foreground mx-1`},`/`,-1),x(`span`,null,d(y(I)(V.value.maxSpace)),1)])]),x(`div`,De,[x(`div`,Oe,[x(`div`,{class:l([`h-full transition-all duration-300`,J(V.value)>V.value.maxSpace?`bg-destructive`:`bg-primary`]),style:ie({width:`${Math.min(J(V.value)/V.value.maxSpace*100,100)}%`})},null,6)])])]),x(`div`,ke,[(r(!0),S(v,null,a(Ke.value,e=>(r(),u(y(F),{key:e,class:`relative`},{default:s(()=>[m(be,{requirements:y(B)[e].requirements,currentLevel:Y(e)},null,8,[`requirements`,`currentLevel`]),m(y(ge),null,{default:s(()=>[x(`div`,Ae,[x(`div`,je,[m(y(he),{class:`text-sm sm:text-base lg:text-lg cursor-pointer hover:text-primary transition-colors underline decoration-dotted underline-offset-4 order-2 sm:order-1`,onClick:t=>y(Ue).openBuilding(e,Y(e))},{default:s(()=>[f(d(y(B)[e].name),1)]),_:2},1032,[`onClick`]),m(y(de),{variant:`secondary`,class:`text-xs whitespace-nowrap self-start order-1 sm:order-2`},{default:s(()=>[f(` Lv `+d(Y(e)),1)]),_:2},1024)])]),m(y(le),{class:`text-xs sm:text-sm`},{default:s(()=>[f(d(y(B)[e].description),1)]),_:2},1024)]),_:2},1024),m(y(L),null,{default:s(()=>[x(`div`,Me,[x(`div`,Ne,[x(`p`,Pe,d(y(z)(`buildingsView.upgradeCost`))+`:`,1),x(`div`,Fe,[(r(),S(v,null,a(Ge,n=>oe(x(`div`,{key:n.key,class:`flex items-center gap-1.5 sm:gap-2`},[m(fe,{type:n.key,size:`sm`},null,8,[`type`]),x(`span`,Ie,d(y(z)(`resources.${n.key}`))+`:`,1),x(`span`,{class:l([`font-medium text-xs sm:text-sm`,y(P)(V.value.resources[n.key],Q(e,Y(e)+1)[n.key])])},d(y(I)(Q(e,Y(e)+1)[n.key])),3)]),[[t,n.key!==`darkMatter`||Q(e,Y(e)+1).darkMatter>0]])),64))])]),x(`div`,Le,[x(`div`,Re,[m(y(ce),{size:14,class:`flex-shrink-0`}),x(`span`,null,d(y(_e)(Ze(e,Y(e)+1))),1)]),x(`div`,ze,[m(y(C),{size:14,class:`flex-shrink-0`}),x(`span`,null,d(y(B)[e].spaceUsage),1)])]),m(y(k),{onClick:t=>Je(e),disabled:!Xe(e),class:`w-full`},{default:s(()=>[f(d(Z(e)),1)]),_:2},1032,[`onClick`,`disabled`]),Y(e)>0?(r(),u(y(k),{key:0,onClick:t=>$e(e),disabled:!tt(e),variant:`destructive`,class:`w-full`},{default:s(()=>[f(d(y(z)(`buildingsView.demolish`)),1)]),_:1},8,[`onClick`,`disabled`])):h(``,!0),Y(e)>0?(r(),S(`div`,Be,[x(`p`,null,d(y(z)(`buildingsView.demolishRefund`))+`:`,1),x(`div`,Ve,[x(`span`,null,d(y(I)($(e).metal))+` `+d(y(z)(`resources.metal`)),1),x(`span`,null,d(y(I)($(e).crystal))+` `+d(y(z)(`resources.crystal`)),1),x(`span`,null,d(y(I)($(e).deuterium))+` `+d(y(z)(`resources.deuterium`)),1),$(e).darkMatter>0?(r(),S(`span`,He,d(y(I)($(e).darkMatter))+` `+d(y(z)(`resources.darkMatter`)),1)):h(``,!0)])])):h(``,!0)])]),_:2},1024)]),_:2},1024))),128))]),m(y(O),{open:H.value,"onUpdate:open":n[0]||=e=>H.value=e},{default:s(()=>[m(y(j),null,{default:s(()=>[m(y(E),null,{default:s(()=>[m(y(N),null,{default:s(()=>[f(d(U.value),1)]),_:1}),m(y(A),{class:`whitespace-pre-line`},{default:s(()=>[f(d(W.value),1)]),_:1})]),_:1}),m(y(M),null,{default:s(()=>[m(y(D),null,{default:s(()=>[f(d(y(z)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`]),m(y(O),{open:G.value,"onUpdate:open":n[1]||=e=>G.value=e},{default:s(()=>[m(y(j),null,{default:s(()=>[m(y(E),null,{default:s(()=>[m(y(N),null,{default:s(()=>[f(d(y(z)(`buildingsView.confirmDemolish`)),1)]),_:1}),m(y(A),{class:`whitespace-pre-line`},{default:s(()=>[f(d(K.value),1)]),_:1})]),_:1}),m(y(M),null,{default:s(()=>[m(y(pe),null,{default:s(()=>[f(d(y(z)(`common.cancel`)),1)]),_:1}),m(y(D),{onClick:et},{default:s(()=>[f(d(y(z)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])])):h(``,!0)}});export{R as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{Gn as e,Qt as t,_n as n,mn as r,qn as i,tn as a}from"./game-logic-Bi1l7y4K.js";import{G as o}from"./index-BT5w6et-.js";var s=a({__name:`CardDescription`,props:{class:{}},setup(a){let s=a;return(a,c)=>(r(),t(`p`,{"data-slot":`card-description`,class:i(e(o)(`text-muted-foreground text-sm`,s.class))},[n(a.$slots,`default`)],2))}});export{s as t};
|
||||
1
docs/assets/CardDescription-DduF5EVY.js
Normal file
1
docs/assets/CardDescription-DduF5EVY.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,ar as t,kn as n,pn as r,rr as i,un as a}from"./game-logic-BBFCPVtv.js";import{Y as o}from"./index-B4KMqZ_y.js";var s=r({__name:`CardDescription`,props:{class:{}},setup(r){let s=r;return(r,c)=>(e(),a(`p`,{"data-slot":`card-description`,class:t(i(o)(`text-muted-foreground text-sm`,s.class))},[n(r.$slots,`default`)],2))}});export{s as t};
|
||||
2
docs/assets/CardUnlockOverlay-CspXlcWR.js
Normal file
2
docs/assets/CardUnlockOverlay-CspXlcWR.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import{En as e,Rn as t,Yn as n,cr as r,dn as i,fn as a,ln as o,on as s,pn as c,rr as l,sn as u,tt as d,un as f}from"./game-logic-BBFCPVtv.js";import{k as p}from"./vendor-icons-Ccm2_GPI.js";import{d as m,v as h}from"./game-config-DRP-gbpS.js";import{$ as g,A as _,F as v,I as y,J as b,M as x,N as S,X as C,j as w,k as T}from"./index-B4KMqZ_y.js";import{t as E}from"./useGameConfig-DLqfPHtW.js";var D={key:0,class:`absolute inset-0 z-10 bg-background/70 backdrop-blur-[2px] rounded-lg flex items-center justify-center`},O={class:`text-center p-4 space-y-2`},k={class:`flex justify-center`},A={class:`rounded-full bg-muted p-2`},j={class:`text-xs font-medium text-muted-foreground`},M=c({__name:`CardUnlockOverlay`,props:{requirements:{},currentLevel:{}},setup(c){let M=c,N=g(),{t:P}=C(),{BUILDINGS:F,TECHNOLOGIES:I}=E(),L=n(!1),R=n(``),z=n(``),B=s(()=>M.currentLevel!==void 0&&M.currentLevel>0||!M.requirements||!N.currentPlanet?!0:d(N.currentPlanet,N.player.technologies,M.requirements)),V=()=>{if(!M.requirements||!N.currentPlanet)return``;let e=[];for(let[t,n]of Object.entries(M.requirements))if(Object.values(m).includes(t)){let r=t,i=N.currentPlanet.buildings[r]||0,a=F.value[r]?.name||r,o=i>=n?`✓`:`✗`;e.push(`${o} ${a}: Lv ${n} (${P(`common.current`)}: Lv ${i})`)}else if(Object.values(h).includes(t)){let r=t,i=N.player.technologies[r]||0,a=I.value[r]?.name||r,o=i>=n?`✓`:`✗`;e.push(`${o} ${a}: Lv ${n} (${P(`common.current`)}: Lv ${i})`)}return e.join(`
|
||||
`)},H=()=>{R.value=P(`common.requirementsNotMet`),z.value=V(),L.value=!0};return(n,s)=>B.value?o(``,!0):(e(),f(`div`,D,[u(`div`,O,[u(`div`,k,[u(`div`,A,[a(l(p),{size:20,class:`text-muted-foreground`})])]),u(`p`,j,r(l(P)(`common.locked`)),1),a(l(b),{variant:`outline`,size:`sm`,onClick:H,class:`text-xs`},{default:t(()=>[i(r(l(P)(`common.viewRequirements`)),1)]),_:1})]),a(l(y),{open:L.value,"onUpdate:open":s[0]||=e=>L.value=e},{default:t(()=>[a(l(S),null,{default:t(()=>[a(l(_),null,{default:t(()=>[a(l(T),null,{default:t(()=>[i(r(R.value),1)]),_:1}),a(l(x),{class:`whitespace-pre-line`},{default:t(()=>[i(r(z.value),1)]),_:1})]),_:1}),a(l(w),null,{default:t(()=>[a(l(v),null,{default:t(()=>[i(r(l(P)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])]))}});export{M as t};
|
||||
@@ -1,2 +0,0 @@
|
||||
import{$t as e,En as t,Gn as n,Jt as r,Ln as i,Qt as a,Xn as o,Yt as s,Zt as c,en as l,mn as u,tn as d,v as f}from"./game-logic-Bi1l7y4K.js";import{D as p}from"./vendor-icons-z9V6Jdbh.js";import{d as m,v as h}from"./game-config-CG6z6nnH.js";import{K as g,W as _,a as v,i as y,l as b,o as x,q as S,r as C,s as w,u as T}from"./index-BT5w6et-.js";import{t as E}from"./useGameConfig-b98LOtBY.js";var D={key:0,class:`absolute inset-0 z-10 bg-background/70 backdrop-blur-[2px] rounded-lg flex items-center justify-center`},O={class:`text-center p-4 space-y-2`},k={class:`flex justify-center`},A={class:`rounded-full bg-muted p-2`},j={class:`text-xs font-medium text-muted-foreground`},M=d({__name:`CardUnlockOverlay`,props:{requirements:{},currentLevel:{}},setup(d){let M=d,N=S(),{t:P}=g(),{BUILDINGS:F,TECHNOLOGIES:I}=E(),L=i(!1),R=i(``),z=i(``),B=r(()=>M.currentLevel!==void 0&&M.currentLevel>0||!M.requirements||!N.currentPlanet?!0:f(N.currentPlanet,N.player.technologies,M.requirements)),V=()=>{if(!M.requirements||!N.currentPlanet)return``;let e=[];for(let[t,n]of Object.entries(M.requirements))if(Object.values(m).includes(t)){let r=t,i=N.currentPlanet.buildings[r]||0,a=F.value[r]?.name||r,o=i>=n?`✓`:`✗`;e.push(`${o} ${a}: Lv ${n} (${P(`common.current`)}: Lv ${i})`)}else if(Object.values(h).includes(t)){let r=t,i=N.player.technologies[r]||0,a=I.value[r]?.name||r,o=i>=n?`✓`:`✗`;e.push(`${o} ${a}: Lv ${n} (${P(`common.current`)}: Lv ${i})`)}return e.join(`
|
||||
`)},H=()=>{R.value=P(`common.requirementsNotMet`),z.value=V(),L.value=!0};return(r,i)=>B.value?c(``,!0):(u(),a(`div`,D,[s(`div`,O,[s(`div`,k,[s(`div`,A,[l(n(p),{size:20,class:`text-muted-foreground`})])]),s(`p`,j,o(n(P)(`common.locked`)),1),l(n(_),{variant:`outline`,size:`sm`,onClick:H,class:`text-xs`},{default:t(()=>[e(o(n(P)(`common.viewRequirements`)),1)]),_:1})]),l(n(T),{open:L.value,"onUpdate:open":i[0]||=e=>L.value=e},{default:t(()=>[l(n(w),null,{default:t(()=>[l(n(y),null,{default:t(()=>[l(n(C),null,{default:t(()=>[e(o(R.value),1)]),_:1}),l(n(x),{class:`whitespace-pre-line`},{default:t(()=>[e(o(z.value),1)]),_:1})]),_:1}),l(n(v),null,{default:t(()=>[l(n(b),null,{default:t(()=>[e(o(n(P)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])]))}});export{M as t};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/DefenseView-aCDrCjw8.js
Normal file
1
docs/assets/DefenseView-aCDrCjw8.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/DiplomacyView-BZEvKDfM.js
Normal file
1
docs/assets/DiplomacyView-BZEvKDfM.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/FleetView-BzElc0kX.js
Normal file
1
docs/assets/FleetView-BzElc0kX.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/GMView-BMhV90vD.js
Normal file
1
docs/assets/GMView-BMhV90vD.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
docs/assets/GalaxyView-LGfalWWd.js
Normal file
2
docs/assets/GalaxyView-LGfalWWd.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/MessagesView-w53RKU-r.js
Normal file
1
docs/assets/MessagesView-w53RKU-r.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/NotFoundView-BqreQe6E.js
Normal file
1
docs/assets/NotFoundView-BqreQe6E.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,Rn as t,cr as n,dn as r,fn as i,pn as a,rr as o,sn as s,un as c}from"./game-logic-BBFCPVtv.js";import"./vendor-pinia-L_8CmDWM.js";import{o as l}from"./vendor-vue-router-0mfLVB6Y.js";import"./vendor-crypto-CQM8pryk.js";import"./game-i18n-BC9uiVvf.js";import"./vendor-others-Bce0hM6h.js";import"./vendor-reka-ui-DR3m_AkV.js";import"./vendor-utils-BlvnUqQX.js";import{N as u}from"./vendor-icons-Ccm2_GPI.js";import{J as d,X as f}from"./index-B4KMqZ_y.js";import{a as p,i as m,n as h,o as g,r as _,t as v}from"./empty-Ds0PR_Pd.js";var y={class:`container mx-auto p-4 sm:p-6 flex items-center justify-center min-h-[60vh]`},b=a({__name:`NotFoundView`,setup(a){let b=l(),{t:x}=f(),S=()=>{b.push(`/`)};return(a,l)=>(e(),c(`div`,y,[i(o(g),{class:`border-0`},{default:t(()=>[i(o(h),null,{default:t(()=>[...l[0]||=[s(`div`,{class:`text-8xl sm:text-9xl font-bold text-muted-foreground/20`},`404`,-1)]]),_:1}),i(o(_),null,{default:t(()=>[i(o(v),null,{default:t(()=>[r(n(o(x)(`notFound.title`)),1)]),_:1}),i(o(m),null,{default:t(()=>[r(n(o(x)(`notFound.description`)),1)]),_:1})]),_:1}),i(o(p),null,{default:t(()=>[i(o(d),{onClick:S,size:`lg`},{default:t(()=>[i(o(u),{class:`mr-2 h-4 w-4`}),r(` `+n(o(x)(`notFound.goHome`)),1)]),_:1})]),_:1})]),_:1})]))}});export{b as default};
|
||||
1
docs/assets/OfficersView-CXh701uW.js
Normal file
1
docs/assets/OfficersView-CXh701uW.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/OverviewView-D7lWE9IR.js
Normal file
1
docs/assets/OverviewView-D7lWE9IR.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
import{$t as e,Dn as t,En as n,Gn as r,Gt as i,It as a,Jt as o,Ln as s,Qt as c,S as l,Vt as ee,Xn as u,Xt as te,Yt as d,Zt as ne,d as re,en as f,gn as p,mn as m,qn as ie,tn as ae,u as oe,v as h,y as g}from"./game-logic-Bi1l7y4K.js";import"./vendor-pinia-DqhKuBjp.js";import"./vendor-vue-router-1sDnnIWZ.js";import"./vendor-crypto-CQM8pryk.js";import"./game-i18n-Dr0JspcV.js";import"./vendor-others-DTUzJ7S-.js";import"./vendor-reka-ui-BEHIKScj.js";import"./vendor-utils-BlvnUqQX.js";import"./vendor-vueuse-CJcfYqoW.js";import"./vendor-icons-z9V6Jdbh.js";import{t as se}from"./CardDescription-BSLS07AU.js";import{d as _,v}from"./game-config-CG6z6nnH.js";import{D as ce,H as le,K as ue,N as de,W as y,_ as b,a as x,b as S,f as C,h as w,i as T,l as E,o as D,q as O,r as k,s as A,u as j,v as M,y as N}from"./index-BT5w6et-.js";import{t as P}from"./useGameConfig-b98LOtBY.js";import{t as F}from"./CardUnlockOverlay-DYfgGY6J.js";import{t as I}from"./UnlockRequirement-CoWPiUl8.js";var L={key:0,class:`container mx-auto p-4 sm:p-6`},R={class:`text-2xl sm:text-3xl font-bold mb-4 sm:mb-6`},fe={class:`grid grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4`},z={class:`mb-2`},B={class:`flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2`},V={class:`space-y-2.5 sm:space-y-3`},pe={class:`text-xs sm:text-sm space-y-1.5 sm:space-y-2`},me={class:`text-muted-foreground mb-1 sm:mb-2`},he={class:`space-y-1 sm:space-y-1.5`},ge={class:`text-xs`},H=ae({__name:`ResearchView`,setup(ae){let H=O(),_e=ce(),{t:U}=ue(),{TECHNOLOGIES:W,BUILDINGS:ve}=P(),G=o(()=>H.currentPlanet),K=o(()=>H.player),q=s(!1),J=s(``),Y=s(``),ye=[{key:`metal`},{key:`crystal`},{key:`deuterium`},{key:`darkMatter`}],X=e=>{if(!H.currentPlanet||!re(H.currentPlanet,e,H.player.technologies,H.player.researchQueue).valid)return!1;let t=H.player.technologies[e]||0,{queueItem:n}=oe(H.currentPlanet,e,t,H.player.officers,H.player.technologies);return H.player.researchQueue.push(n),!0},Z=e=>{if(!G.value)return!1;let t=W.value[e],n=g(t,Q(e)+1);return!n||Object.keys(n).length===0?!0:h(G.value,H.player.technologies,n)},be=e=>{if(!G.value)return U(`researchView.research`);let t=W.value[e],n=Q(e);if(t.maxLevel!==void 0&&n>=t.maxLevel)return U(`researchView.maxLevelReached`);let r=l(H.player.technologies);return K.value.researchQueue.length>=r||Z(e)?U(`researchView.research`):U(`buildingsView.requirementsNotMet`)},xe=e=>{let t=W.value[e],n=g(t,Q(e)+1);if(!n||!G.value)return``;let r=[];for(let[e,t]of Object.entries(n))if(Object.values(_).includes(e)){let n=e,i=G.value.buildings[n]||0,a=ve.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${U(`common.current`)}: Lv ${i})`)}else if(Object.values(v).includes(e)){let n=e,i=H.player.technologies[n]||0,a=W.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${U(`common.current`)}: Lv ${i})`)}return r.join(`
|
||||
`)},Se=e=>{if(!Z(e)){J.value=U(`common.requirementsNotMet`),Y.value=xe(e),q.value=!0;return}X(e)||(J.value=U(`researchView.researchFailed`),Y.value=U(`researchView.researchFailedMessage`),q.value=!0)},Q=e=>K.value.technologies[e]||0,Ce=e=>{if(!G.value)return!1;let t=W.value[e],n=Q(e);if(t.maxLevel!==void 0&&n>=t.maxLevel)return!1;let r=l(H.player.technologies);if(K.value.researchQueue.length>=r)return!1;let i=$(e,n+1);return h(G.value,H.player.technologies,t.requirements)&&G.value.resources.metal>=i.metal&&G.value.resources.crystal>=i.crystal&&G.value.resources.deuterium>=i.deuterium&&G.value.resources.darkMatter>=i.darkMatter},$=(e,t)=>a(e,t);return(a,o)=>G.value?(m(),c(`div`,L,[f(I,{"required-building":r(_).ResearchLab,"required-level":1},null,8,[`required-building`]),d(`h1`,R,u(r(U)(`researchView.title`)),1),d(`div`,fe,[(m(!0),c(i,null,p(Object.values(r(v)),a=>(m(),te(r(S),{key:a,class:`relative`},{default:n(()=>[f(F,{requirements:r(W)[a].requirements,currentLevel:Q(a)},null,8,[`requirements`,`currentLevel`]),f(r(M),null,{default:n(()=>[d(`div`,z,[d(`div`,B,[f(r(b),{class:`text-sm sm:text-base lg:text-lg cursor-pointer hover:text-primary transition-colors underline decoration-dotted underline-offset-4 order-2 sm:order-1`,onClick:e=>r(_e).openTechnology(a,Q(a))},{default:n(()=>[e(u(r(W)[a].name),1)]),_:2},1032,[`onClick`]),f(r(le),{variant:`secondary`,class:`text-xs whitespace-nowrap self-start order-1 sm:order-2`},{default:n(()=>[e(` Lv `+u(Q(a)),1)]),_:2},1024)])]),f(r(se),{class:`text-xs sm:text-sm`},{default:n(()=>[e(u(r(W)[a].description),1)]),_:2},1024)]),_:2},1024),f(r(N),null,{default:n(()=>[d(`div`,V,[d(`div`,pe,[d(`p`,me,u(r(U)(`researchView.researchCost`))+`:`,1),d(`div`,he,[(m(),c(i,null,p(ye,e=>t(d(`div`,{key:e.key,class:`flex items-center gap-1.5 sm:gap-2`},[f(de,{type:e.key,size:`sm`},null,8,[`type`]),d(`span`,ge,u(r(U)(`resources.${e.key}`))+`:`,1),d(`span`,{class:ie([`font-medium text-xs sm:text-sm`,r(w)(G.value.resources[e.key],$(a,Q(a)+1)[e.key])])},u(r(C)($(a,Q(a)+1)[e.key])),3)]),[[ee,e.key!==`darkMatter`||$(a,Q(a)+1).darkMatter>0]])),64))])]),f(r(y),{onClick:e=>Se(a),disabled:!Ce(a),class:`w-full`},{default:n(()=>[e(u(be(a)),1)]),_:2},1032,[`onClick`,`disabled`])])]),_:2},1024)]),_:2},1024))),128))]),f(r(j),{open:q.value,"onUpdate:open":o[0]||=e=>q.value=e},{default:n(()=>[f(r(A),null,{default:n(()=>[f(r(T),null,{default:n(()=>[f(r(k),null,{default:n(()=>[e(u(J.value),1)]),_:1}),f(r(D),{class:`whitespace-pre-line`},{default:n(()=>[e(u(Y.value),1)]),_:1})]),_:1}),f(r(x),null,{default:n(()=>[f(r(E),null,{default:n(()=>[e(u(r(U)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])])):ne(``,!0)}});export{H as default};
|
||||
2
docs/assets/ResearchView-BoGXU7Gy.js
Normal file
2
docs/assets/ResearchView-BoGXU7Gy.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import{$t as e,En as t,J as n,On as r,Rn as i,Yn as a,ar as o,at as s,cn as ee,cr as c,dn as l,fn as u,ln as te,nt as d,on as f,pn as ne,q as re,qt as ie,rn as p,rr as m,sn as h,tt as g,un as _,zn as ae}from"./game-logic-BBFCPVtv.js";import"./vendor-pinia-L_8CmDWM.js";import"./vendor-vue-router-0mfLVB6Y.js";import"./vendor-crypto-CQM8pryk.js";import"./game-i18n-BC9uiVvf.js";import"./vendor-others-Bce0hM6h.js";import"./vendor-reka-ui-DR3m_AkV.js";import"./vendor-utils-BlvnUqQX.js";import"./vendor-vueuse-Djj-oRw9.js";import"./vendor-icons-Ccm2_GPI.js";import{t as oe}from"./CardDescription-DduF5EVY.js";import{d as v,v as y}from"./game-config-DRP-gbpS.js";import{$ as se,A as ce,F as le,I as ue,J as de,K as b,L as x,M as S,N as C,X as w,d as T,f as E,j as D,k as O,l as k,m as A,o as j,p as M,x as N}from"./index-B4KMqZ_y.js";import{t as P}from"./useGameConfig-DLqfPHtW.js";import{t as F}from"./CardUnlockOverlay-CspXlcWR.js";import{t as I}from"./UnlockRequirement-DJ9dN5JL.js";var L={key:0,class:`container mx-auto p-4 sm:p-6`},R={class:`text-2xl sm:text-3xl font-bold mb-4 sm:mb-6`},z={class:`grid grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4`},fe={class:`mb-2`},B={class:`flex flex-col sm:flex-row sm:justify-between sm:items-start gap-2`},V={class:`space-y-2.5 sm:space-y-3`},pe={class:`text-xs sm:text-sm space-y-1.5 sm:space-y-2`},me={class:`text-muted-foreground mb-1 sm:mb-2`},he={class:`space-y-1 sm:space-y-1.5`},ge={class:`text-xs`},H=ne({__name:`ResearchView`,setup(ne){let H=se(),_e=N(),{t:U}=w(),{TECHNOLOGIES:W,BUILDINGS:ve}=P(),G=f(()=>H.currentPlanet),K=f(()=>H.player),q=a(!1),J=a(``),Y=a(``),ye=[{key:`metal`},{key:`crystal`},{key:`deuterium`},{key:`darkMatter`}],X=e=>{if(!H.currentPlanet||!n(H.currentPlanet,e,H.player.technologies,H.player.researchQueue).valid)return!1;let t=H.player.technologies[e]||0,{queueItem:r}=re(H.currentPlanet,e,t,H.player.officers,H.player.technologies);return H.player.researchQueue.push(r),!0},Z=e=>{if(!G.value)return!1;let t=W.value[e],n=d(t,Q(e)+1);return!n||Object.keys(n).length===0?!0:g(G.value,H.player.technologies,n)},be=e=>{if(!G.value)return U(`researchView.research`);let t=W.value[e],n=Q(e);if(t.maxLevel!==void 0&&n>=t.maxLevel)return U(`researchView.maxLevelReached`);let r=s(H.player.technologies);return K.value.researchQueue.length>=r||Z(e)?U(`researchView.research`):U(`buildingsView.requirementsNotMet`)},xe=e=>{let t=W.value[e],n=d(t,Q(e)+1);if(!n||!G.value)return``;let r=[];for(let[e,t]of Object.entries(n))if(Object.values(v).includes(e)){let n=e,i=G.value.buildings[n]||0,a=ve.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${U(`common.current`)}: Lv ${i})`)}else if(Object.values(y).includes(e)){let n=e,i=H.player.technologies[n]||0,a=W.value[n]?.name||n,o=i>=t?`✓`:`✗`;r.push(`${o} ${a}: Lv ${t} (${U(`common.current`)}: Lv ${i})`)}return r.join(`
|
||||
`)},Se=e=>{if(!Z(e)){J.value=U(`common.requirementsNotMet`),Y.value=xe(e),q.value=!0;return}X(e)||(J.value=U(`researchView.researchFailed`),Y.value=U(`researchView.researchFailedMessage`),q.value=!0)},Q=e=>K.value.technologies[e]||0,Ce=e=>{if(!G.value)return!1;let t=W.value[e],n=Q(e);if(t.maxLevel!==void 0&&n>=t.maxLevel)return!1;let r=s(H.player.technologies);if(K.value.researchQueue.length>=r)return!1;let i=$(e,n+1);return g(G.value,H.player.technologies,t.requirements)&&G.value.resources.metal>=i.metal&&G.value.resources.crystal>=i.crystal&&G.value.resources.deuterium>=i.deuterium&&G.value.resources.darkMatter>=i.darkMatter},$=(e,t)=>ie(e,t);return(n,a)=>G.value?(t(),_(`div`,L,[u(I,{"required-building":m(v).ResearchLab,"required-level":1},null,8,[`required-building`]),h(`h1`,R,c(m(U)(`researchView.title`)),1),h(`div`,z,[(t(!0),_(p,null,r(Object.values(m(y)),n=>(t(),ee(m(A),{key:n,class:`relative`},{default:i(()=>[u(F,{requirements:m(W)[n].requirements,currentLevel:Q(n)},null,8,[`requirements`,`currentLevel`]),u(m(E),null,{default:i(()=>[h(`div`,fe,[h(`div`,B,[u(m(T),{class:`text-sm sm:text-base lg:text-lg cursor-pointer hover:text-primary transition-colors underline decoration-dotted underline-offset-4 order-2 sm:order-1`,onClick:e=>m(_e).openTechnology(n,Q(n))},{default:i(()=>[l(c(m(W)[n].name),1)]),_:2},1032,[`onClick`]),u(m(b),{variant:`secondary`,class:`text-xs whitespace-nowrap self-start order-1 sm:order-2`},{default:i(()=>[l(` Lv `+c(Q(n)),1)]),_:2},1024)])]),u(m(oe),{class:`text-xs sm:text-sm`},{default:i(()=>[l(c(m(W)[n].description),1)]),_:2},1024)]),_:2},1024),u(m(M),null,{default:i(()=>[h(`div`,V,[h(`div`,pe,[h(`p`,me,c(m(U)(`researchView.researchCost`))+`:`,1),h(`div`,he,[(t(),_(p,null,r(ye,t=>ae(h(`div`,{key:t.key,class:`flex items-center gap-1.5 sm:gap-2`},[u(x,{type:t.key,size:`sm`},null,8,[`type`]),h(`span`,ge,c(m(U)(`resources.${t.key}`))+`:`,1),h(`span`,{class:o([`font-medium text-xs sm:text-sm`,m(k)(G.value.resources[t.key],$(n,Q(n)+1)[t.key])])},c(m(j)($(n,Q(n)+1)[t.key])),3)]),[[e,t.key!==`darkMatter`||$(n,Q(n)+1).darkMatter>0]])),64))])]),u(m(de),{onClick:e=>Se(n),disabled:!Ce(n),class:`w-full`},{default:i(()=>[l(c(be(n)),1)]),_:2},1032,[`onClick`,`disabled`])])]),_:2},1024)]),_:2},1024))),128))]),u(m(ue),{open:q.value,"onUpdate:open":a[0]||=e=>q.value=e},{default:i(()=>[u(m(C),null,{default:i(()=>[u(m(ce),null,{default:i(()=>[u(m(O),null,{default:i(()=>[l(c(J.value),1)]),_:1}),u(m(S),{class:`whitespace-pre-line`},{default:i(()=>[l(c(Y.value),1)]),_:1})]),_:1}),u(m(D),null,{default:i(()=>[u(m(le),null,{default:i(()=>[l(c(m(U)(`common.confirm`)),1)]),_:1})]),_:1})]),_:1})]),_:1},8,[`open`])])):te(``,!0)}});export{H as default};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/SettingsView-CqA4xeYn.js
Normal file
1
docs/assets/SettingsView-CqA4xeYn.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/ShipyardView-DRH89hEQ.js
Normal file
1
docs/assets/ShipyardView-DRH89hEQ.js
Normal file
File diff suppressed because one or more lines are too long
1
docs/assets/TabsTrigger-BnZOhHbD.js
Normal file
1
docs/assets/TabsTrigger-BnZOhHbD.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,Rn as t,cn as n,hn as r,kn as i,or as a,pn as o,rr as s,xn as c}from"./game-logic-BBFCPVtv.js";import{l}from"./vendor-others-Bce0hM6h.js";import{$ as u,c as d,et as f,l as p,s as m,u as h}from"./vendor-reka-ui-DR3m_AkV.js";import{Y as g}from"./index-B4KMqZ_y.js";var _=o({__name:`Tabs`,props:{defaultValue:{},orientation:{},dir:{},activationMode:{},modelValue:{},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},emits:[`update:modelValue`],setup(o,{emit:d}){let f=o,p=d,m=u(l(f,`class`),p);return(o,l)=>(e(),n(s(h),c({"data-slot":`tabs`},s(m),{class:s(g)(`flex flex-col gap-2`,f.class)}),{default:t(e=>[i(o.$slots,`default`,a(r(e)))]),_:3},16,[`class`]))}}),v=o({__name:`TabsContent`,props:{value:{},forceMount:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},setup(r){let a=r,o=l(a,`class`);return(r,l)=>(e(),n(s(p),c({"data-slot":`tabs-content`,class:s(g)(`flex-1 outline-none`,a.class)},s(o)),{default:t(()=>[i(r.$slots,`default`)]),_:3},16,[`class`]))}}),y=o({__name:`TabsList`,props:{loop:{type:Boolean},asChild:{type:Boolean},as:{},class:{},tabCount:{}},setup(r){let a=r,o=l(a,`class`,`tabCount`);return(l,u)=>(e(),n(s(d),c({"data-slot":`tabs-list`},s(o),{class:s(g)(`bg-muted text-muted-foreground inline-flex w-fit items-center justify-center rounded-lg p-[3px]`,r.tabCount&&r.tabCount>3?r.tabCount>6?`h-[85px] sm:h-9`:`h-[65px] sm:h-9`:`h-9`,a.class)}),{default:t(()=>[i(l.$slots,`default`)]),_:3},16,[`class`]))}}),b=o({__name:`TabsTrigger`,props:{value:{},disabled:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},setup(r){let a=r,o=f(l(a,`class`));return(r,l)=>(e(),n(s(m),c({"data-slot":`tabs-trigger`,class:s(g)(`inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all`,`text-muted-foreground hover:text-foreground`,`data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-md data-[state=active]:border-border`,`dark:data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-border dark:data-[state=active]:shadow-lg`,`focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:outline-1`,`disabled:pointer-events-none disabled:opacity-50`,`[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,a.class)},s(o)),{default:t(()=>[i(r.$slots,`default`)]),_:3},16,[`class`]))}});export{_ as i,y as n,v as r,b as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{En as e,Gn as t,Jn as n,Xt as r,_n as i,ln as a,mn as o,rn as s,tn as c}from"./game-logic-Bi1l7y4K.js";import{c as l}from"./vendor-others-DTUzJ7S-.js";import{$ as u,c as d,et as f,l as p,s as m,u as h}from"./vendor-reka-ui-BEHIKScj.js";import{G as g}from"./index-BT5w6et-.js";var _=c({__name:`Tabs`,props:{defaultValue:{},orientation:{},dir:{},activationMode:{},modelValue:{},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},emits:[`update:modelValue`],setup(c,{emit:d}){let f=c,p=d,m=u(l(f,`class`),p);return(c,l)=>(o(),r(t(h),a({"data-slot":`tabs`},t(m),{class:t(g)(`flex flex-col gap-2`,f.class)}),{default:e(e=>[i(c.$slots,`default`,n(s(e)))]),_:3},16,[`class`]))}}),v=c({__name:`TabsContent`,props:{value:{},forceMount:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},setup(n){let s=n,c=l(s,`class`);return(n,l)=>(o(),r(t(p),a({"data-slot":`tabs-content`,class:t(g)(`flex-1 outline-none`,s.class)},t(c)),{default:e(()=>[i(n.$slots,`default`)]),_:3},16,[`class`]))}}),y=c({__name:`TabsList`,props:{loop:{type:Boolean},asChild:{type:Boolean},as:{},class:{},tabCount:{}},setup(n){let s=n,c=l(s,`class`,`tabCount`);return(l,u)=>(o(),r(t(d),a({"data-slot":`tabs-list`},t(c),{class:t(g)(`bg-muted text-muted-foreground inline-flex w-fit items-center justify-center rounded-lg p-[3px]`,n.tabCount&&n.tabCount>3?n.tabCount>6?`h-[85px] sm:h-9`:`h-[65px] sm:h-9`:`h-9`,s.class)}),{default:e(()=>[i(l.$slots,`default`)]),_:3},16,[`class`]))}}),b=c({__name:`TabsTrigger`,props:{value:{},disabled:{type:Boolean},asChild:{type:Boolean},as:{},class:{}},setup(n){let s=n,c=f(l(s,`class`));return(n,l)=>(o(),r(t(m),a({"data-slot":`tabs-trigger`,class:t(g)(`inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all`,`text-muted-foreground hover:text-foreground`,`data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-md data-[state=active]:border-border`,`dark:data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-border dark:data-[state=active]:shadow-lg`,`focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:outline-1`,`disabled:pointer-events-none disabled:opacity-50`,`[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,s.class)},t(c)),{default:e(()=>[i(n.$slots,`default`)]),_:3},16,[`class`]))}});export{_ as i,y as n,v as r,b as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{En as e,Gn as t,Jn as n,Xt as r,_n as i,mn as a,rn as o,tn as s}from"./game-logic-Bi1l7y4K.js";import{a as c}from"./vendor-reka-ui-BEHIKScj.js";var l=s({__name:`TooltipProvider`,props:{delayDuration:{default:0},skipDelayDuration:{},disableHoverableContent:{type:Boolean},disableClosingTrigger:{type:Boolean},disabled:{type:Boolean},ignoreNonKeyboardFocus:{type:Boolean}},setup(s){let l=s;return(s,u)=>(a(),r(t(c),n(o(l)),{default:e(()=>[i(s.$slots,`default`)]),_:3},16))}});export{l as t};
|
||||
1
docs/assets/TooltipProvider-Cspoy8HK.js
Normal file
1
docs/assets/TooltipProvider-Cspoy8HK.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,Rn as t,cn as n,hn as r,kn as i,or as a,pn as o,rr as s}from"./game-logic-BBFCPVtv.js";import{a as c}from"./vendor-reka-ui-DR3m_AkV.js";var l=o({__name:`TooltipProvider`,props:{delayDuration:{default:0},skipDelayDuration:{},disableHoverableContent:{type:Boolean},disableClosingTrigger:{type:Boolean},disabled:{type:Boolean},ignoreNonKeyboardFocus:{type:Boolean}},setup(o){let l=o;return(o,u)=>(e(),n(s(c),a(r(l)),{default:t(()=>[i(o.$slots,`default`)]),_:3},16))}});export{l as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{$t as e,En as t,Gn as n,Jt as r,Qt as i,Xn as a,Yt as o,Zt as s,en as c,mn as l,tn as u}from"./game-logic-Bi1l7y4K.js";import{o as d}from"./vendor-vue-router-1sDnnIWZ.js";import{D as f,J as p}from"./vendor-icons-z9V6Jdbh.js";import{t as m}from"./CardDescription-BSLS07AU.js";import{H as h,K as g,W as _,_ as v,b as y,q as b,v as x,y as S}from"./index-BT5w6et-.js";import{t as C}from"./useGameConfig-b98LOtBY.js";var w={key:0,class:`fixed inset-0 z-50 bg-background/80 backdrop-blur-sm flex items-center justify-center p-4`},T={class:`flex justify-center mb-4`},E={class:`rounded-full bg-muted p-4`},D={class:`p-4 bg-muted rounded-lg space-y-2`},O={class:`text-sm font-medium text-center`},k={class:`flex items-center justify-center gap-2`},A={class:`text-base sm:text-lg font-bold`},j={key:0,class:`text-xs text-center text-muted-foreground`},M={class:`flex gap-2`},N=u({__name:`UnlockRequirement`,props:{requiredBuilding:{},requiredLevel:{}},setup(u){let N=u,P=d(),F=b(),{t:I}=g(),{BUILDINGS:L}=C(),R=r(()=>L.value[N.requiredBuilding]?.name||N.requiredBuilding),z=r(()=>F.currentPlanet&&F.currentPlanet.buildings[N.requiredBuilding]||0),B=r(()=>z.value>=N.requiredLevel),V=()=>{P.push(`/buildings`)};return(r,d)=>B.value?s(``,!0):(l(),i(`div`,w,[c(n(y),{class:`max-w-md w-full`},{default:t(()=>[c(n(x),{class:`text-center`},{default:t(()=>[o(`div`,T,[o(`div`,E,[c(n(f),{size:48,class:`text-muted-foreground`})])]),c(n(v),{class:`text-xl sm:text-2xl`},{default:t(()=>[e(a(n(I)(`common.featureLocked`)),1)]),_:1}),c(n(m),{class:`text-sm sm:text-base`},{default:t(()=>[e(a(n(I)(`common.unlockRequired`)),1)]),_:1})]),_:1}),c(n(S),{class:`space-y-4`},{default:t(()=>[o(`div`,D,[o(`p`,O,a(n(I)(`common.requiredBuilding`))+`:`,1),o(`div`,k,[o(`span`,A,a(R.value),1),c(n(h),{variant:`default`},{default:t(()=>[e(`Lv `+a(u.requiredLevel),1)]),_:1})]),z.value===void 0?s(``,!0):(l(),i(`p`,j,a(n(I)(`common.currentLevel`))+`: Lv `+a(z.value),1))]),o(`div`,M,[c(n(_),{onClick:V,class:`flex-1`},{default:t(()=>[c(n(p),{size:16,class:`mr-2`}),e(` `+a(n(I)(`common.goToBuildings`)),1)]),_:1})])]),_:1})]),_:1})]))}});export{N as t};
|
||||
1
docs/assets/UnlockRequirement-DJ9dN5JL.js
Normal file
1
docs/assets/UnlockRequirement-DJ9dN5JL.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,Rn as t,cr as n,dn as r,fn as i,ln as a,on as o,pn as s,rr as c,sn as l,un as u}from"./game-logic-BBFCPVtv.js";import{o as d}from"./vendor-vue-router-0mfLVB6Y.js";import{Q as f,k as p}from"./vendor-icons-Ccm2_GPI.js";import{t as m}from"./CardDescription-DduF5EVY.js";import{$ as h,J as g,K as _,X as v,d as y,f as b,m as x,p as S}from"./index-B4KMqZ_y.js";import{t as C}from"./useGameConfig-DLqfPHtW.js";var w={key:0,class:`fixed inset-0 z-50 bg-background/80 backdrop-blur-sm flex items-center justify-center p-4`},T={class:`flex justify-center mb-4`},E={class:`rounded-full bg-muted p-4`},D={class:`p-4 bg-muted rounded-lg space-y-2`},O={class:`text-sm font-medium text-center`},k={class:`flex items-center justify-center gap-2`},A={class:`text-base sm:text-lg font-bold`},j={key:0,class:`text-xs text-center text-muted-foreground`},M={class:`flex gap-2`},N=s({__name:`UnlockRequirement`,props:{requiredBuilding:{},requiredLevel:{}},setup(s){let N=s,P=d(),F=h(),{t:I}=v(),{BUILDINGS:L}=C(),R=o(()=>L.value[N.requiredBuilding]?.name||N.requiredBuilding),z=o(()=>F.currentPlanet&&F.currentPlanet.buildings[N.requiredBuilding]||0),B=o(()=>z.value>=N.requiredLevel),V=()=>{P.push(`/buildings`)};return(o,d)=>B.value?a(``,!0):(e(),u(`div`,w,[i(c(x),{class:`max-w-md w-full`},{default:t(()=>[i(c(b),{class:`text-center`},{default:t(()=>[l(`div`,T,[l(`div`,E,[i(c(p),{size:48,class:`text-muted-foreground`})])]),i(c(y),{class:`text-xl sm:text-2xl`},{default:t(()=>[r(n(c(I)(`common.featureLocked`)),1)]),_:1}),i(c(m),{class:`text-sm sm:text-base`},{default:t(()=>[r(n(c(I)(`common.unlockRequired`)),1)]),_:1})]),_:1}),i(c(S),{class:`space-y-4`},{default:t(()=>[l(`div`,D,[l(`p`,O,n(c(I)(`common.requiredBuilding`))+`:`,1),l(`div`,k,[l(`span`,A,n(R.value),1),i(c(_),{variant:`default`},{default:t(()=>[r(`Lv `+n(s.requiredLevel),1)]),_:1})]),z.value===void 0?a(``,!0):(e(),u(`p`,j,n(c(I)(`common.currentLevel`))+`: Lv `+n(z.value),1))]),l(`div`,M,[i(c(g),{onClick:V,class:`flex-1`},{default:t(()=>[i(c(f),{size:16,class:`mr-2`}),r(` `+n(c(I)(`common.goToBuildings`)),1)]),_:1})])]),_:1})]),_:1})]))}});export{N as t};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/empty-Ds0PR_Pd.js
Normal file
1
docs/assets/empty-Ds0PR_Pd.js
Normal file
@@ -0,0 +1 @@
|
||||
import{En as e,ar as t,kn as n,pn as r,rr as i,un as a}from"./game-logic-BBFCPVtv.js";import{z as o}from"./vendor-others-Bce0hM6h.js";import{Y as s}from"./index-B4KMqZ_y.js";var c=r({__name:`Empty`,props:{class:{}},setup(r){let o=r;return(r,c)=>(e(),a(`div`,{"data-slot":`empty`,class:t(i(s)(`flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12`,o.class))},[n(r.$slots,`default`)],2))}}),l=r({__name:`EmptyContent`,props:{class:{}},setup(r){let o=r;return(r,c)=>(e(),a(`div`,{"data-slot":`empty-content`,class:t(i(s)(`flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm`,o.class))},[n(r.$slots,`default`)],2))}}),u=r({__name:`EmptyDescription`,props:{class:{}},setup(r){return(r,o)=>(e(),a(`p`,{"data-slot":`empty-description`,class:t(i(s)(`text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4`,r.$attrs.class??``))},[n(r.$slots,`default`)],2))}}),d=r({__name:`EmptyHeader`,props:{class:{}},setup(r){let o=r;return(r,c)=>(e(),a(`div`,{"data-slot":`empty-header`,class:t(i(s)(`flex max-w-sm flex-col items-center gap-2 text-center`,o.class))},[n(r.$slots,`default`)],2))}}),f=[`data-variant`],p=r({__name:`EmptyMedia`,props:{class:{},variant:{}},setup(r){let o=r;return(c,l)=>(e(),a(`div`,{"data-slot":`empty-icon`,"data-variant":r.variant,class:t(i(s)(i(h)({variant:r.variant}),o.class))},[n(c.$slots,`default`)],10,f))}}),m=r({__name:`EmptyTitle`,props:{class:{}},setup(r){let o=r;return(r,c)=>(e(),a(`div`,{"data-slot":`empty-title`,class:t(i(s)(`text-lg font-medium tracking-tight`,o.class))},[n(r.$slots,`default`)],2))}});const h=o(`mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0`,{variants:{variant:{default:`bg-transparent`,icon:`bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6`}},defaultVariants:{variant:`default`}});export{l as a,u as i,p as n,c as o,d as r,m as t};
|
||||
File diff suppressed because one or more lines are too long
1
docs/assets/game-config-DRP-gbpS.js
Normal file
1
docs/assets/game-config-DRP-gbpS.js
Normal file
File diff suppressed because one or more lines are too long
120
docs/assets/game-i18n-BC9uiVvf.js
Normal file
120
docs/assets/game-i18n-BC9uiVvf.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3
docs/assets/game-logic-BBFCPVtv.js
Normal file
3
docs/assets/game-logic-BBFCPVtv.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
docs/assets/index-B4KMqZ_y.js
Normal file
2
docs/assets/index-B4KMqZ_y.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
docs/assets/index-CZ4UHUCa.css
Normal file
2
docs/assets/index-CZ4UHUCa.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
docs/assets/missileLogic-_46Nr-Ju.js
Normal file
1
docs/assets/missileLogic-_46Nr-Ju.js
Normal file
@@ -0,0 +1 @@
|
||||
import{_ as e,d as t,f as n,g as r,h as i,l as a,m as o,p as s,u as c}from"./game-logic-BBFCPVtv.js";import"./game-config-DRP-gbpS.js";export{a as applyMissileAttackResult,c as calculateMissileFlightTime,t as calculateMissileImpact,n as calculateMissileRange,s as calculateSystemDistance,o as createMissileAttack,i as executeMissileLaunch,r as isTargetInRange,e as validateMissileLaunch};
|
||||
@@ -1 +1 @@
|
||||
import{Jt as e}from"./game-logic-Bi1l7y4K.js";import{_ as t,d as n,f as r,h as i,l as a,n as o,r as s,s as c,u as l,v as u}from"./game-config-CG6z6nnH.js";import{K as d}from"./index-BT5w6et-.js";const f=()=>{let{t:f}=d(),p={[n.MetalMine]:`metalMine`,[n.CrystalMine]:`crystalMine`,[n.DeuteriumSynthesizer]:`deuteriumSynthesizer`,[n.SolarPlant]:`solarPlant`,[n.FusionReactor]:`fusionReactor`,[n.RoboticsFactory]:`roboticsFactory`,[n.NaniteFactory]:`naniteFactory`,[n.Shipyard]:`shipyard`,[n.ResearchLab]:`researchLab`,[n.MetalStorage]:`metalStorage`,[n.CrystalStorage]:`crystalStorage`,[n.DeuteriumTank]:`deuteriumTank`,[n.DarkMatterCollector]:`darkMatterCollector`,[n.DarkMatterTank]:`darkMatterTank`,[n.MissileSilo]:`missileSilo`,[n.Terraformer]:`terraformer`,[n.LunarBase]:`lunarBase`,[n.SensorPhalanx]:`sensorPhalanx`,[n.JumpGate]:`jumpGate`,[n.PlanetDestroyerFactory]:`planetDestroyerFactory`},m={[t.LightFighter]:`lightFighter`,[t.HeavyFighter]:`heavyFighter`,[t.Cruiser]:`cruiser`,[t.Battleship]:`battleship`,[t.Battlecruiser]:`battlecruiser`,[t.Bomber]:`bomber`,[t.Destroyer]:`destroyer`,[t.SmallCargo]:`smallCargo`,[t.LargeCargo]:`largeCargo`,[t.ColonyShip]:`colonyShip`,[t.Recycler]:`recycler`,[t.EspionageProbe]:`espionageProbe`,[t.SolarSatellite]:`solarSatellite`,[t.DarkMatterHarvester]:`darkMatterHarvester`,[t.Deathstar]:`deathstar`},h={[r.RocketLauncher]:`rocketLauncher`,[r.LightLaser]:`lightLaser`,[r.HeavyLaser]:`heavyLaser`,[r.GaussCannon]:`gaussCannon`,[r.IonCannon]:`ionCannon`,[r.PlasmaTurret]:`plasmaTurret`,[r.SmallShieldDome]:`smallShieldDome`,[r.LargeShieldDome]:`largeShieldDome`,[r.AntiBallisticMissile]:`antiBallisticMissile`,[r.InterplanetaryMissile]:`interplanetaryMissile`,[r.PlanetaryShield]:`planetaryShield`},g={[u.EnergyTechnology]:`energyTechnology`,[u.LaserTechnology]:`laserTechnology`,[u.IonTechnology]:`ionTechnology`,[u.HyperspaceTechnology]:`hyperspaceTechnology`,[u.PlasmaTechnology]:`plasmaTechnology`,[u.ComputerTechnology]:`computerTechnology`,[u.EspionageTechnology]:`espionageTechnology`,[u.WeaponsTechnology]:`weaponsTechnology`,[u.ShieldingTechnology]:`shieldingTechnology`,[u.ArmourTechnology]:`armourTechnology`,[u.Astrophysics]:`astrophysics`,[u.GravitonTechnology]:`gravitonTechnology`,[u.CombustionDrive]:`combustionDrive`,[u.ImpulseDrive]:`impulseDrive`,[u.HyperspaceDrive]:`hyperspaceDrive`,[u.DarkMatterTechnology]:`darkMatterTechnology`,[u.TerraformingTechnology]:`terraformingTechnology`,[u.PlanetDestructionTech]:`planetDestructionTech`},_={[i.Commander]:`commander`,[i.Admiral]:`admiral`,[i.Engineer]:`engineer`,[i.Geologist]:`geologist`,[i.Technocrat]:`technocrat`,[i.DarkMatterSpecialist]:`darkMatterSpecialist`};return{BUILDINGS:e(()=>{let e={};for(let[t,n]of Object.entries(o)){let r=t,i=p[r];e[r]={...n,name:f(`buildings.${i}`),description:f(`buildingDescriptions.${i}`)}}return e}),SHIPS:e(()=>{let e={};for(let[t,n]of Object.entries(a)){let r=t,i=m[r];e[r]={...n,name:f(`ships.${i}`),description:f(`shipDescriptions.${i}`)}}return e}),DEFENSES:e(()=>{let e={};for(let[t,n]of Object.entries(s)){let r=t,i=h[r];e[r]={...n,name:f(`defenses.${i}`),description:f(`defenseDescriptions.${i}`)}}return e}),TECHNOLOGIES:e(()=>{let e={};for(let[t,n]of Object.entries(l)){let r=t,i=g[r];e[r]={...n,name:f(`technologies.${i}`),description:f(`technologyDescriptions.${i}`)}}return e}),OFFICERS:e(()=>{let e={};for(let[t,n]of Object.entries(c)){let r=t,i=_[r];e[r]={...n,name:f(`officers.${i}`),description:f(`officerDescriptions.${i}`)}}return e})}};export{f as t};
|
||||
import{on as e}from"./game-logic-BBFCPVtv.js";import{_ as t,d as n,f as r,h as i,l as a,n as o,r as s,s as c,u as l,v as u}from"./game-config-DRP-gbpS.js";import{X as d}from"./index-B4KMqZ_y.js";const f=()=>{let{t:f}=d(),p={[n.MetalMine]:`metalMine`,[n.CrystalMine]:`crystalMine`,[n.DeuteriumSynthesizer]:`deuteriumSynthesizer`,[n.SolarPlant]:`solarPlant`,[n.FusionReactor]:`fusionReactor`,[n.RoboticsFactory]:`roboticsFactory`,[n.NaniteFactory]:`naniteFactory`,[n.Shipyard]:`shipyard`,[n.ResearchLab]:`researchLab`,[n.MetalStorage]:`metalStorage`,[n.CrystalStorage]:`crystalStorage`,[n.DeuteriumTank]:`deuteriumTank`,[n.DarkMatterCollector]:`darkMatterCollector`,[n.DarkMatterTank]:`darkMatterTank`,[n.MissileSilo]:`missileSilo`,[n.Terraformer]:`terraformer`,[n.LunarBase]:`lunarBase`,[n.SensorPhalanx]:`sensorPhalanx`,[n.JumpGate]:`jumpGate`,[n.PlanetDestroyerFactory]:`planetDestroyerFactory`},m={[t.LightFighter]:`lightFighter`,[t.HeavyFighter]:`heavyFighter`,[t.Cruiser]:`cruiser`,[t.Battleship]:`battleship`,[t.Battlecruiser]:`battlecruiser`,[t.Bomber]:`bomber`,[t.Destroyer]:`destroyer`,[t.SmallCargo]:`smallCargo`,[t.LargeCargo]:`largeCargo`,[t.ColonyShip]:`colonyShip`,[t.Recycler]:`recycler`,[t.EspionageProbe]:`espionageProbe`,[t.SolarSatellite]:`solarSatellite`,[t.DarkMatterHarvester]:`darkMatterHarvester`,[t.Deathstar]:`deathstar`},h={[r.RocketLauncher]:`rocketLauncher`,[r.LightLaser]:`lightLaser`,[r.HeavyLaser]:`heavyLaser`,[r.GaussCannon]:`gaussCannon`,[r.IonCannon]:`ionCannon`,[r.PlasmaTurret]:`plasmaTurret`,[r.SmallShieldDome]:`smallShieldDome`,[r.LargeShieldDome]:`largeShieldDome`,[r.AntiBallisticMissile]:`antiBallisticMissile`,[r.InterplanetaryMissile]:`interplanetaryMissile`,[r.PlanetaryShield]:`planetaryShield`},g={[u.EnergyTechnology]:`energyTechnology`,[u.LaserTechnology]:`laserTechnology`,[u.IonTechnology]:`ionTechnology`,[u.HyperspaceTechnology]:`hyperspaceTechnology`,[u.PlasmaTechnology]:`plasmaTechnology`,[u.ComputerTechnology]:`computerTechnology`,[u.EspionageTechnology]:`espionageTechnology`,[u.WeaponsTechnology]:`weaponsTechnology`,[u.ShieldingTechnology]:`shieldingTechnology`,[u.ArmourTechnology]:`armourTechnology`,[u.Astrophysics]:`astrophysics`,[u.GravitonTechnology]:`gravitonTechnology`,[u.CombustionDrive]:`combustionDrive`,[u.ImpulseDrive]:`impulseDrive`,[u.HyperspaceDrive]:`hyperspaceDrive`,[u.DarkMatterTechnology]:`darkMatterTechnology`,[u.TerraformingTechnology]:`terraformingTechnology`,[u.PlanetDestructionTech]:`planetDestructionTech`},_={[i.Commander]:`commander`,[i.Admiral]:`admiral`,[i.Engineer]:`engineer`,[i.Geologist]:`geologist`,[i.Technocrat]:`technocrat`,[i.DarkMatterSpecialist]:`darkMatterSpecialist`};return{BUILDINGS:e(()=>{let e={};for(let[t,n]of Object.entries(o)){let r=t,i=p[r];e[r]={...n,name:f(`buildings.${i}`),description:f(`buildingDescriptions.${i}`)}}return e}),SHIPS:e(()=>{let e={};for(let[t,n]of Object.entries(a)){let r=t,i=m[r];e[r]={...n,name:f(`ships.${i}`),description:f(`shipDescriptions.${i}`)}}return e}),DEFENSES:e(()=>{let e={};for(let[t,n]of Object.entries(s)){let r=t,i=h[r];e[r]={...n,name:f(`defenses.${i}`),description:f(`defenseDescriptions.${i}`)}}return e}),TECHNOLOGIES:e(()=>{let e={};for(let[t,n]of Object.entries(l)){let r=t,i=g[r];e[r]={...n,name:f(`technologies.${i}`),description:f(`technologyDescriptions.${i}`)}}return e}),OFFICERS:e(()=>{let e={};for(let[t,n]of Object.entries(c)){let r=t,i=_[r];e[r]={...n,name:f(`officers.${i}`),description:f(`officerDescriptions.${i}`)}}return e})}};export{f as t};
|
||||
1
docs/assets/vendor-icons-Ccm2_GPI.js
Normal file
1
docs/assets/vendor-icons-Ccm2_GPI.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
59
docs/assets/vendor-others-Bce0hM6h.js
Normal file
59
docs/assets/vendor-others-Bce0hM6h.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
docs/assets/vendor-reka-ui-DR3m_AkV.js
Normal file
8
docs/assets/vendor-reka-ui-DR3m_AkV.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
import{Bn as e,Cn as t,Gn as n,Jt as r,Ln as i,Wn as a,an as o,fn as s,nn as c,un as l,wn as u}from"./game-logic-Bi1l7y4K.js";import{a as d,i as f,l as p,o as m,r as h,s as g,u as _}from"./vendor-others-DTUzJ7S-.js";var v=f?window:void 0,y=f?window.document:void 0;f&&window.navigator,f&&window.location;function b(e){let t=a(e);return t?.$el??t}function x(...e){let t=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),i=r(()=>{let t=p(a(e[0])).filter(e=>e!=null);return t.every(e=>typeof e!=`string`)?t:void 0});return _(()=>[i.value?.map(e=>b(e))??[v].filter(e=>e!=null),p(a(i.value?e[1]:e[0])),p(n(i.value?e[2]:e[1])),a(i.value?e[3]:e[2])],([e,n,r,i],a,o)=>{if(!e?.length||!n?.length||!r?.length)return;let s=m(i)?{...i}:i,c=e.flatMap(e=>n.flatMap(n=>r.map(r=>t(e,n,r,s))));o(()=>{c.forEach(e=>e())})},{flush:`post`})}function S(){let t=e(!1),n=c();return n&&s(()=>{t.value=!0},n),t}function C(e){let t=S();return r(()=>(t.value,!!e()))}var w=Symbol(`vueuse-ssr-width`);function T(){let e=o()?h(w,null):null;return typeof e==`number`?e:void 0}function E(t,n={}){let{window:i=v,ssrWidth:o=T()}=n,s=C(()=>i&&`matchMedia`in i&&typeof i.matchMedia==`function`),c=e(typeof o==`number`),l=e(),d=e(!1);return u(()=>{if(c.value){c.value=!s.value,d.value=a(t).split(`,`).some(e=>{let t=e.includes(`not all`),n=e.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),r=e.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),i=!!(n||r);return n&&i&&(i=o>=g(n[1])),r&&i&&(i=o<=g(r[1])),t?!i:i});return}s.value&&(l.value=i.matchMedia(a(t)),d.value=l.value.matches)}),x(l,`change`,e=>{d.value=e.matches},{passive:!0}),r(()=>d.value)}function D(e){return JSON.parse(JSON.stringify(e))}function O(e,n,a,o={}){var s,u;let{clone:f=!1,passive:p=!1,eventName:m,deep:h=!1,defaultValue:g,shouldEmit:_}=o,v=c(),y=a||v?.emit||(v==null||(s=v.$emit)==null?void 0:s.bind(v))||(v==null||(u=v.proxy)==null||(u=u.$emit)==null?void 0:u.bind(v?.proxy)),b=m;n||=`modelValue`,b||=`update:${n.toString()}`;let x=e=>f?typeof f==`function`?f(e):D(e):e,S=()=>d(e[n])?x(e[n]):g,C=e=>{_?_(e)&&y(b,e):y(b,e)};if(p){let r=i(S()),a=!1;return t(()=>e[n],e=>{a||(a=!0,r.value=x(e),l(()=>a=!1))}),t(r,t=>{!a&&(t!==e[n]||h)&&C(t)},{deep:h}),r}else return r({get(){return S()},set(e){C(e)}})}export{O as i,x as n,E as r,y as t};
|
||||
1
docs/assets/vendor-vueuse-Djj-oRw9.js
Normal file
1
docs/assets/vendor-vueuse-Djj-oRw9.js
Normal file
@@ -0,0 +1 @@
|
||||
import{Fn as e,In as t,Qn as n,Sn as r,Yn as i,_n as a,mn as o,nr as s,on as c,rr as l,wn as u}from"./game-logic-BBFCPVtv.js";import{a as d,c as f,d as p,i as m,o as h,s as g,u as _}from"./vendor-others-Bce0hM6h.js";var v=d?window:void 0,y=d?window.document:void 0;d&&window.navigator,d&&window.location;function b(e){let t=s(e);return t?.$el??t}function x(...e){let t=(e,t,n,r)=>(e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)),n=c(()=>{let t=_(s(e[0])).filter(e=>e!=null);return t.every(e=>typeof e!=`string`)?t:void 0});return p(()=>[n.value?.map(e=>b(e))??[v].filter(e=>e!=null),_(s(n.value?e[1]:e[0])),_(l(n.value?e[2]:e[1])),s(n.value?e[3]:e[2])],([e,n,r,i],a,o)=>{if(!e?.length||!n?.length||!r?.length)return;let s=g(i)?{...i}:i,c=e.flatMap(e=>n.flatMap(n=>r.map(r=>t(e,n,r,s))));o(()=>{c.forEach(e=>e())})},{flush:`post`})}function S(){let e=n(!1),t=o();return t&&u(()=>{e.value=!0},t),e}function C(e){let t=S();return c(()=>(t.value,!!e()))}var w=Symbol(`vueuse-ssr-width`);function T(){let e=a()?m(w,null):null;return typeof e==`number`?e:void 0}function E(e,r={}){let{window:i=v,ssrWidth:a=T()}=r,o=C(()=>i&&`matchMedia`in i&&typeof i.matchMedia==`function`),l=n(typeof a==`number`),u=n(),d=n(!1);return t(()=>{if(l.value){l.value=!o.value,d.value=s(e).split(`,`).some(e=>{let t=e.includes(`not all`),n=e.match(/\(\s*min-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),r=e.match(/\(\s*max-width:\s*(-?\d+(?:\.\d*)?[a-z]+\s*)\)/),i=!!(n||r);return n&&i&&(i=a>=f(n[1])),r&&i&&(i=a<=f(r[1])),t?!i:i});return}o.value&&(u.value=i.matchMedia(s(e)),d.value=u.value.matches)}),x(u,`change`,e=>{d.value=e.matches},{passive:!0}),c(()=>d.value)}function D(e){return JSON.parse(JSON.stringify(e))}function O(t,n,a,s={}){var l,u;let{clone:d=!1,passive:f=!1,eventName:p,deep:m=!1,defaultValue:g,shouldEmit:_}=s,v=o(),y=a||v?.emit||(v==null||(l=v.$emit)==null?void 0:l.bind(v))||(v==null||(u=v.proxy)==null||(u=u.$emit)==null?void 0:u.bind(v?.proxy)),b=p;n||=`modelValue`,b||=`update:${n.toString()}`;let x=e=>d?typeof d==`function`?d(e):D(e):e,S=()=>h(t[n])?x(t[n]):g,C=e=>{_?_(e)&&y(b,e):y(b,e)};if(f){let a=i(S()),o=!1;return e(()=>t[n],e=>{o||(o=!0,a.value=x(e),r(()=>o=!1))}),e(a,e=>{!o&&(e!==t[n]||m)&&C(e)},{deep:m}),a}else return c({get(){return S()},set(e){C(e)}})}export{O as i,x as n,E as r,y as t};
|
||||
@@ -7,21 +7,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="icon" type="image/svg+xml" href="./logo.svg" />
|
||||
<title>OGame-Vue-Ts</title>
|
||||
<script type="module" crossorigin src="./assets/index-BT5w6et-.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-config-CG6z6nnH.js">
|
||||
<script type="module" crossorigin src="./assets/index-B4KMqZ_y.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-config-DRP-gbpS.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-i18n-BC9uiVvf.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-CIDIeb-o.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-logic-Bi1l7y4K.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-others-DTUzJ7S-.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-reka-ui-BEHIKScj.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vueuse-CJcfYqoW.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-logic-BBFCPVtv.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-others-Bce0hM6h.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-reka-ui-DR3m_AkV.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vueuse-Djj-oRw9.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-crypto-CQM8pryk.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-utils-BlvnUqQX.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-icons-z9V6Jdbh.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-pinia-DqhKuBjp.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-router-1sDnnIWZ.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/game-i18n-Dr0JspcV.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-icons-Ccm2_GPI.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-pinia-L_8CmDWM.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/vendor-vue-router-0mfLVB6Y.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/vendor-others-BMPyaZWq.css">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-D0T1QtIu.css">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-CZ4UHUCa.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
"email": "1962257451@qq.com"
|
||||
},
|
||||
"private": true,
|
||||
"version": "1.2.0",
|
||||
"buildDate": "2025/12/15 08:22:52",
|
||||
"version": "1.2.5",
|
||||
"buildDate": "2025/12/15 20:03:44",
|
||||
"main": "dist-electron/main.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -29,6 +29,7 @@
|
||||
"file-saver": "^2.0.5",
|
||||
"finalhandler": "^2.1.1",
|
||||
"lucide-vue-next": "^0.556.0",
|
||||
"marked": "^17.0.1",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"reka-ui": "^2.6.1",
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@@ -38,6 +38,9 @@ importers:
|
||||
lucide-vue-next:
|
||||
specifier: ^0.556.0
|
||||
version: 0.556.0(vue@3.5.25(typescript@5.9.3))
|
||||
marked:
|
||||
specifier: ^17.0.1
|
||||
version: 17.0.1
|
||||
pinia:
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
||||
@@ -1744,6 +1747,11 @@ packages:
|
||||
resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==}
|
||||
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
|
||||
|
||||
marked@17.0.1:
|
||||
resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==}
|
||||
engines: {node: '>= 20'}
|
||||
hasBin: true
|
||||
|
||||
matcher@3.0.0:
|
||||
resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4271,6 +4279,8 @@ snapshots:
|
||||
- bluebird
|
||||
- supports-color
|
||||
|
||||
marked@17.0.1: {}
|
||||
|
||||
matcher@3.0.0:
|
||||
dependencies:
|
||||
escape-string-regexp: 4.0.0
|
||||
|
||||
1192
src/App.vue
1192
src/App.vue
@@ -13,15 +13,67 @@
|
||||
<!-- 星球信息 -->
|
||||
<SidebarGroup v-if="planet" class="border-b group-data-[collapsible=icon]:hidden">
|
||||
<div class="px-4 py-3 space-y-2 text-sm">
|
||||
<div>
|
||||
<p class="font-semibold mb-1">
|
||||
{{ planet.name }}
|
||||
<Badge v-if="planet.isMoon" variant="secondary" class="ml-1 text-xs">{{ t('planet.moon') }}</Badge>
|
||||
</p>
|
||||
<p class="text-muted-foreground text-xs">
|
||||
[{{ planet.position.galaxy }}:{{ planet.position.system }}:{{ planet.position.position }}]
|
||||
</p>
|
||||
</div>
|
||||
<!-- 星球切换器 -->
|
||||
<Popover>
|
||||
<PopoverTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="w-full justify-between h-auto px-3 py-2.5 border-2 hover:bg-accent hover:border-primary transition-colors"
|
||||
>
|
||||
<div class="flex items-start gap-2.5 flex-1 min-w-0">
|
||||
<Globe class="h-5 w-5 flex-shrink-0 mt-0.5 text-primary" />
|
||||
<div class="flex-1 min-w-0 text-left">
|
||||
<div class="text-[10px] text-muted-foreground uppercase tracking-wider mb-0.5">
|
||||
{{ t('planet.currentPlanet') }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 mb-0.5">
|
||||
<span class="truncate font-semibold text-sm">{{ planet.name }}</span>
|
||||
<Badge v-if="planet.isMoon" variant="secondary" class="text-[10px] px-1 py-0 h-4">
|
||||
{{ t('planet.moon') }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="text-[11px] text-muted-foreground">
|
||||
[{{ planet.position.galaxy }}:{{ planet.position.system }}:{{ planet.position.position }}]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronsUpDown class="h-4 w-4 flex-shrink-0 text-muted-foreground ml-2" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-72 p-0" side="bottom" align="start">
|
||||
<div class="p-2">
|
||||
<div class="px-2 py-1.5 mb-1 text-xs font-semibold text-muted-foreground">
|
||||
{{ t('planet.switchPlanet') }}
|
||||
</div>
|
||||
<div class="space-y-0.5 max-h-80 overflow-y-auto">
|
||||
<Button
|
||||
v-for="p in gameStore.player.planets"
|
||||
:key="p.id"
|
||||
@click="switchToPlanet(p.id)"
|
||||
:variant="p.id === planet.id ? 'secondary' : 'ghost'"
|
||||
class="w-full justify-start h-auto py-2 px-2"
|
||||
size="sm"
|
||||
>
|
||||
<div class="flex items-start gap-2 w-full min-w-0">
|
||||
<Globe class="h-4 w-4 flex-shrink-0 mt-0.5" :class="p.id === planet.id ? 'text-primary' : ''" />
|
||||
<div class="flex-1 min-w-0 text-left">
|
||||
<div class="flex items-center gap-1.5 mb-0.5">
|
||||
<span class="truncate font-medium text-sm">{{ p.name }}</span>
|
||||
<Badge v-if="p.isMoon" variant="outline" class="text-[10px] px-1 py-0 h-4">
|
||||
{{ t('planet.moon') }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="text-[11px] text-muted-foreground">
|
||||
[{{ p.position.galaxy }}:{{ p.position.system }}:{{ p.position.position }}]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<!-- 玩家积分显示 -->
|
||||
<div class="bg-muted/50 rounded-lg p-2">
|
||||
<div class="flex items-center justify-between">
|
||||
@@ -50,9 +102,19 @@
|
||||
<component :is="item.icon" />
|
||||
<span>{{ item.name.value }}</span>
|
||||
<!-- 未读消息数量 -->
|
||||
<SidebarMenuBadge v-if="item.path === '/messages' && unreadMessagesCount > 0">
|
||||
<SidebarMenuBadge
|
||||
v-if="item.path === '/messages' && unreadMessagesCount > 0"
|
||||
class="bg-destructive text-destructive-foreground"
|
||||
>
|
||||
{{ unreadMessagesCount }}
|
||||
</SidebarMenuBadge>
|
||||
<!-- 正在执行的舰队任务数量 -->
|
||||
<SidebarMenuBadge
|
||||
v-if="item.path === '/fleet' && activeFleetMissionsCount > 0"
|
||||
class="bg-primary text-primary-foreground"
|
||||
>
|
||||
{{ activeFleetMissionsCount }}
|
||||
</SidebarMenuBadge>
|
||||
</RouterLink>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
@@ -72,7 +134,11 @@
|
||||
<span>{{ localeNames[gameStore.locale] }}</span>
|
||||
</SidebarMenuButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="w-48 p-2" side="right" align="end">
|
||||
<PopoverContent
|
||||
class="w-48 p-2"
|
||||
:side="sidebarOpen || innerWidth < 768 ? 'top' : 'right'"
|
||||
:align="sidebarOpen || innerWidth < 768 ? 'center' : 'end'"
|
||||
>
|
||||
<div class="space-y-1">
|
||||
<Button
|
||||
v-for="locale in locales"
|
||||
@@ -111,49 +177,139 @@
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<SidebarInset>
|
||||
<div class="flex flex-col h-full overflow-hidden">
|
||||
<!-- 顶部资源栏 -->
|
||||
<header v-if="planet" class="bg-card border-b px-4 sm:px-6 py-6.5 shadow-md">
|
||||
<div class="flex items-center justify-between gap-3 sm:gap-6">
|
||||
<!-- 汉堡菜单(移动端)- 左侧占位 -->
|
||||
<div class="lg:flex-1">
|
||||
<SidebarTrigger class="lg:hidden" />
|
||||
</div>
|
||||
<div class="flex flex-col h-full overflow-hidden pt-[60px]">
|
||||
<!-- 顶部资源栏 - 固定定位 -->
|
||||
<header
|
||||
v-if="planet"
|
||||
class="fixed top-0 right-0 left-0 z-40 bg-card border-b px-4 sm:px-6 py-3 shadow-md"
|
||||
:class="sidebarOpen ? 'lg:left-[var(--sidebar-width)]' : 'lg:left-[var(--sidebar-width-icon)]'"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<!-- 第一行:菜单、资源预览、状态 -->
|
||||
<div class="grid items-center gap-3 sm:gap-6" style="grid-template-columns: auto 1fr auto">
|
||||
<!-- 左侧:汉堡菜单(移动端)/ 占位(PC端) -->
|
||||
<div>
|
||||
<SidebarTrigger class="lg:hidden" />
|
||||
</div>
|
||||
|
||||
<!-- 资源显示 - PC端居中 -->
|
||||
<div class="flex items-center gap-3 sm:gap-6 flex-1 lg:flex-none overflow-x-auto lg:justify-center">
|
||||
<div v-for="resourceType in resourceTypes" :key="resourceType.key" class="flex items-center gap-1.5 sm:gap-2 flex-shrink-0">
|
||||
<ResourceIcon :type="resourceType.key" size="md" />
|
||||
<div class="min-w-0">
|
||||
<!-- 所有资源统一显示:当前值/容量 -->
|
||||
<p
|
||||
class="text-xs sm:text-sm font-medium truncate"
|
||||
:class="getResourceColor(planet.resources[resourceType.key], capacity?.[resourceType.key] || Infinity)"
|
||||
>
|
||||
{{ formatNumber(planet.resources[resourceType.key]) }} / {{ formatNumber(capacity?.[resourceType.key] || 0) }}
|
||||
</p>
|
||||
<p class="text-[10px] sm:text-xs text-muted-foreground truncate">
|
||||
+{{ formatNumber(Math.round((production?.[resourceType.key] || 0) / 60)) }}/{{ t('resources.perMinute') }}
|
||||
</p>
|
||||
<!-- 资源显示 - PC端居中,移动端可折叠 -->
|
||||
<div :class="['flex items-center gap-3 sm:gap-6 justify-center', resourceBarExpanded ? 'hidden' : 'overflow-x-auto']">
|
||||
<div v-for="resourceType in resourceTypes" :key="resourceType.key" class="flex items-center gap-1.5 sm:gap-2 flex-shrink-0">
|
||||
<ResourceIcon :type="resourceType.key" size="md" />
|
||||
<div class="min-w-0">
|
||||
<!-- 电力显示净产量和效率 -->
|
||||
<template v-if="resourceType.key === 'energy'">
|
||||
<p
|
||||
class="text-xs sm:text-sm font-medium truncate"
|
||||
:class="netEnergy >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'"
|
||||
>
|
||||
{{ netEnergy >= 0 ? '+' : '' }}{{ formatNumber(netEnergy) }}
|
||||
</p>
|
||||
<p class="text-[10px] sm:text-xs text-muted-foreground truncate">
|
||||
{{ formatNumber(production?.energy || 0) }} / {{ formatNumber(energyConsumption) }}
|
||||
</p>
|
||||
</template>
|
||||
<!-- 其他资源统一显示:当前值/容量 -->
|
||||
<template v-else>
|
||||
<p
|
||||
class="text-xs sm:text-sm font-medium truncate"
|
||||
:class="getResourceColor(planet.resources[resourceType.key], capacity?.[resourceType.key] || Infinity)"
|
||||
>
|
||||
{{ formatNumber(planet.resources[resourceType.key]) }} / {{ formatNumber(capacity?.[resourceType.key] || 0) }}
|
||||
</p>
|
||||
<p class="text-[10px] sm:text-xs text-muted-foreground truncate">
|
||||
+{{ formatNumber(Math.round((production?.[resourceType.key] || 0) / 60)) }}/{{ t('resources.perMinute') }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧状态 - 右侧占位 -->
|
||||
<div class="flex items-center gap-2 sm:gap-4 flex-shrink-0 lg:flex-1 lg:justify-end">
|
||||
<!-- 建造队列状态 -->
|
||||
<div v-if="planet.buildQueue.length > 0" class="flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm">
|
||||
<div class="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
<span class="text-muted-foreground hidden sm:inline">{{ t('queue.building') }}</span>
|
||||
</div>
|
||||
<div v-if="gameStore.player.researchQueue.length > 0" class="flex items-center gap-1.5 sm:gap-2 text-xs sm:text-sm">
|
||||
<div class="h-2 w-2 rounded-full bg-blue-500 animate-pulse" />
|
||||
<span class="text-muted-foreground hidden sm:inline">{{ t('queue.researching') }}</span>
|
||||
<!-- 右侧:展开按钮(仅移动端) + 状态 -->
|
||||
<div class="flex items-center gap-2 sm:gap-3 flex-shrink-0 justify-end">
|
||||
<!-- 移动端展开按钮 -->
|
||||
<Button @click="resourceBarExpanded = !resourceBarExpanded" variant="ghost" size="sm" class="lg:hidden h-8 w-8 p-0">
|
||||
<ChevronDown v-if="!resourceBarExpanded" class="h-4 w-4" />
|
||||
<ChevronUp v-else class="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<!-- 建造队列状态 -->
|
||||
<div v-if="planet.buildQueue.length > 0" class="flex items-center gap-1.5 text-xs sm:text-sm">
|
||||
<div class="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
<span class="text-muted-foreground hidden sm:inline">{{ t('queue.building') }}</span>
|
||||
</div>
|
||||
<div v-if="gameStore.player.researchQueue.length > 0" class="flex items-center gap-1.5 text-xs sm:text-sm">
|
||||
<div class="h-2 w-2 rounded-full bg-blue-500 animate-pulse" />
|
||||
<span class="text-muted-foreground hidden sm:inline">{{ t('queue.researching') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 展开的资源详情(仅移动端且展开时显示) - absolute定位覆盖在内容上,带过渡动画 -->
|
||||
<Transition
|
||||
enter-active-class="transition-all duration-300 ease-out"
|
||||
enter-from-class="opacity-0 -translate-y-2"
|
||||
enter-to-class="opacity-100 translate-y-0"
|
||||
leave-active-class="transition-all duration-200 ease-in"
|
||||
leave-from-class="opacity-100 translate-y-0"
|
||||
leave-to-class="opacity-0 -translate-y-2"
|
||||
>
|
||||
<div
|
||||
v-if="planet && resourceBarExpanded"
|
||||
class="fixed top-[60px] right-0 left-0 z-30 bg-card border-b px-4 py-3 shadow-md lg:hidden"
|
||||
:class="sidebarOpen ? 'lg:left-[var(--sidebar-width)]' : 'lg:left-[var(--sidebar-width-icon)]'"
|
||||
>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div v-for="resourceType in resourceTypes" :key="resourceType.key" class="bg-muted/50 rounded-lg p-2.5">
|
||||
<div class="flex items-center justify-center gap-2 mb-1.5">
|
||||
<ResourceIcon :type="resourceType.key" size="md" />
|
||||
<span class="text-xs font-medium text-muted-foreground">{{ t(`resources.${resourceType.key}`) }}</span>
|
||||
</div>
|
||||
<div class="space-y-0.5 text-center">
|
||||
<!-- 电力显示净产量和效率 -->
|
||||
<template v-if="resourceType.key === 'energy'">
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="netEnergy >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'"
|
||||
>
|
||||
{{ netEnergy >= 0 ? '+' : '' }}{{ formatNumber(netEnergy) }}
|
||||
</p>
|
||||
<p class="text-[10px] text-muted-foreground">
|
||||
{{ t('resources.production') }}: {{ formatNumber(production?.energy || 0) }} / {{ formatNumber(energyConsumption) }}
|
||||
</p>
|
||||
</template>
|
||||
<!-- 其他资源统一显示:当前值/容量 -->
|
||||
<template v-else>
|
||||
<p
|
||||
class="text-sm font-semibold"
|
||||
:class="getResourceColor(planet.resources[resourceType.key], capacity?.[resourceType.key] || Infinity)"
|
||||
>
|
||||
{{ formatNumber(planet.resources[resourceType.key]) }}
|
||||
</p>
|
||||
<p class="text-[10px] text-muted-foreground">
|
||||
{{ t('resources.capacity') }}: {{ formatNumber(capacity?.[resourceType.key] || 0) }}
|
||||
</p>
|
||||
<p class="text-[10px] text-muted-foreground">
|
||||
{{ t('resources.production') }}: +{{ formatNumber(Math.round((production?.[resourceType.key] || 0) / 60)) }}/{{
|
||||
t('resources.perMinute')
|
||||
}}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 即将到来的敌对舰队警告 -->
|
||||
<IncomingFleetAlerts
|
||||
v-if="gameStore.player.incomingFleetAlerts && gameStore.player.incomingFleetAlerts.length > 0"
|
||||
:alerts="gameStore.player.incomingFleetAlerts"
|
||||
@mark-as-read="removeIncomingFleetAlert"
|
||||
/>
|
||||
|
||||
<!-- 建造队列 -->
|
||||
<div
|
||||
v-if="planet && (planet.buildQueue.length > 0 || gameStore.player.researchQueue.length > 0)"
|
||||
@@ -232,11 +388,13 @@
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{{ confirmDialogTitle }}</AlertDialogTitle>
|
||||
<AlertDialogDescription>{{ confirmDialogMessage }}</AlertDialogDescription>
|
||||
<AlertDialogDescription class="whitespace-pre-line">
|
||||
{{ confirmDialogMessage }}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{{ t('common.cancel') }}</AlertDialogCancel>
|
||||
<AlertDialogAction @click="handleConfirmAction">{{ t('common.confirm') }}</AlertDialogAction>
|
||||
<AlertDialogAction @click="handleConfirmDialogConfirm">{{ t('common.confirm') }}</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
@@ -244,15 +402,20 @@
|
||||
<!-- 详情弹窗 -->
|
||||
<DetailDialog />
|
||||
|
||||
<!-- 更新弹窗 -->
|
||||
<UpdateDialog v-model:open="showUpdateDialog" :version-info="updateInfo" />
|
||||
|
||||
<!-- Toast 通知 -->
|
||||
<Sonner position="top-center" />
|
||||
</SidebarProvider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, computed, ref } from 'vue'
|
||||
import { onMounted, onUnmounted, computed, ref, watch } from 'vue'
|
||||
import { RouterView, RouterLink } from 'vue-router'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useUniverseStore } from '@/stores/universeStore'
|
||||
import { useNPCStore } from '@/stores/npcStore'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { localeNames, detectBrowserLocale, type Locale } from '@/locales'
|
||||
@@ -260,6 +423,7 @@
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover'
|
||||
import IncomingFleetAlerts from '@/components/IncomingFleetAlerts.vue'
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
@@ -275,8 +439,6 @@
|
||||
SidebarTrigger
|
||||
} from '@/components/ui/sidebar'
|
||||
import ResourceIcon from '@/components/ResourceIcon.vue'
|
||||
import DetailDialog from '@/components/DetailDialog.vue'
|
||||
import Sonner from '@/components/ui/sonner/Sonner.vue'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -287,6 +449,12 @@
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import DetailDialog from '@/components/DetailDialog.vue'
|
||||
import UpdateDialog from '@/components/UpdateDialog.vue'
|
||||
import Sonner from '@/components/ui/sonner/Sonner.vue'
|
||||
import { MissionType } from '@/types/game'
|
||||
import type { BuildQueueItem, FleetMission, NPC, IncomingFleetAlert, MissileAttack } from '@/types/game'
|
||||
import type { VersionInfo } from '@/utils/versionCheck'
|
||||
import { formatNumber, formatTime, getResourceColor } from '@/utils/format'
|
||||
import {
|
||||
Moon,
|
||||
@@ -304,62 +472,737 @@
|
||||
Languages,
|
||||
Settings,
|
||||
Wrench,
|
||||
ChevronsLeft
|
||||
ChevronsLeft,
|
||||
ChevronsUpDown,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Handshake
|
||||
} from 'lucide-vue-next'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
import * as planetLogic from '@/logic/planetLogic'
|
||||
import * as officerLogic from '@/logic/officerLogic'
|
||||
import * as buildingValidation from '@/logic/buildingValidation'
|
||||
import * as resourceLogic from '@/logic/resourceLogic'
|
||||
import { useGameLifecycle } from '@/composables/useGameLifecycle'
|
||||
import { useMissionHandler } from '@/composables/useMissionHandler'
|
||||
import { useNPCHandler } from '@/composables/useNPCHandler'
|
||||
import { useQueueHandler } from '@/composables/useQueueHandler'
|
||||
import { useGameUpdate } from '@/composables/useGameUpdate'
|
||||
import { migrateGameData } from '@/utils/migration'
|
||||
import * as researchValidation from '@/logic/researchValidation'
|
||||
import * as fleetLogic from '@/logic/fleetLogic'
|
||||
import * as shipLogic from '@/logic/shipLogic'
|
||||
import * as npcGrowthLogic from '@/logic/npcGrowthLogic'
|
||||
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
||||
import * as diplomaticLogic from '@/logic/diplomaticLogic'
|
||||
import pkg from '../package.json'
|
||||
import { toast } from 'vue-sonner'
|
||||
import { migrateGameData } from '@/utils/migration'
|
||||
import { checkLatestVersion } from '@/utils/versionCheck'
|
||||
|
||||
// 执行数据迁移(在 store 初始化之前)
|
||||
migrateGameData()
|
||||
|
||||
const gameStore = useGameStore()
|
||||
const universeStore = useUniverseStore()
|
||||
const npcStore = useNPCStore()
|
||||
const { isDark } = useTheme()
|
||||
const { t } = useI18n()
|
||||
|
||||
// ConfirmDialog 状态
|
||||
const confirmDialogOpen = ref(false)
|
||||
const confirmDialogTitle = ref('')
|
||||
const confirmDialogMessage = ref('')
|
||||
const innerWidth = computed(() => window.innerWidth)
|
||||
const confirmDialogAction = ref<(() => void) | null>(null)
|
||||
|
||||
// 更新弹窗状态
|
||||
const showUpdateDialog = ref(false)
|
||||
const updateInfo = ref<VersionInfo | null>(null)
|
||||
|
||||
const handleConfirmDialogConfirm = () => {
|
||||
if (confirmDialogAction.value) {
|
||||
confirmDialogAction.value()
|
||||
}
|
||||
confirmDialogOpen.value = false
|
||||
}
|
||||
|
||||
// 所有可用的语言选项
|
||||
const locales: Locale[] = ['zh-CN', 'zh-TW', 'en', 'de', 'ru', 'ko', 'ja']
|
||||
|
||||
// 侧边栏状态(不持久化,根据屏幕尺寸初始化)
|
||||
// PC端(≥1024px)默认打开,移动端默认关闭
|
||||
const sidebarOpen = ref(window.innerWidth >= 1024)
|
||||
|
||||
// 初始化 composables
|
||||
const { initGame } = useGameLifecycle()
|
||||
// 移动端资源栏展开状态
|
||||
const resourceBarExpanded = ref(false)
|
||||
|
||||
const { processMissionArrival, processMissionReturn } = useMissionHandler(t)
|
||||
const initGame = async () => {
|
||||
const shouldInit = gameLogic.shouldInitializeGame(gameStore.player.planets)
|
||||
if (!shouldInit) {
|
||||
const now = Date.now()
|
||||
|
||||
const { processNPCMissionArrival, processNPCMissionReturn, updateNPCGrowth, updateNPCBehavior } = useNPCHandler()
|
||||
// 计算离线收益(直接同步计算)
|
||||
const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, now)
|
||||
gameStore.player.planets.forEach(planet => {
|
||||
resourceLogic.updatePlanetResources(planet, now, bonuses)
|
||||
})
|
||||
|
||||
const { handleCancelBuild, handleCancelResearch, getItemName, getRemainingTime, getQueueProgress } = useQueueHandler(
|
||||
t,
|
||||
confirmDialogOpen,
|
||||
confirmDialogTitle,
|
||||
confirmDialogMessage,
|
||||
confirmDialogAction
|
||||
)
|
||||
// 只在没有NPC星球时才生成(首次加载已有玩家数据时)
|
||||
if (Object.keys(universeStore.planets).length === 0) {
|
||||
generateNPCPlanets()
|
||||
}
|
||||
return
|
||||
}
|
||||
gameStore.player = gameLogic.initializePlayer(gameStore.player.id, t('common.playerName'))
|
||||
const initialPlanet = planetLogic.createInitialPlanet(gameStore.player.id, t('planet.homePlanet'))
|
||||
gameStore.player.planets = [initialPlanet]
|
||||
gameStore.currentPlanetId = initialPlanet.id
|
||||
// 新玩家初始化时生成NPC星球
|
||||
generateNPCPlanets()
|
||||
}
|
||||
|
||||
const { updateGame } = useGameUpdate(
|
||||
processMissionArrival,
|
||||
processMissionReturn,
|
||||
processNPCMissionArrival,
|
||||
processNPCMissionReturn,
|
||||
updateNPCGrowth,
|
||||
updateNPCBehavior
|
||||
)
|
||||
const generateNPCPlanets = () => {
|
||||
const npcCount = 200
|
||||
for (let i = 0; i < npcCount; i++) {
|
||||
const position = gameLogic.generateRandomPosition()
|
||||
const key = gameLogic.generatePositionKey(position.galaxy, position.system, position.position)
|
||||
if (universeStore.planets[key]) continue
|
||||
const npcPlanet = planetLogic.createNPCPlanet(i, position, t('planet.planetPrefix'))
|
||||
universeStore.planets[key] = npcPlanet
|
||||
}
|
||||
}
|
||||
|
||||
const updateGame = async () => {
|
||||
if (gameStore.isPaused) return
|
||||
const now = Date.now()
|
||||
gameStore.gameTime = now
|
||||
// 检查军官过期
|
||||
gameLogic.checkOfficersExpiration(gameStore.player.officers, now)
|
||||
// 处理游戏更新(建造队列、研究队列等)
|
||||
const result = gameLogic.processGameUpdate(gameStore.player, now)
|
||||
gameStore.player.researchQueue = result.updatedResearchQueue
|
||||
// 处理舰队任务
|
||||
gameStore.player.fleetMissions.forEach(mission => {
|
||||
if (mission.status === 'outbound' && now >= mission.arrivalTime) {
|
||||
processMissionArrival(mission)
|
||||
} else if (mission.status === 'returning' && mission.returnTime && now >= mission.returnTime) {
|
||||
processMissionReturn(mission)
|
||||
}
|
||||
})
|
||||
|
||||
// 处理导弹攻击任务(使用反向循环以便安全删除)
|
||||
for (let i = gameStore.player.missileAttacks.length - 1; i >= 0; i--) {
|
||||
const missileAttack = gameStore.player.missileAttacks[i]
|
||||
if (missileAttack && missileAttack.status === 'flying' && now >= missileAttack.arrivalTime) {
|
||||
await processMissileAttackArrival(missileAttack)
|
||||
// 导弹攻击是单程的,到达后直接从数组中移除
|
||||
gameStore.player.missileAttacks.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理NPC舰队任务
|
||||
npcStore.npcs.forEach(npc => {
|
||||
if (npc.fleetMissions) {
|
||||
npc.fleetMissions.forEach(mission => {
|
||||
if (mission.status === 'outbound' && now >= mission.arrivalTime) {
|
||||
processNPCMissionArrival(npc, mission)
|
||||
} else if (mission.status === 'returning' && mission.returnTime && now >= mission.returnTime) {
|
||||
processNPCMissionReturn(npc, mission)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// NPC成长系统更新
|
||||
updateNPCGrowth(1)
|
||||
|
||||
// NPC行为系统更新(侦查和攻击决策)
|
||||
updateNPCBehavior(1)
|
||||
}
|
||||
|
||||
const processMissionArrival = async (mission: FleetMission) => {
|
||||
// 从宇宙星球地图中查找目标星球
|
||||
const targetKey = gameLogic.generatePositionKey(
|
||||
mission.targetPosition.galaxy,
|
||||
mission.targetPosition.system,
|
||||
mission.targetPosition.position
|
||||
)
|
||||
// 先从玩家星球中查找,再从宇宙地图中查找
|
||||
const targetPlanet =
|
||||
gameStore.player.planets.find(
|
||||
p =>
|
||||
p.position.galaxy === mission.targetPosition.galaxy &&
|
||||
p.position.system === mission.targetPosition.system &&
|
||||
p.position.position === mission.targetPosition.position
|
||||
) || universeStore.planets[targetKey]
|
||||
|
||||
// 获取起始星球名称(用于报告)
|
||||
const originPlanet = gameStore.player.planets.find(p => p.id === mission.originPlanetId)
|
||||
const originPlanetName = originPlanet?.name || t('fleetView.unknownPlanet')
|
||||
|
||||
if (mission.missionType === MissionType.Transport) {
|
||||
const result = fleetLogic.processTransportArrival(mission, targetPlanet, gameStore.player, npcStore.npcs)
|
||||
// 生成运输任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Transport,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName:
|
||||
targetPlanet?.name || `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
|
||||
success: result.success,
|
||||
message: result.success ? t('missionReports.transportSuccess') : t('missionReports.transportFailed'),
|
||||
details: {
|
||||
transportedResources: mission.cargo
|
||||
},
|
||||
read: false
|
||||
})
|
||||
} else if (mission.missionType === MissionType.Attack) {
|
||||
const attackResult = await fleetLogic.processAttackArrival(mission, targetPlanet, gameStore.player, null, gameStore.player.planets)
|
||||
if (attackResult) {
|
||||
gameStore.player.battleReports.push(attackResult.battleResult)
|
||||
|
||||
// 检查是否攻击了NPC星球,更新外交关系
|
||||
if (targetPlanet) {
|
||||
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
||||
if (targetNpc) {
|
||||
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, attackResult.battleResult, npcStore.npcs, gameStore.locale)
|
||||
}
|
||||
}
|
||||
|
||||
if (attackResult.moon) {
|
||||
gameStore.player.planets.push(attackResult.moon)
|
||||
}
|
||||
if (attackResult.debrisField) {
|
||||
// 将残骸场添加到游戏状态
|
||||
universeStore.debrisFields[attackResult.debrisField.id] = attackResult.debrisField
|
||||
}
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Colonize) {
|
||||
const newPlanet = fleetLogic.processColonizeArrival(mission, targetPlanet, gameStore.player, t('planet.colonyPrefix'))
|
||||
// 生成殖民任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Colonize,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: newPlanet?.id,
|
||||
targetPlanetName: newPlanet?.name,
|
||||
success: !!newPlanet,
|
||||
message: newPlanet ? t('missionReports.colonizeSuccess') : t('missionReports.colonizeFailed'),
|
||||
details: newPlanet
|
||||
? {
|
||||
newPlanetId: newPlanet.id,
|
||||
newPlanetName: newPlanet.name
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
if (newPlanet) {
|
||||
gameStore.player.planets.push(newPlanet)
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Spy) {
|
||||
const spyReport = fleetLogic.processSpyArrival(mission, targetPlanet, gameStore.player, null, npcStore.npcs)
|
||||
if (spyReport) gameStore.player.spyReports.push(spyReport)
|
||||
} else if (mission.missionType === MissionType.Deploy) {
|
||||
const deployed = fleetLogic.processDeployArrival(mission, targetPlanet, gameStore.player.id)
|
||||
// 生成部署任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Deploy,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName:
|
||||
targetPlanet?.name || `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
|
||||
success: deployed,
|
||||
message: deployed ? t('missionReports.deploySuccess') : t('missionReports.deployFailed'),
|
||||
details: {
|
||||
deployedFleet: mission.fleet
|
||||
},
|
||||
read: false
|
||||
})
|
||||
if (deployed) {
|
||||
const missionIndex = gameStore.player.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) gameStore.player.fleetMissions.splice(missionIndex, 1)
|
||||
return
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Recycle) {
|
||||
// 处理回收任务
|
||||
const debrisId = `debris_${mission.targetPosition.galaxy}_${mission.targetPosition.system}_${mission.targetPosition.position}`
|
||||
const debrisField = universeStore.debrisFields[debrisId]
|
||||
const recycleResult = fleetLogic.processRecycleArrival(mission, debrisField)
|
||||
|
||||
// 生成回收任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Recycle,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
success: !!recycleResult,
|
||||
message: recycleResult ? t('missionReports.recycleSuccess') : t('missionReports.recycleFailed'),
|
||||
details: recycleResult
|
||||
? {
|
||||
recycledResources: recycleResult.collectedResources,
|
||||
remainingDebris: recycleResult.remainingDebris || undefined
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
|
||||
if (recycleResult && debrisField) {
|
||||
if (recycleResult.remainingDebris && (recycleResult.remainingDebris.metal > 0 || recycleResult.remainingDebris.crystal > 0)) {
|
||||
// 更新残骸场
|
||||
universeStore.debrisFields[debrisId] = {
|
||||
id: debrisField.id,
|
||||
position: debrisField.position,
|
||||
resources: recycleResult.remainingDebris,
|
||||
createdAt: debrisField.createdAt,
|
||||
expiresAt: debrisField.expiresAt
|
||||
}
|
||||
} else {
|
||||
// 残骸场已被完全收集,删除
|
||||
delete universeStore.debrisFields[debrisId]
|
||||
}
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Destroy) {
|
||||
// 处理行星毁灭任务
|
||||
const destroyResult = fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player)
|
||||
|
||||
// 生成毁灭任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Destroy,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName: targetPlanet?.name,
|
||||
success: destroyResult?.success || false,
|
||||
message: destroyResult?.success ? t('missionReports.destroySuccess') : t('missionReports.destroyFailed'),
|
||||
details: destroyResult?.success
|
||||
? {
|
||||
destroyedPlanetName:
|
||||
targetPlanet?.name ||
|
||||
`[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
|
||||
if (destroyResult && destroyResult.success && destroyResult.planetId) {
|
||||
// 星球被摧毁
|
||||
// 从玩家星球列表中移除(如果是玩家的星球)
|
||||
const planetIndex = gameStore.player.planets.findIndex(p => p.id === destroyResult.planetId)
|
||||
if (planetIndex > -1) {
|
||||
gameStore.player.planets.splice(planetIndex, 1)
|
||||
} else {
|
||||
// 不是玩家星球,从宇宙地图中移除
|
||||
delete universeStore.planets[targetKey]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processMissionReturn = (mission: FleetMission) => {
|
||||
const originPlanet = gameStore.player.planets.find(p => p.id === mission.originPlanetId)
|
||||
if (!originPlanet) return
|
||||
shipLogic.addFleet(originPlanet.fleet, mission.fleet)
|
||||
resourceLogic.addResources(originPlanet.resources, mission.cargo)
|
||||
const missionIndex = gameStore.player.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) gameStore.player.fleetMissions.splice(missionIndex, 1)
|
||||
}
|
||||
|
||||
// NPC任务处理
|
||||
const processNPCMissionArrival = (npc: NPC, mission: FleetMission) => {
|
||||
if (mission.missionType === MissionType.Recycle) {
|
||||
// NPC回收任务到达
|
||||
const debrisId = mission.debrisFieldId
|
||||
if (!debrisId) {
|
||||
console.warn('[NPC Mission] Recycle mission missing debrisFieldId')
|
||||
mission.status = 'returning'
|
||||
mission.returnTime = Date.now() + (mission.arrivalTime - mission.departureTime)
|
||||
return
|
||||
}
|
||||
|
||||
const debrisField = universeStore.debrisFields[debrisId]
|
||||
const recycleResult = fleetLogic.processRecycleArrival(mission, debrisField)
|
||||
|
||||
if (recycleResult && debrisField) {
|
||||
if (recycleResult.remainingDebris && (recycleResult.remainingDebris.metal > 0 || recycleResult.remainingDebris.crystal > 0)) {
|
||||
// 更新残骸场
|
||||
universeStore.debrisFields[debrisId] = {
|
||||
id: debrisField.id,
|
||||
position: debrisField.position,
|
||||
resources: recycleResult.remainingDebris,
|
||||
createdAt: debrisField.createdAt
|
||||
}
|
||||
} else {
|
||||
// 残骸已被完全回收,从宇宙中删除
|
||||
delete universeStore.debrisFields[debrisId]
|
||||
}
|
||||
}
|
||||
|
||||
// 移除即将到来的警告(回收任务已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
|
||||
// 设置返回时间
|
||||
mission.returnTime = Date.now() + (mission.arrivalTime - mission.departureTime)
|
||||
return
|
||||
}
|
||||
|
||||
// 找到目标星球
|
||||
const targetKey = gameLogic.generatePositionKey(
|
||||
mission.targetPosition.galaxy,
|
||||
mission.targetPosition.system,
|
||||
mission.targetPosition.position
|
||||
)
|
||||
const targetPlanet =
|
||||
gameStore.player.planets.find(
|
||||
p =>
|
||||
p.position.galaxy === mission.targetPosition.galaxy &&
|
||||
p.position.system === mission.targetPosition.system &&
|
||||
p.position.position === mission.targetPosition.position
|
||||
) || universeStore.planets[targetKey]
|
||||
|
||||
if (!targetPlanet) {
|
||||
console.warn('[NPC Mission] Target planet not found')
|
||||
return
|
||||
}
|
||||
|
||||
if (mission.missionType === MissionType.Spy) {
|
||||
// NPC侦查到达
|
||||
const { spiedNotification, spyReport } = npcBehaviorLogic.processNPCSpyArrival(npc, mission, targetPlanet, gameStore.player)
|
||||
|
||||
// 保存侦查报告到NPC(用于后续攻击决策)
|
||||
if (!npc.playerSpyReports) {
|
||||
npc.playerSpyReports = {}
|
||||
}
|
||||
npc.playerSpyReports[targetPlanet.id] = spyReport
|
||||
|
||||
// 添加被侦查通知给玩家
|
||||
if (!gameStore.player.spiedNotifications) {
|
||||
gameStore.player.spiedNotifications = []
|
||||
}
|
||||
gameStore.player.spiedNotifications.push(spiedNotification)
|
||||
|
||||
// 移除即将到来的警告(侦查已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
} else if (mission.missionType === MissionType.Attack) {
|
||||
// NPC攻击到达 - 使用专门的NPC攻击处理逻辑
|
||||
fleetLogic.processNPCAttackArrival(npc, mission, targetPlanet, gameStore.player, gameStore.player.planets).then(attackResult => {
|
||||
if (attackResult) {
|
||||
// 添加战斗报告给玩家
|
||||
gameStore.player.battleReports.push(attackResult.battleResult)
|
||||
|
||||
// 如果生成月球,添加到玩家星球列表
|
||||
if (attackResult.moon) {
|
||||
gameStore.player.planets.push(attackResult.moon)
|
||||
}
|
||||
|
||||
// 如果生成残骸场,添加到宇宙残骸场列表
|
||||
if (attackResult.debrisField) {
|
||||
universeStore.debrisFields[attackResult.debrisField.id] = attackResult.debrisField
|
||||
}
|
||||
}
|
||||
|
||||
// 移除即将到来的警告(攻击已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const processNPCMissionReturn = (npc: NPC, mission: FleetMission) => {
|
||||
// 找到NPC的起始星球
|
||||
const originPlanet = npc.planets.find(p => p.id === mission.originPlanetId)
|
||||
if (!originPlanet) return
|
||||
|
||||
// 返还舰队
|
||||
shipLogic.addFleet(originPlanet.fleet, mission.fleet)
|
||||
|
||||
// 如果携带掠夺资源,给NPC添加资源
|
||||
if (mission.cargo) {
|
||||
originPlanet.resources.metal += mission.cargo.metal
|
||||
originPlanet.resources.crystal += mission.cargo.crystal
|
||||
originPlanet.resources.deuterium += mission.cargo.deuterium
|
||||
}
|
||||
|
||||
// 从NPC任务列表中移除
|
||||
if (npc.fleetMissions) {
|
||||
const missionIndex = npc.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) {
|
||||
npc.fleetMissions.splice(missionIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理导弹攻击到达
|
||||
const processMissileAttackArrival = async (missileAttack: MissileAttack) => {
|
||||
// 动态导入导弹逻辑
|
||||
const missileLogic = await import('@/logic/missileLogic')
|
||||
|
||||
// 找到目标星球
|
||||
const targetKey = gameLogic.generatePositionKey(
|
||||
missileAttack.targetPosition.galaxy,
|
||||
missileAttack.targetPosition.system,
|
||||
missileAttack.targetPosition.position
|
||||
)
|
||||
const targetPlanet =
|
||||
gameStore.player.planets.find(
|
||||
p =>
|
||||
p.position.galaxy === missileAttack.targetPosition.galaxy &&
|
||||
p.position.system === missileAttack.targetPosition.system &&
|
||||
p.position.position === missileAttack.targetPosition.position
|
||||
) || universeStore.planets[targetKey]
|
||||
|
||||
// 如果目标星球不存在,导弹失败
|
||||
if (!targetPlanet) {
|
||||
missileAttack.status = 'arrived'
|
||||
// 生成失败报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `missile-report-${missileAttack.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.MissileAttack,
|
||||
originPlanetId: missileAttack.originPlanetId,
|
||||
originPlanetName: gameStore.player.planets.find(p => p.id === missileAttack.originPlanetId)?.name || t('fleetView.unknownPlanet'),
|
||||
targetPosition: missileAttack.targetPosition,
|
||||
targetPlanetId: undefined,
|
||||
targetPlanetName: `[${missileAttack.targetPosition.galaxy}:${missileAttack.targetPosition.system}:${missileAttack.targetPosition.position}]`,
|
||||
success: false,
|
||||
message: t('missionReports.missileAttackFailed'),
|
||||
details: {
|
||||
missileCount: missileAttack.missileCount,
|
||||
missileHits: 0,
|
||||
missileIntercepted: 0,
|
||||
defenseLosses: {}
|
||||
},
|
||||
read: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 计算导弹攻击结果
|
||||
const impactResult = missileLogic.calculateMissileImpact(missileAttack.missileCount, targetPlanet)
|
||||
|
||||
// 应用损失到目标星球
|
||||
missileLogic.applyMissileAttackResult(targetPlanet, impactResult.defenseLosses)
|
||||
|
||||
// 标记导弹攻击为已到达
|
||||
missileAttack.status = 'arrived'
|
||||
|
||||
// 生成导弹攻击报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
const reportMessage =
|
||||
impactResult.missileHits > 0
|
||||
? `${t('missionReports.missileAttackSuccess')}: ${impactResult.missileHits} ${t('missionReports.hits')}`
|
||||
: t('missionReports.missileAttackIntercepted')
|
||||
|
||||
gameStore.player.missionReports.push({
|
||||
id: `missile-report-${missileAttack.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.MissileAttack,
|
||||
originPlanetId: missileAttack.originPlanetId,
|
||||
originPlanetName: gameStore.player.planets.find(p => p.id === missileAttack.originPlanetId)?.name || t('fleetView.unknownPlanet'),
|
||||
targetPosition: missileAttack.targetPosition,
|
||||
targetPlanetId: targetPlanet.id,
|
||||
targetPlanetName: targetPlanet.name,
|
||||
success: true,
|
||||
message: reportMessage,
|
||||
details: {
|
||||
missileCount: missileAttack.missileCount,
|
||||
missileHits: impactResult.missileHits,
|
||||
missileIntercepted: impactResult.missileIntercepted,
|
||||
defenseLosses: impactResult.defenseLosses
|
||||
},
|
||||
read: false
|
||||
})
|
||||
}
|
||||
|
||||
// 移除即将到来的舰队警告
|
||||
const removeIncomingFleetAlert = (alert: IncomingFleetAlert) => {
|
||||
if (!gameStore.player.incomingFleetAlerts) return
|
||||
const index = gameStore.player.incomingFleetAlerts.indexOf(alert)
|
||||
if (index > -1) {
|
||||
gameStore.player.incomingFleetAlerts.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const removeIncomingFleetAlertById = (missionId: string) => {
|
||||
if (!gameStore.player.incomingFleetAlerts) return
|
||||
const index = gameStore.player.incomingFleetAlerts.findIndex(a => a.id === missionId)
|
||||
if (index > -1) {
|
||||
gameStore.player.incomingFleetAlerts.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// NPC成长系统更新函数
|
||||
let npcUpdateCounter = 0 // 累计秒数
|
||||
const NPC_UPDATE_INTERVAL = 10 // 每10秒更新一次NPC,减少性能开销
|
||||
|
||||
const updateNPCGrowth = (deltaSeconds: number) => {
|
||||
// 累积时间
|
||||
npcUpdateCounter += deltaSeconds
|
||||
|
||||
// 只在达到更新间隔时才执行
|
||||
if (npcUpdateCounter < NPC_UPDATE_INTERVAL) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有星球
|
||||
const allPlanets = Object.values(universeStore.planets)
|
||||
|
||||
// 如果NPC store为空,从星球数据中初始化NPC
|
||||
if (npcStore.npcs.length === 0) {
|
||||
const npcMap = new Map<string, any>()
|
||||
|
||||
allPlanets.forEach(planet => {
|
||||
// 跳过玩家的星球
|
||||
if (planet.ownerId === gameStore.player.id || !planet.ownerId) return
|
||||
|
||||
// 这是NPC的星球
|
||||
if (!npcMap.has(planet.ownerId)) {
|
||||
npcMap.set(planet.ownerId, {
|
||||
id: planet.ownerId,
|
||||
name: `NPC-${planet.ownerId.substring(0, 8)}`,
|
||||
planets: [],
|
||||
technologies: {}, // 初始化空科技树
|
||||
difficulty: 'medium' as const, // 默认中等难度
|
||||
relations: {}, // 外交关系
|
||||
allies: [], // 盟友列表
|
||||
enemies: [] // 敌人列表
|
||||
})
|
||||
}
|
||||
|
||||
npcMap.get(planet.ownerId)!.planets.push(planet)
|
||||
})
|
||||
|
||||
// 保存到store
|
||||
npcStore.npcs = Array.from(npcMap.values())
|
||||
|
||||
// 如果有NPC,基于玩家实力初始化NPC
|
||||
if (npcStore.npcs.length > 0) {
|
||||
const gameState: npcGrowthLogic.NPCGrowthGameState = {
|
||||
planets: allPlanets,
|
||||
player: gameStore.player,
|
||||
npcs: npcStore.npcs
|
||||
}
|
||||
|
||||
const playerPower = npcGrowthLogic.calculatePlayerAveragePower(gameState)
|
||||
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcGrowthLogic.initializeNPCStartingPower(npc, playerPower)
|
||||
})
|
||||
|
||||
// 初始化NPC之间的外交关系(盟友/敌人)
|
||||
npcGrowthLogic.initializeNPCDiplomacy(npcStore.npcs)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有NPC,直接返回
|
||||
if (npcStore.npcs.length === 0) {
|
||||
npcUpdateCounter = 0
|
||||
return
|
||||
}
|
||||
|
||||
// 构建游戏状态
|
||||
const gameState: npcGrowthLogic.NPCGrowthGameState = {
|
||||
planets: allPlanets,
|
||||
player: gameStore.player,
|
||||
npcs: npcStore.npcs
|
||||
}
|
||||
|
||||
// 使用累积的时间更新每个NPC
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcGrowthLogic.updateNPCGrowth(npc, gameState, npcUpdateCounter)
|
||||
})
|
||||
|
||||
// 重置计数器
|
||||
npcUpdateCounter = 0
|
||||
}
|
||||
|
||||
// NPC行为系统更新函数(侦查和攻击决策)
|
||||
let npcBehaviorCounter = 0
|
||||
const NPC_BEHAVIOR_INTERVAL = 5 // 每5秒检查一次NPC行为
|
||||
|
||||
const updateNPCBehavior = (deltaSeconds: number) => {
|
||||
// 累积时间
|
||||
npcBehaviorCounter += deltaSeconds
|
||||
|
||||
// 只在达到更新间隔时才执行
|
||||
if (npcBehaviorCounter < NPC_BEHAVIOR_INTERVAL) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有NPC,直接返回
|
||||
if (npcStore.npcs.length === 0) {
|
||||
npcBehaviorCounter = 0
|
||||
return
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
const allPlanets = Object.values(universeStore.planets)
|
||||
|
||||
// 更新每个NPC的行为
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcBehaviorLogic.updateNPCBehavior(npc, gameStore.player, allPlanets, universeStore.debrisFields, now)
|
||||
})
|
||||
|
||||
npcBehaviorCounter = 0
|
||||
}
|
||||
|
||||
// 游戏循环定时器
|
||||
let gameLoop: ReturnType<typeof setInterval> | null = null
|
||||
let konamiCleanup: (() => void) | null = null
|
||||
let versionCheckInterval: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (gameLoop) clearInterval(gameLoop)
|
||||
})
|
||||
// 启动游戏循环
|
||||
const startGameLoop = () => {
|
||||
// 清理旧的定时器
|
||||
if (gameLoop) {
|
||||
clearInterval(gameLoop)
|
||||
}
|
||||
// 根据游戏速度计算间隔时间
|
||||
const interval = 1000 / (gameStore.gameSpeed || 1)
|
||||
// 启动新的游戏循环
|
||||
gameLoop = setInterval(() => {
|
||||
updateGame()
|
||||
}, interval)
|
||||
}
|
||||
|
||||
// 监听游戏速度变化,重新启动游戏循环
|
||||
watch(
|
||||
() => gameStore.gameSpeed,
|
||||
() => {
|
||||
if (gameLoop) {
|
||||
startGameLoop()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 初始化游戏
|
||||
onMounted(async () => {
|
||||
@@ -368,17 +1211,91 @@
|
||||
if (isFirstVisit) {
|
||||
gameStore.locale = detectBrowserLocale()
|
||||
}
|
||||
await initGame(t('common.playerName'), t('planet.homePlanet'), t('planet.planetPrefix'))
|
||||
await initGame()
|
||||
// 启动游戏循环
|
||||
gameLoop = setInterval(() => {
|
||||
updateGame()
|
||||
}, 1000) // 每1秒更新一次
|
||||
startGameLoop()
|
||||
// 启动科乐美秘籍监听
|
||||
konamiCleanup = setupKonamiCode()
|
||||
// 首次检查版本(被动检测)
|
||||
const versionInfo = await checkLatestVersion(gameStore.player.lastVersionCheckTime || 0, (time: number) => {
|
||||
gameStore.player.lastVersionCheckTime = time
|
||||
})
|
||||
if (versionInfo) {
|
||||
updateInfo.value = versionInfo
|
||||
toast.info(t('settings.newVersionAvailable', { version: versionInfo.version }), {
|
||||
duration: Infinity,
|
||||
dismissible: true,
|
||||
action: {
|
||||
label: t('settings.viewUpdate'),
|
||||
onClick: () => {
|
||||
showUpdateDialog.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// 启动版本检查定时器(每5分钟被动检查一次)
|
||||
versionCheckInterval = setInterval(async () => {
|
||||
const versionInfo = await checkLatestVersion(gameStore.player.lastVersionCheckTime || 0, (time: number) => {
|
||||
gameStore.player.lastVersionCheckTime = time
|
||||
})
|
||||
if (versionInfo) {
|
||||
updateInfo.value = versionInfo
|
||||
toast.info(t('settings.newVersionAvailable', { version: versionInfo.version }), {
|
||||
duration: Infinity,
|
||||
dismissible: true,
|
||||
action: {
|
||||
label: t('settings.viewUpdate'),
|
||||
onClick: () => {
|
||||
showUpdateDialog.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 5 * 60 * 1000)
|
||||
})
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (gameLoop) clearInterval(gameLoop)
|
||||
if (konamiCleanup) konamiCleanup()
|
||||
if (versionCheckInterval) clearInterval(versionCheckInterval)
|
||||
})
|
||||
|
||||
// 科乐美秘籍:上上下下左左右右BA
|
||||
const setupKonamiCode = () => {
|
||||
const konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowLeft', 'ArrowRight', 'ArrowRight', 'b', 'a']
|
||||
let konamiIndex = 0
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
// 如果已经激活GM模式,直接返回
|
||||
if (gameStore.player.isGMEnabled) return
|
||||
|
||||
const key = event.key.toLowerCase()
|
||||
// 检查是否匹配当前秘籍序列
|
||||
if (key === konamiCode[konamiIndex] || event.key === konamiCode[konamiIndex]) {
|
||||
konamiIndex++
|
||||
// 如果完成整个秘籍序列
|
||||
if (konamiIndex === konamiCode.length) {
|
||||
gameStore.player.isGMEnabled = true
|
||||
// 显示成功消息
|
||||
toast.success(t('common.gmModeActivated'))
|
||||
konamiIndex = 0
|
||||
}
|
||||
} else {
|
||||
// 如果按错了键,重置序列
|
||||
konamiIndex = 0
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
// 返回清理函数
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}
|
||||
|
||||
// 定义 planet computed(需要在 watch 之前定义)
|
||||
const planet = computed(() => gameStore.currentPlanet)
|
||||
|
||||
const navItems = [
|
||||
const navItems = computed(() => [
|
||||
{ name: computed(() => t('nav.overview')), path: '/', icon: Home },
|
||||
{ name: computed(() => t('nav.buildings')), path: '/buildings', icon: Building2 },
|
||||
{ name: computed(() => t('nav.research')), path: '/research', icon: FlaskConical },
|
||||
@@ -388,11 +1305,12 @@
|
||||
{ name: computed(() => t('nav.officers')), path: '/officers', icon: Users },
|
||||
{ name: computed(() => t('nav.simulator')), path: '/battle-simulator', icon: Swords },
|
||||
{ name: computed(() => t('nav.galaxy')), path: '/galaxy', icon: Globe },
|
||||
{ name: computed(() => t('nav.diplomacy')), path: '/diplomacy', icon: Handshake },
|
||||
{ name: computed(() => t('nav.messages')), path: '/messages', icon: Mail },
|
||||
{ name: computed(() => t('nav.settings')), path: '/settings', icon: Settings },
|
||||
// GM菜单仅在开发模式下显示
|
||||
...(import.meta.env.DEV ? [{ name: computed(() => t('nav.gm')), path: '/gm', icon: Wrench }] : [])
|
||||
]
|
||||
// GM菜单在启用GM模式时显示
|
||||
...(gameStore.player.isGMEnabled ? [{ name: computed(() => t('nav.gm')), path: '/gm', icon: Wrench }] : [])
|
||||
])
|
||||
|
||||
// 使用直接计算,不再缓存
|
||||
const production = computed(() => {
|
||||
@@ -413,11 +1331,35 @@
|
||||
return resourceLogic.calculateResourceCapacity(planet.value, bonuses.storageCapacityBonus)
|
||||
})
|
||||
|
||||
// 电力消耗
|
||||
const energyConsumption = computed(() => {
|
||||
if (!planet.value) return 0
|
||||
return resourceLogic.calculateEnergyConsumption(planet.value)
|
||||
})
|
||||
|
||||
// 净电力(产量 - 消耗)
|
||||
const netEnergy = computed(() => {
|
||||
if (!planet.value || !production.value) return 0
|
||||
return production.value.energy - energyConsumption.value
|
||||
})
|
||||
|
||||
// 未读消息数量
|
||||
const unreadMessagesCount = computed(() => {
|
||||
const unreadBattles = gameStore.player.battleReports.filter(r => !r.read).length
|
||||
const unreadSpies = gameStore.player.spyReports.filter(r => !r.read).length
|
||||
return unreadBattles + unreadSpies
|
||||
const unreadSpied = gameStore.player.spiedNotifications?.filter(n => !n.read).length || 0
|
||||
const unreadMissions = gameStore.player.missionReports?.filter(r => !r.read).length || 0
|
||||
const unreadNPCActivity = gameStore.player.npcActivityNotifications?.filter(n => !n.read).length || 0
|
||||
const unreadGifts = gameStore.player.giftNotifications?.filter(n => !n.read).length || 0
|
||||
const unreadGiftRejected = gameStore.player.giftRejectedNotifications?.filter(n => !n.read).length || 0
|
||||
return unreadBattles + unreadSpies + unreadSpied + unreadMissions + unreadNPCActivity + unreadGifts + unreadGiftRejected
|
||||
})
|
||||
|
||||
// 正在执行的舰队任务数量(包括飞行中的导弹)
|
||||
const activeFleetMissionsCount = computed(() => {
|
||||
const fleetMissions = gameStore.player.fleetMissions.filter(m => m.status === 'outbound' || m.status === 'returning').length
|
||||
const flyingMissiles = gameStore.player.missileAttacks?.filter(m => m.status === 'flying').length || 0
|
||||
return fleetMissions + flyingMissiles
|
||||
})
|
||||
|
||||
// 资源类型配置
|
||||
@@ -450,17 +1392,79 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 切换到指定星球
|
||||
const switchToPlanet = (planetId: string) => {
|
||||
gameStore.currentPlanetId = planetId
|
||||
}
|
||||
|
||||
// 切换侧边栏
|
||||
const toggleSidebar = () => {
|
||||
sidebarOpen.value = !sidebarOpen.value
|
||||
}
|
||||
|
||||
// 处理确认对话框的确认操作
|
||||
const handleConfirmAction = () => {
|
||||
if (confirmDialogAction.value) {
|
||||
confirmDialogAction.value()
|
||||
// 获取队列项的名称
|
||||
const getItemName = (item: BuildQueueItem): string => {
|
||||
if (item.type === 'building' || item.type === 'demolish') {
|
||||
const buildingName = t(`buildings.${item.itemType}`)
|
||||
return item.type === 'demolish' ? `${t('buildingsView.demolish')} - ${buildingName}` : buildingName
|
||||
} else if (item.type === 'technology') {
|
||||
return t(`technologies.${item.itemType}`)
|
||||
} else if (item.type === 'ship') {
|
||||
return t(`ships.${item.itemType}`)
|
||||
} else if (item.type === 'defense') {
|
||||
return t(`defenses.${item.itemType}`)
|
||||
}
|
||||
confirmDialogOpen.value = false
|
||||
return item.itemType
|
||||
}
|
||||
|
||||
// 获取剩余时间
|
||||
const getRemainingTime = (item: BuildQueueItem): number => {
|
||||
const now = Date.now()
|
||||
return Math.max(0, Math.floor((item.endTime - now) / 1000))
|
||||
}
|
||||
|
||||
// 获取队列进度
|
||||
const getQueueProgress = (item: BuildQueueItem): number => {
|
||||
const now = Date.now()
|
||||
const total = item.endTime - item.startTime
|
||||
const elapsed = now - item.startTime
|
||||
return Math.min(100, Math.max(0, (elapsed / total) * 100))
|
||||
}
|
||||
|
||||
// 取消建造
|
||||
const handleCancelBuild = (queueId: string) => {
|
||||
confirmDialogTitle.value = t('queue.cancelBuild')
|
||||
confirmDialogMessage.value = t('queue.confirmCancel')
|
||||
confirmDialogAction.value = () => {
|
||||
if (!gameStore.currentPlanet) return false
|
||||
const { item, index } = buildingValidation.findQueueItem(gameStore.currentPlanet.buildQueue, queueId)
|
||||
if (!item) return false
|
||||
if (item.type === 'building') {
|
||||
const refund = buildingValidation.cancelBuildingUpgrade(gameStore.currentPlanet, item)
|
||||
resourceLogic.addResources(gameStore.currentPlanet.resources, refund)
|
||||
}
|
||||
gameStore.currentPlanet.buildQueue.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
confirmDialogOpen.value = true
|
||||
}
|
||||
|
||||
// 取消研究
|
||||
const handleCancelResearch = (queueId: string) => {
|
||||
confirmDialogTitle.value = t('queue.cancelResearch')
|
||||
confirmDialogMessage.value = t('queue.confirmCancel')
|
||||
confirmDialogAction.value = () => {
|
||||
if (!gameStore.currentPlanet) return false
|
||||
const { item, index } = buildingValidation.findQueueItem(gameStore.player.researchQueue, queueId)
|
||||
if (!item) return false
|
||||
if (item.type === 'technology') {
|
||||
const refund = researchValidation.cancelTechnologyResearch(item)
|
||||
resourceLogic.addResources(gameStore.currentPlanet.resources, refund)
|
||||
}
|
||||
gameStore.player.researchQueue.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
confirmDialogOpen.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,23 +15,37 @@
|
||||
<TableHead v-if="type === 'building' && showProductionColumn" class="text-center">{{ t('buildings.production') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showConsumptionColumn" class="text-center">{{ t('buildings.consumption') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showCapacityColumn" class="text-center">{{ t('buildings.storageCapacity') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showFleetStorageColumn" class="text-center">{{ t('buildings.fleetStorage') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showBuildQueueColumn" class="text-center">{{ t('buildings.buildQueueBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showFleetStorageColumn" class="text-center">
|
||||
{{ t('buildings.fleetStorage') }}
|
||||
</TableHead>
|
||||
<TableHead v-if="type === 'building' && showBuildQueueColumn" class="text-center">
|
||||
{{ t('buildings.buildQueueBonus') }}
|
||||
</TableHead>
|
||||
<TableHead v-if="type === 'building' && showSpaceColumn" class="text-center">{{ t('buildings.spaceBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showMissileColumn" class="text-center">{{ t('buildings.missileCapacity') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showBuildSpeedColumn" class="text-center">{{ t('buildings.buildSpeedBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showResearchSpeedColumn" class="text-center">{{ t('buildings.researchSpeedBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'building' && showBuildSpeedColumn" class="text-center">
|
||||
{{ t('buildings.buildSpeedBonus') }}
|
||||
</TableHead>
|
||||
<TableHead v-if="type === 'building' && showResearchSpeedColumn" class="text-center">
|
||||
{{ t('buildings.researchSpeedBonus') }}
|
||||
</TableHead>
|
||||
<!-- 科技相关列 -->
|
||||
<TableHead v-if="type === 'technology' && showAttackBonusColumn" class="text-center">{{ t('research.attackBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showShieldBonusColumn" class="text-center">{{ t('research.shieldBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showArmorBonusColumn" class="text-center">{{ t('research.armorBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showSpyLevelColumn" class="text-center">{{ t('research.spyLevel') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showFleetStorageColumn" class="text-center">{{ t('buildings.fleetStorage') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showResearchQueueColumn" class="text-center">{{ t('research.researchQueueBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showFleetStorageColumn" class="text-center">
|
||||
{{ t('buildings.fleetStorage') }}
|
||||
</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showResearchQueueColumn" class="text-center">
|
||||
{{ t('research.researchQueueBonus') }}
|
||||
</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showColonySlotsColumn" class="text-center">{{ t('research.colonySlots') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showSpaceColumn" class="text-center">{{ t('buildings.spaceBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showSpeedBonusColumn" class="text-center">{{ t('research.speedBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showResearchSpeedColumn" class="text-center">{{ t('buildings.researchSpeedBonus') }}</TableHead>
|
||||
<TableHead v-if="type === 'technology' && showResearchSpeedColumn" class="text-center">
|
||||
{{ t('buildings.researchSpeedBonus') }}
|
||||
</TableHead>
|
||||
<TableHead class="text-center">{{ t('player.points') }}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@@ -78,7 +92,8 @@
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'building' && showFleetStorageColumn" class="text-center text-sm">
|
||||
<span v-if="getLevelData(level).fleetStorage > 0" class="text-blue-600 dark:text-blue-400">
|
||||
+<NumberWithTooltip :value="getLevelData(level).fleetStorage" />
|
||||
+
|
||||
<NumberWithTooltip :value="getLevelData(level).fleetStorage" />
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</TableCell>
|
||||
@@ -87,7 +102,8 @@
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'building' && showSpaceColumn" class="text-center text-sm">
|
||||
<span v-if="getLevelData(level).spaceBonus > 0" class="text-green-600 dark:text-green-400">
|
||||
+<NumberWithTooltip :value="getLevelData(level).spaceBonus" />
|
||||
+
|
||||
<NumberWithTooltip :value="getLevelData(level).spaceBonus" />
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</TableCell>
|
||||
@@ -95,8 +111,12 @@
|
||||
<span class="text-orange-600 dark:text-orange-400">+10</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'building' && showBuildSpeedColumn" class="text-center text-sm">
|
||||
<span v-if="itemType === 'roboticsFactory'" class="text-cyan-600 dark:text-cyan-400">+{{ getLevelData(level).buildSpeedBonus * 100 }}%</span>
|
||||
<span v-else-if="itemType === 'naniteFactory'" class="text-cyan-600 dark:text-cyan-400">+{{ getLevelData(level).buildSpeedBonus * 100 }}%</span>
|
||||
<span v-if="itemType === 'roboticsFactory'" class="text-cyan-600 dark:text-cyan-400">
|
||||
+{{ getLevelData(level).buildSpeedBonus * 100 }}%
|
||||
</span>
|
||||
<span v-else-if="itemType === 'naniteFactory'" class="text-cyan-600 dark:text-cyan-400">
|
||||
+{{ getLevelData(level).buildSpeedBonus * 100 }}%
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'building' && showResearchSpeedColumn" class="text-center text-sm">
|
||||
<span class="text-indigo-600 dark:text-indigo-400">+{{ (getLevelData(level).researchSpeedBonus - 1) * 100 }}%</span>
|
||||
@@ -115,7 +135,10 @@
|
||||
<span class="text-purple-600 dark:text-purple-400">+{{ level }}</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'technology' && showFleetStorageColumn" class="text-center text-sm">
|
||||
<span class="text-blue-600 dark:text-blue-400">+<NumberWithTooltip :value="level * 500" /></span>
|
||||
<span class="text-blue-600 dark:text-blue-400">
|
||||
+
|
||||
<NumberWithTooltip :value="level * 500" />
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'technology' && showResearchQueueColumn" class="text-center text-sm">
|
||||
<span class="text-purple-600 dark:text-purple-400">+1</span>
|
||||
@@ -124,7 +147,7 @@
|
||||
<span class="text-green-600 dark:text-green-400">+1</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'technology' && showSpaceColumn" class="text-center text-sm">
|
||||
<span class="text-green-600 dark:text-green-400">+5 {{ t('research.forAllPlanets') }}</span>
|
||||
<span class="text-green-600 dark:text-green-400">+30 {{ t('research.forAllPlanets') }}</span>
|
||||
</TableCell>
|
||||
<TableCell v-if="type === 'technology' && showSpeedBonusColumn" class="text-center text-sm">
|
||||
<span class="text-yellow-600 dark:text-yellow-400">+{{ level * 10 }}%</span>
|
||||
@@ -275,7 +298,12 @@
|
||||
<CardTitle class="text-sm">{{ t(`${typeKey}.buildCost`) }}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-2">
|
||||
<div v-for="resourceType in costResourceTypes" :key="resourceType.key" v-show="unitCost[resourceType.key] > 0" class="flex items-center justify-between text-sm">
|
||||
<div
|
||||
v-for="resourceType in costResourceTypes"
|
||||
:key="resourceType.key"
|
||||
v-show="unitCost[resourceType.key] > 0"
|
||||
class="flex items-center justify-between text-sm"
|
||||
>
|
||||
<span class="text-muted-foreground">{{ t(`resources.${resourceType.key}`) }}:</span>
|
||||
<span class="font-medium"><NumberWithTooltip :value="unitCost[resourceType.key]" /></span>
|
||||
</div>
|
||||
@@ -311,9 +339,17 @@
|
||||
<div class="space-y-2">
|
||||
<p class="text-sm text-muted-foreground">{{ t(`${typeKey}.totalCost`) }}:</p>
|
||||
<div class="space-y-1 text-sm">
|
||||
<div v-for="resourceType in costResourceTypes" :key="resourceType.key" class="flex justify-between">
|
||||
<span>{{ t(`resources.${resourceType.key}`) }}:</span>
|
||||
<span class="font-medium"><NumberWithTooltip :value="batchCost[resourceType.key]" /></span>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ t('resources.metal') }}:</span>
|
||||
<span class="font-medium"><NumberWithTooltip :value="batchCost.metal" /></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ t('resources.crystal') }}:</span>
|
||||
<span class="font-medium"><NumberWithTooltip :value="batchCost.crystal" /></span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ t('resources.deuterium') }}:</span>
|
||||
<span class="font-medium"><NumberWithTooltip :value="batchCost.deuterium" /></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -654,9 +690,9 @@
|
||||
} else if (buildingType === 'shipyard') {
|
||||
fleetStorage = 1000 * level
|
||||
} else if (buildingType === 'terraformer') {
|
||||
spaceBonus = 5
|
||||
spaceBonus = 30
|
||||
} else if (buildingType === 'lunarBase') {
|
||||
spaceBonus = 5
|
||||
spaceBonus = 30
|
||||
} else if (buildingType === 'roboticsFactory') {
|
||||
buildSpeedBonus = level
|
||||
} else if (buildingType === 'naniteFactory') {
|
||||
@@ -685,7 +721,18 @@
|
||||
}
|
||||
|
||||
const points = pointsLogic.calculateTechnologyPoints(techType, level - 1, level)
|
||||
return { cost, time, production: 0, consumption: 0, points, capacity: 0, fleetStorage: 0, spaceBonus: 0, buildSpeedBonus: 0, researchSpeedBonus }
|
||||
return {
|
||||
cost,
|
||||
time,
|
||||
production: 0,
|
||||
consumption: 0,
|
||||
points,
|
||||
capacity: 0,
|
||||
fleetStorage: 0,
|
||||
spaceBonus: 0,
|
||||
buildSpeedBonus: 0,
|
||||
researchSpeedBonus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
120
src/components/UpdateDialog.vue
Normal file
120
src/components/UpdateDialog.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="$emit('update:open', $event)">
|
||||
<DialogScrollContent class="max-w-2xl max-h-[80vh] flex flex-col">
|
||||
<DialogHeader class="flex-shrink-0">
|
||||
<DialogTitle>{{ t('settings.newVersionAvailable', { version: versionInfo?.version || '' }) }}</DialogTitle>
|
||||
<DialogDescription>{{ t('settings.updateAvailable') }}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="flex-1 overflow-y-auto min-h-0 mt-4 pr-2">
|
||||
<div class="prose prose-sm dark:prose-invert max-w-none" v-html="renderedMarkdown"></div>
|
||||
</div>
|
||||
|
||||
<DialogFooter class="flex gap-2 flex-shrink-0 mt-4">
|
||||
<Button variant="outline" @click="$emit('update:open', false)">
|
||||
{{ t('common.cancel') }}
|
||||
</Button>
|
||||
<Button @click="handleDownload">
|
||||
<Download class="mr-2 h-4 w-4" />
|
||||
{{ t('settings.download') }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogScrollContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { marked } from 'marked'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { Dialog, DialogScrollContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Download } from 'lucide-vue-next'
|
||||
import type { VersionInfo } from '@/utils/versionCheck'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
versionInfo: VersionInfo | null
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
'update:open': [value: boolean]
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const renderedMarkdown = computed(() => {
|
||||
if (!props.versionInfo?.releaseNotes) return ''
|
||||
return marked(props.versionInfo.releaseNotes)
|
||||
})
|
||||
|
||||
const handleDownload = () => {
|
||||
if (props.versionInfo?.downloadUrl) {
|
||||
window.open(props.versionInfo.downloadUrl, '_blank')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.prose) {
|
||||
color: hsl(var(--foreground));
|
||||
}
|
||||
|
||||
:deep(.prose h1) {
|
||||
font-size: 1.5em;
|
||||
font-weight: 700;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
:deep(.prose h2) {
|
||||
font-size: 1.25em;
|
||||
font-weight: 600;
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
|
||||
:deep(.prose h3) {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
margin-top: 0.6em;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
:deep(.prose p) {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
:deep(.prose ul) {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
:deep(.prose li) {
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
:deep(.prose code) {
|
||||
background: hsl(var(--muted));
|
||||
padding: 0.2em 0.4em;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
:deep(.prose pre) {
|
||||
background: hsl(var(--muted));
|
||||
padding: 1em;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: auto;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
:deep(.prose a) {
|
||||
color: hsl(var(--primary));
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -6,7 +6,7 @@
|
||||
v-bind="{ ...$attrs, ...forwarded }"
|
||||
:class="
|
||||
cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-2xl',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
v-bind="{ ...$attrs, ...forwarded }"
|
||||
:class="
|
||||
cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 w-[calc(100vw-3rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200 sm:w-auto flex flex-col p-0',
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 w-[calc(100vw-3rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200 sm:w-auto sm:min-w-[764px] flex flex-col p-0',
|
||||
containerClass
|
||||
)
|
||||
"
|
||||
|
||||
22
src/components/ui/empty/Empty.vue
Normal file
22
src/components/ui/empty/Empty.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div
|
||||
data-slot="empty"
|
||||
:class="
|
||||
cn(
|
||||
'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance rounded-lg border-dashed p-6 text-center md:p-12',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
14
src/components/ui/empty/EmptyContent.vue
Normal file
14
src/components/ui/empty/EmptyContent.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div data-slot="empty-content" :class="cn('flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
19
src/components/ui/empty/EmptyDescription.vue
Normal file
19
src/components/ui/empty/EmptyDescription.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<p
|
||||
data-slot="empty-description"
|
||||
:class="
|
||||
cn('text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4', $attrs.class ?? '')
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
14
src/components/ui/empty/EmptyHeader.vue
Normal file
14
src/components/ui/empty/EmptyHeader.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div data-slot="empty-header" :class="cn('flex max-w-sm flex-col items-center gap-2 text-center', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
17
src/components/ui/empty/EmptyMedia.vue
Normal file
17
src/components/ui/empty/EmptyMedia.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div data-slot="empty-icon" :data-variant="variant" :class="cn(emptyMediaVariants({ variant }), props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import type { EmptyMediaVariants } from '.'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { emptyMediaVariants } from '.'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
variant?: EmptyMediaVariants['variant']
|
||||
}>()
|
||||
</script>
|
||||
14
src/components/ui/empty/EmptyTitle.vue
Normal file
14
src/components/ui/empty/EmptyTitle.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div data-slot="empty-title" :class="cn('text-lg font-medium tracking-tight', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
23
src/components/ui/empty/index.ts
Normal file
23
src/components/ui/empty/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
export { default as Empty } from './Empty.vue'
|
||||
export { default as EmptyContent } from './EmptyContent.vue'
|
||||
export { default as EmptyDescription } from './EmptyDescription.vue'
|
||||
export { default as EmptyHeader } from './EmptyHeader.vue'
|
||||
export { default as EmptyMedia } from './EmptyMedia.vue'
|
||||
export { default as EmptyTitle } from './EmptyTitle.vue'
|
||||
|
||||
export const emptyMediaVariants = cva('mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0', {
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6"
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
})
|
||||
|
||||
export type EmptyMediaVariants = VariantProps<typeof emptyMediaVariants>
|
||||
25
src/components/ui/scroll-area/ScrollArea.vue
Normal file
25
src/components/ui/scroll-area/ScrollArea.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<ScrollAreaRoot data-slot="scroll-area" v-bind="delegatedProps" :class="cn('relative', props.class)">
|
||||
<ScrollAreaViewport
|
||||
data-slot="scroll-area-viewport"
|
||||
class="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
>
|
||||
<slot />
|
||||
</ScrollAreaViewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaCorner />
|
||||
</ScrollAreaRoot>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ScrollAreaRootProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { ScrollAreaCorner, ScrollAreaRoot, ScrollAreaViewport } from 'reka-ui'
|
||||
import { cn } from '@/lib/utils'
|
||||
import ScrollBar from './ScrollBar.vue'
|
||||
|
||||
const props = defineProps<ScrollAreaRootProps & { class?: HTMLAttributes['class'] }>()
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class')
|
||||
</script>
|
||||
30
src/components/ui/scroll-area/ScrollBar.vue
Normal file
30
src/components/ui/scroll-area/ScrollBar.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex touch-none p-px transition-colors select-none',
|
||||
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent',
|
||||
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<ScrollAreaThumb data-slot="scroll-area-thumb" class="bg-border relative flex-1 rounded-full" />
|
||||
</ScrollAreaScrollbar>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ScrollAreaScrollbarProps } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { reactiveOmit } from '@vueuse/core'
|
||||
import { ScrollAreaScrollbar, ScrollAreaThumb } from 'reka-ui'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = withDefaults(defineProps<ScrollAreaScrollbarProps & { class?: HTMLAttributes['class'] }>(), {
|
||||
orientation: 'vertical'
|
||||
})
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class')
|
||||
</script>
|
||||
2
src/components/ui/scroll-area/index.ts
Normal file
2
src/components/ui/scroll-area/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as ScrollArea } from './ScrollArea.vue'
|
||||
export { default as ScrollBar } from './ScrollBar.vue'
|
||||
@@ -37,6 +37,7 @@
|
||||
import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon, XIcon } from 'lucide-vue-next'
|
||||
import { Toaster as Sonner } from 'vue-sonner'
|
||||
import { cn } from '@/lib/utils'
|
||||
import 'vue-sonner/style.css'
|
||||
|
||||
const props = defineProps<ToasterProps>()
|
||||
</script>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useUniverseStore } from '@/stores/universeStore'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
import * as planetLogic from '@/logic/planetLogic'
|
||||
import * as resourceLogic from '@/logic/resourceLogic'
|
||||
import * as officerLogic from '@/logic/officerLogic'
|
||||
|
||||
/**
|
||||
* 游戏生命周期管理
|
||||
* 处理游戏初始化、NPC星球生成等
|
||||
*/
|
||||
export const useGameLifecycle = () => {
|
||||
const gameStore = useGameStore()
|
||||
const universeStore = useUniverseStore()
|
||||
|
||||
/**
|
||||
* 生成NPC星球
|
||||
*/
|
||||
const generateNPCPlanets = (npcCount: number, planetPrefix: string) => {
|
||||
for (let i = 0; i < npcCount; i++) {
|
||||
const position = gameLogic.generateRandomPosition()
|
||||
const key = gameLogic.generatePositionKey(position.galaxy, position.system, position.position)
|
||||
if (universeStore.planets[key]) continue
|
||||
const npcPlanet = planetLogic.createNPCPlanet(i, position, planetPrefix)
|
||||
universeStore.planets[key] = npcPlanet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化游戏
|
||||
*/
|
||||
const initGame = async (playerName: string, homePlanetName: string, planetPrefix: string) => {
|
||||
const shouldInit = gameLogic.shouldInitializeGame(gameStore.player.planets)
|
||||
|
||||
if (!shouldInit) {
|
||||
const now = Date.now()
|
||||
|
||||
// 计算离线收益(直接同步计算)
|
||||
const bonuses = officerLogic.calculateActiveBonuses(gameStore.player.officers, now)
|
||||
gameStore.player.planets.forEach(planet => {
|
||||
resourceLogic.updatePlanetResources(planet, now, bonuses)
|
||||
})
|
||||
|
||||
// 只在没有NPC星球时才生成(首次加载已有玩家数据时)
|
||||
if (Object.keys(universeStore.planets).length === 0) {
|
||||
generateNPCPlanets(200, planetPrefix)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
gameStore.player = gameLogic.initializePlayer(gameStore.player.id, playerName)
|
||||
const initialPlanet = planetLogic.createInitialPlanet(gameStore.player.id, homePlanetName)
|
||||
gameStore.player.planets = [initialPlanet]
|
||||
gameStore.currentPlanetId = initialPlanet.id
|
||||
// 新玩家初始化时生成NPC星球
|
||||
generateNPCPlanets(200, planetPrefix)
|
||||
}
|
||||
|
||||
return {
|
||||
initGame,
|
||||
generateNPCPlanets
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useNPCStore } from '@/stores/npcStore'
|
||||
import type { FleetMission } from '@/types/game'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
|
||||
/**
|
||||
* 游戏更新循环
|
||||
* 处理游戏状态的定期更新
|
||||
*/
|
||||
export const useGameUpdate = (
|
||||
processMissionArrival: (mission: FleetMission) => Promise<void>,
|
||||
processMissionReturn: (mission: FleetMission) => void,
|
||||
processNPCMissionArrival: (npc: any, mission: FleetMission) => void,
|
||||
processNPCMissionReturn: (npc: any, mission: FleetMission) => void,
|
||||
updateNPCGrowth: (deltaSeconds: number) => void,
|
||||
updateNPCBehavior: (deltaSeconds: number) => void
|
||||
) => {
|
||||
const gameStore = useGameStore()
|
||||
const npcStore = useNPCStore()
|
||||
|
||||
/**
|
||||
* 游戏主更新函数
|
||||
*/
|
||||
const updateGame = () => {
|
||||
if (gameStore.isPaused) return
|
||||
const now = Date.now()
|
||||
gameStore.gameTime = now
|
||||
|
||||
// 检查军官过期
|
||||
gameLogic.checkOfficersExpiration(gameStore.player.officers, now)
|
||||
|
||||
// 处理游戏更新(建造队列、研究队列等)
|
||||
const result = gameLogic.processGameUpdate(gameStore.player, now)
|
||||
gameStore.player.researchQueue = result.updatedResearchQueue
|
||||
|
||||
// 处理舰队任务
|
||||
gameStore.player.fleetMissions.forEach(mission => {
|
||||
if (mission.status === 'outbound' && now >= mission.arrivalTime) {
|
||||
processMissionArrival(mission)
|
||||
} else if (mission.status === 'returning' && mission.returnTime && now >= mission.returnTime) {
|
||||
processMissionReturn(mission)
|
||||
}
|
||||
})
|
||||
|
||||
// 处理NPC舰队任务
|
||||
npcStore.npcs.forEach(npc => {
|
||||
if (npc.fleetMissions) {
|
||||
npc.fleetMissions.forEach(mission => {
|
||||
if (mission.status === 'outbound' && now >= mission.arrivalTime) {
|
||||
processNPCMissionArrival(npc, mission)
|
||||
} else if (mission.status === 'returning' && mission.returnTime && now >= mission.returnTime) {
|
||||
processNPCMissionReturn(npc, mission)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// NPC成长系统更新
|
||||
updateNPCGrowth(1) // 传入1秒的时间间隔
|
||||
|
||||
// NPC行为系统更新(侦查和攻击决策)
|
||||
updateNPCBehavior(1)
|
||||
}
|
||||
|
||||
return {
|
||||
updateGame
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export const useI18n = () => {
|
||||
const messages = computed(() => locales[currentLocale.value])
|
||||
|
||||
// 获取翻译文本的辅助函数
|
||||
const t = (key: string): string => {
|
||||
const t = (key: string, params?: Record<string, string | number>): string => {
|
||||
const keys = key.split('.')
|
||||
let value: any = messages.value
|
||||
|
||||
@@ -22,7 +22,16 @@ export const useI18n = () => {
|
||||
}
|
||||
}
|
||||
|
||||
return typeof value === 'string' ? value : key
|
||||
let result = typeof value === 'string' ? value : key
|
||||
|
||||
// 替换参数占位符
|
||||
if (params) {
|
||||
Object.entries(params).forEach(([paramKey, paramValue]) => {
|
||||
result = result.replace(new RegExp(`\\{${paramKey}\\}`, 'g'), String(paramValue))
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const setLocale = (locale: Locale) => {
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useUniverseStore } from '@/stores/universeStore'
|
||||
import { useNPCStore } from '@/stores/npcStore'
|
||||
import type { FleetMission } from '@/types/game'
|
||||
import { MissionType } from '@/types/game'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
import * as fleetLogic from '@/logic/fleetLogic'
|
||||
import * as shipLogic from '@/logic/shipLogic'
|
||||
import * as resourceLogic from '@/logic/resourceLogic'
|
||||
import * as diplomaticLogic from '@/logic/diplomaticLogic'
|
||||
|
||||
/**
|
||||
* 舰队任务处理
|
||||
* 处理玩家舰队任务的到达和返回
|
||||
*/
|
||||
export const useMissionHandler = (t: (key: string) => string) => {
|
||||
const gameStore = useGameStore()
|
||||
const universeStore = useUniverseStore()
|
||||
const npcStore = useNPCStore()
|
||||
|
||||
/**
|
||||
* 处理任务到达
|
||||
*/
|
||||
const processMissionArrival = async (mission: FleetMission) => {
|
||||
// 从宇宙星球地图中查找目标星球
|
||||
const targetKey = gameLogic.generatePositionKey(
|
||||
mission.targetPosition.galaxy,
|
||||
mission.targetPosition.system,
|
||||
mission.targetPosition.position
|
||||
)
|
||||
// 先从玩家星球中查找,再从宇宙地图中查找
|
||||
const targetPlanet =
|
||||
gameStore.player.planets.find(
|
||||
p =>
|
||||
p.position.galaxy === mission.targetPosition.galaxy &&
|
||||
p.position.system === mission.targetPosition.system &&
|
||||
p.position.position === mission.targetPosition.position
|
||||
) || universeStore.planets[targetKey]
|
||||
|
||||
// 获取起始星球名称(用于报告)
|
||||
const originPlanet = gameStore.player.planets.find(p => p.id === mission.originPlanetId)
|
||||
const originPlanetName = originPlanet?.name || t('fleetView.unknownPlanet')
|
||||
|
||||
if (mission.missionType === MissionType.Transport) {
|
||||
const result = fleetLogic.processTransportArrival(mission, targetPlanet, gameStore.player, npcStore.npcs)
|
||||
// 生成运输任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Transport,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName:
|
||||
targetPlanet?.name || `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
|
||||
success: result.success,
|
||||
message: result.success ? t('missionReports.transportSuccess') : t('missionReports.transportFailed'),
|
||||
details: {
|
||||
transportedResources: mission.cargo
|
||||
},
|
||||
read: false
|
||||
})
|
||||
} else if (mission.missionType === MissionType.Attack) {
|
||||
const attackResult = await fleetLogic.processAttackArrival(mission, targetPlanet, gameStore.player, null, gameStore.player.planets)
|
||||
if (attackResult) {
|
||||
gameStore.player.battleReports.push(attackResult.battleResult)
|
||||
|
||||
// 检查是否攻击了NPC星球,更新外交关系
|
||||
if (targetPlanet) {
|
||||
const targetNpc = npcStore.npcs.find(npc => npc.planets.some(p => p.id === targetPlanet.id))
|
||||
if (targetNpc) {
|
||||
diplomaticLogic.handleAttackReputation(gameStore.player, targetNpc, attackResult.battleResult, npcStore.npcs)
|
||||
}
|
||||
}
|
||||
|
||||
if (attackResult.moon) {
|
||||
gameStore.player.planets.push(attackResult.moon)
|
||||
}
|
||||
if (attackResult.debrisField) {
|
||||
// 将残骸场添加到游戏状态
|
||||
universeStore.debrisFields[attackResult.debrisField.id] = attackResult.debrisField
|
||||
}
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Colonize) {
|
||||
const newPlanet = fleetLogic.processColonizeArrival(mission, targetPlanet, gameStore.player, t('planet.colonyPrefix'))
|
||||
// 生成殖民任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Colonize,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: newPlanet?.id,
|
||||
targetPlanetName: newPlanet?.name,
|
||||
success: !!newPlanet,
|
||||
message: newPlanet ? t('missionReports.colonizeSuccess') : t('missionReports.colonizeFailed'),
|
||||
details: newPlanet
|
||||
? {
|
||||
newPlanetId: newPlanet.id,
|
||||
newPlanetName: newPlanet.name
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
if (newPlanet) {
|
||||
gameStore.player.planets.push(newPlanet)
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Spy) {
|
||||
const spyReport = fleetLogic.processSpyArrival(mission, targetPlanet, gameStore.player, null, npcStore.npcs)
|
||||
if (spyReport) gameStore.player.spyReports.push(spyReport)
|
||||
} else if (mission.missionType === MissionType.Deploy) {
|
||||
const deployed = fleetLogic.processDeployArrival(mission, targetPlanet, gameStore.player.id)
|
||||
// 生成部署任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Deploy,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName:
|
||||
targetPlanet?.name || `[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`,
|
||||
success: deployed,
|
||||
message: deployed ? t('missionReports.deploySuccess') : t('missionReports.deployFailed'),
|
||||
details: {
|
||||
deployedFleet: mission.fleet
|
||||
},
|
||||
read: false
|
||||
})
|
||||
if (deployed) {
|
||||
const missionIndex = gameStore.player.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) gameStore.player.fleetMissions.splice(missionIndex, 1)
|
||||
return
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Recycle) {
|
||||
// 处理回收任务
|
||||
const debrisId = `debris_${mission.targetPosition.galaxy}_${mission.targetPosition.system}_${mission.targetPosition.position}`
|
||||
const debrisField = universeStore.debrisFields[debrisId]
|
||||
const recycleResult = fleetLogic.processRecycleArrival(mission, debrisField)
|
||||
|
||||
// 生成回收任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Recycle,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
success: !!recycleResult,
|
||||
message: recycleResult ? t('missionReports.recycleSuccess') : t('missionReports.recycleFailed'),
|
||||
details: recycleResult
|
||||
? {
|
||||
recycledResources: recycleResult.collectedResources,
|
||||
remainingDebris: recycleResult.remainingDebris || undefined
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
|
||||
if (recycleResult && debrisField) {
|
||||
if (recycleResult.remainingDebris && (recycleResult.remainingDebris.metal > 0 || recycleResult.remainingDebris.crystal > 0)) {
|
||||
// 更新残骸场
|
||||
universeStore.debrisFields[debrisId] = {
|
||||
id: debrisField.id,
|
||||
position: debrisField.position,
|
||||
resources: recycleResult.remainingDebris,
|
||||
createdAt: debrisField.createdAt,
|
||||
expiresAt: debrisField.expiresAt
|
||||
}
|
||||
} else {
|
||||
// 残骸场已被完全收集,删除
|
||||
delete universeStore.debrisFields[debrisId]
|
||||
}
|
||||
}
|
||||
} else if (mission.missionType === MissionType.Destroy) {
|
||||
// 处理行星毁灭任务
|
||||
const destroyResult = fleetLogic.processDestroyArrival(mission, targetPlanet, gameStore.player)
|
||||
|
||||
// 生成毁灭任务报告
|
||||
if (!gameStore.player.missionReports) {
|
||||
gameStore.player.missionReports = []
|
||||
}
|
||||
gameStore.player.missionReports.push({
|
||||
id: `mission-report-${mission.id}`,
|
||||
timestamp: Date.now(),
|
||||
missionType: MissionType.Destroy,
|
||||
originPlanetId: mission.originPlanetId,
|
||||
originPlanetName,
|
||||
targetPosition: mission.targetPosition,
|
||||
targetPlanetId: targetPlanet?.id,
|
||||
targetPlanetName: targetPlanet?.name,
|
||||
success: destroyResult?.success || false,
|
||||
message: destroyResult?.success ? t('missionReports.destroySuccess') : t('missionReports.destroyFailed'),
|
||||
details: destroyResult?.success
|
||||
? {
|
||||
destroyedPlanetName:
|
||||
targetPlanet?.name ||
|
||||
`[${mission.targetPosition.galaxy}:${mission.targetPosition.system}:${mission.targetPosition.position}]`
|
||||
}
|
||||
: undefined,
|
||||
read: false
|
||||
})
|
||||
|
||||
if (destroyResult && destroyResult.success && destroyResult.planetId) {
|
||||
// 星球被摧毁
|
||||
// 从玩家星球列表中移除(如果是玩家的星球)
|
||||
const planetIndex = gameStore.player.planets.findIndex(p => p.id === destroyResult.planetId)
|
||||
if (planetIndex > -1) {
|
||||
gameStore.player.planets.splice(planetIndex, 1)
|
||||
} else {
|
||||
// 不是玩家星球,从宇宙地图中移除
|
||||
delete universeStore.planets[targetKey]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理任务返回
|
||||
*/
|
||||
const processMissionReturn = (mission: FleetMission) => {
|
||||
const originPlanet = gameStore.player.planets.find(p => p.id === mission.originPlanetId)
|
||||
if (!originPlanet) return
|
||||
shipLogic.addFleet(originPlanet.fleet, mission.fleet)
|
||||
resourceLogic.addResources(originPlanet.resources, mission.cargo)
|
||||
const missionIndex = gameStore.player.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) gameStore.player.fleetMissions.splice(missionIndex, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
processMissionArrival,
|
||||
processMissionReturn
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useUniverseStore } from '@/stores/universeStore'
|
||||
import { useNPCStore } from '@/stores/npcStore'
|
||||
import type { NPC, FleetMission, IncomingFleetAlert } from '@/types/game'
|
||||
import { MissionType } from '@/types/game'
|
||||
import * as gameLogic from '@/logic/gameLogic'
|
||||
import * as fleetLogic from '@/logic/fleetLogic'
|
||||
import * as shipLogic from '@/logic/shipLogic'
|
||||
import * as npcGrowthLogic from '@/logic/npcGrowthLogic'
|
||||
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
||||
|
||||
/**
|
||||
* NPC处理
|
||||
* 处理NPC舰队任务、成长系统、行为系统
|
||||
*/
|
||||
export const useNPCHandler = () => {
|
||||
const gameStore = useGameStore()
|
||||
const universeStore = useUniverseStore()
|
||||
const npcStore = useNPCStore()
|
||||
|
||||
/**
|
||||
* 移除即将到来的舰队警告
|
||||
*/
|
||||
const removeIncomingFleetAlert = (alert: IncomingFleetAlert) => {
|
||||
if (!gameStore.player.incomingFleetAlerts) return
|
||||
const index = gameStore.player.incomingFleetAlerts.indexOf(alert)
|
||||
if (index > -1) {
|
||||
gameStore.player.incomingFleetAlerts.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务ID移除即将到来的舰队警告
|
||||
*/
|
||||
const removeIncomingFleetAlertById = (missionId: string) => {
|
||||
if (!gameStore.player.incomingFleetAlerts) return
|
||||
const index = gameStore.player.incomingFleetAlerts.findIndex(a => a.id === missionId)
|
||||
if (index > -1) {
|
||||
gameStore.player.incomingFleetAlerts.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理NPC任务到达
|
||||
*/
|
||||
const processNPCMissionArrival = (npc: NPC, mission: FleetMission) => {
|
||||
if (mission.missionType === MissionType.Recycle) {
|
||||
// NPC回收任务到达
|
||||
const debrisId = mission.debrisFieldId
|
||||
if (!debrisId) {
|
||||
console.warn('[NPC Mission] Recycle mission missing debrisFieldId')
|
||||
mission.status = 'returning'
|
||||
mission.returnTime = Date.now() + (mission.arrivalTime - mission.departureTime)
|
||||
return
|
||||
}
|
||||
|
||||
const debrisField = universeStore.debrisFields[debrisId]
|
||||
const recycleResult = fleetLogic.processRecycleArrival(mission, debrisField)
|
||||
|
||||
if (recycleResult && debrisField) {
|
||||
if (recycleResult.remainingDebris && (recycleResult.remainingDebris.metal > 0 || recycleResult.remainingDebris.crystal > 0)) {
|
||||
// 更新残骸场
|
||||
universeStore.debrisFields[debrisId] = {
|
||||
id: debrisField.id,
|
||||
position: debrisField.position,
|
||||
resources: recycleResult.remainingDebris,
|
||||
createdAt: debrisField.createdAt
|
||||
}
|
||||
} else {
|
||||
// 残骸已被完全回收,从宇宙中删除
|
||||
delete universeStore.debrisFields[debrisId]
|
||||
}
|
||||
}
|
||||
|
||||
// 移除即将到来的警告(回收任务已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
|
||||
// 设置返回时间
|
||||
mission.returnTime = Date.now() + (mission.arrivalTime - mission.departureTime)
|
||||
return
|
||||
}
|
||||
|
||||
// 找到目标星球
|
||||
const targetKey = gameLogic.generatePositionKey(
|
||||
mission.targetPosition.galaxy,
|
||||
mission.targetPosition.system,
|
||||
mission.targetPosition.position
|
||||
)
|
||||
const targetPlanet =
|
||||
gameStore.player.planets.find(
|
||||
p =>
|
||||
p.position.galaxy === mission.targetPosition.galaxy &&
|
||||
p.position.system === mission.targetPosition.system &&
|
||||
p.position.position === mission.targetPosition.position
|
||||
) || universeStore.planets[targetKey]
|
||||
|
||||
if (!targetPlanet) {
|
||||
console.warn('[NPC Mission] Target planet not found')
|
||||
return
|
||||
}
|
||||
|
||||
if (mission.missionType === MissionType.Spy) {
|
||||
// NPC侦查到达
|
||||
const { spiedNotification, spyReport } = npcBehaviorLogic.processNPCSpyArrival(npc, mission, targetPlanet, gameStore.player)
|
||||
|
||||
// 保存侦查报告到NPC(用于后续攻击决策)
|
||||
if (!npc.playerSpyReports) {
|
||||
npc.playerSpyReports = {}
|
||||
}
|
||||
npc.playerSpyReports[targetPlanet.id] = spyReport
|
||||
|
||||
// 添加被侦查通知给玩家
|
||||
if (!gameStore.player.spiedNotifications) {
|
||||
gameStore.player.spiedNotifications = []
|
||||
}
|
||||
gameStore.player.spiedNotifications.push(spiedNotification)
|
||||
|
||||
// 移除即将到来的警告(侦查已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
} else if (mission.missionType === MissionType.Attack) {
|
||||
// NPC攻击到达 - 使用专门的NPC攻击处理逻辑
|
||||
fleetLogic.processNPCAttackArrival(npc, mission, targetPlanet, gameStore.player, gameStore.player.planets).then(attackResult => {
|
||||
if (attackResult) {
|
||||
// 添加战斗报告给玩家
|
||||
gameStore.player.battleReports.push(attackResult.battleResult)
|
||||
|
||||
// 如果生成月球,添加到玩家星球列表
|
||||
if (attackResult.moon) {
|
||||
gameStore.player.planets.push(attackResult.moon)
|
||||
}
|
||||
|
||||
// 如果生成残骸场,添加到宇宙残骸场列表
|
||||
if (attackResult.debrisField) {
|
||||
universeStore.debrisFields[attackResult.debrisField.id] = attackResult.debrisField
|
||||
}
|
||||
}
|
||||
|
||||
// 移除即将到来的警告(攻击已到达)
|
||||
removeIncomingFleetAlertById(mission.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理NPC任务返回
|
||||
*/
|
||||
const processNPCMissionReturn = (npc: NPC, mission: FleetMission) => {
|
||||
// 找到NPC的起始星球
|
||||
const originPlanet = npc.planets.find(p => p.id === mission.originPlanetId)
|
||||
if (!originPlanet) return
|
||||
|
||||
// 返还舰队
|
||||
shipLogic.addFleet(originPlanet.fleet, mission.fleet)
|
||||
|
||||
// 如果携带掠夺资源,给NPC添加资源
|
||||
if (mission.cargo) {
|
||||
originPlanet.resources.metal += mission.cargo.metal
|
||||
originPlanet.resources.crystal += mission.cargo.crystal
|
||||
originPlanet.resources.deuterium += mission.cargo.deuterium
|
||||
}
|
||||
|
||||
// 从NPC任务列表中移除
|
||||
if (npc.fleetMissions) {
|
||||
const missionIndex = npc.fleetMissions.indexOf(mission)
|
||||
if (missionIndex > -1) {
|
||||
npc.fleetMissions.splice(missionIndex, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NPC成长系统更新
|
||||
let npcUpdateCounter = 0
|
||||
const NPC_UPDATE_INTERVAL = 10
|
||||
|
||||
/**
|
||||
* 更新NPC成长系统
|
||||
*/
|
||||
const updateNPCGrowth = (deltaSeconds: number) => {
|
||||
// 累积时间
|
||||
npcUpdateCounter += deltaSeconds
|
||||
|
||||
// 只在达到更新间隔时才执行
|
||||
if (npcUpdateCounter < NPC_UPDATE_INTERVAL) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取所有星球
|
||||
const allPlanets = Object.values(universeStore.planets)
|
||||
|
||||
// 如果NPC store为空,从星球数据中初始化NPC
|
||||
if (npcStore.npcs.length === 0) {
|
||||
const npcMap = new Map<string, any>()
|
||||
|
||||
allPlanets.forEach(planet => {
|
||||
// 跳过玩家的星球
|
||||
if (planet.ownerId === gameStore.player.id || !planet.ownerId) return
|
||||
|
||||
// 这是NPC的星球
|
||||
if (!npcMap.has(planet.ownerId)) {
|
||||
npcMap.set(planet.ownerId, {
|
||||
id: planet.ownerId,
|
||||
name: `NPC-${planet.ownerId.substring(0, 8)}`,
|
||||
planets: [],
|
||||
technologies: {},
|
||||
difficulty: 'medium' as const,
|
||||
relations: {},
|
||||
allies: [],
|
||||
enemies: []
|
||||
})
|
||||
}
|
||||
|
||||
npcMap.get(planet.ownerId)!.planets.push(planet)
|
||||
})
|
||||
|
||||
// 保存到store
|
||||
npcStore.npcs = Array.from(npcMap.values())
|
||||
|
||||
// 如果有NPC,基于玩家实力初始化NPC
|
||||
if (npcStore.npcs.length > 0) {
|
||||
const gameState: npcGrowthLogic.NPCGrowthGameState = {
|
||||
planets: allPlanets,
|
||||
player: gameStore.player,
|
||||
npcs: npcStore.npcs
|
||||
}
|
||||
|
||||
const playerPower = npcGrowthLogic.calculatePlayerAveragePower(gameState)
|
||||
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcGrowthLogic.initializeNPCStartingPower(npc, playerPower)
|
||||
})
|
||||
|
||||
// 初始化NPC之间的外交关系(盟友/敌人)
|
||||
npcGrowthLogic.initializeNPCDiplomacy(npcStore.npcs)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有NPC,直接返回
|
||||
if (npcStore.npcs.length === 0) {
|
||||
npcUpdateCounter = 0
|
||||
return
|
||||
}
|
||||
|
||||
// 构建游戏状态
|
||||
const gameState: npcGrowthLogic.NPCGrowthGameState = {
|
||||
planets: allPlanets,
|
||||
player: gameStore.player,
|
||||
npcs: npcStore.npcs
|
||||
}
|
||||
|
||||
// 使用累积的时间更新每个NPC
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcGrowthLogic.updateNPCGrowth(npc, gameState, npcUpdateCounter)
|
||||
})
|
||||
|
||||
// 重置计数器
|
||||
npcUpdateCounter = 0
|
||||
}
|
||||
|
||||
// NPC行为系统更新
|
||||
let npcBehaviorCounter = 0
|
||||
const NPC_BEHAVIOR_INTERVAL = 5
|
||||
|
||||
/**
|
||||
* 更新NPC行为系统
|
||||
*/
|
||||
const updateNPCBehavior = (deltaSeconds: number) => {
|
||||
// 累积时间
|
||||
npcBehaviorCounter += deltaSeconds
|
||||
|
||||
// 只在达到更新间隔时才执行
|
||||
if (npcBehaviorCounter < NPC_BEHAVIOR_INTERVAL) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有NPC,直接返回
|
||||
if (npcStore.npcs.length === 0) {
|
||||
npcBehaviorCounter = 0
|
||||
return
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
const allPlanets = Object.values(universeStore.planets)
|
||||
|
||||
// 更新每个NPC的行为
|
||||
npcStore.npcs.forEach(npc => {
|
||||
npcBehaviorLogic.updateNPCBehavior(npc, gameStore.player, allPlanets, universeStore.debrisFields, now)
|
||||
})
|
||||
|
||||
npcBehaviorCounter = 0
|
||||
}
|
||||
|
||||
return {
|
||||
processNPCMissionArrival,
|
||||
processNPCMissionReturn,
|
||||
removeIncomingFleetAlert,
|
||||
removeIncomingFleetAlertById,
|
||||
updateNPCGrowth,
|
||||
updateNPCBehavior
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import type { Ref } from 'vue'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import type { BuildQueueItem } from '@/types/game'
|
||||
import * as buildingValidation from '@/logic/buildingValidation'
|
||||
import * as resourceLogic from '@/logic/resourceLogic'
|
||||
import * as researchValidation from '@/logic/researchValidation'
|
||||
|
||||
/**
|
||||
* 队列处理
|
||||
* 处理建造队列和研究队列的取消操作
|
||||
*/
|
||||
export const useQueueHandler = (
|
||||
t: (key: string) => string,
|
||||
confirmDialogOpen: Ref<boolean>,
|
||||
confirmDialogTitle: Ref<string>,
|
||||
confirmDialogMessage: Ref<string>,
|
||||
confirmDialogAction: Ref<(() => void) | null>
|
||||
) => {
|
||||
const gameStore = useGameStore()
|
||||
|
||||
/**
|
||||
* 取消建造
|
||||
*/
|
||||
const handleCancelBuild = (queueId: string) => {
|
||||
confirmDialogTitle.value = t('queue.cancelBuild')
|
||||
confirmDialogMessage.value = t('queue.confirmCancel')
|
||||
confirmDialogAction.value = () => {
|
||||
if (!gameStore.currentPlanet) return false
|
||||
const { item, index } = buildingValidation.findQueueItem(gameStore.currentPlanet.buildQueue, queueId)
|
||||
if (!item) return false
|
||||
if (item.type === 'building') {
|
||||
const refund = buildingValidation.cancelBuildingUpgrade(gameStore.currentPlanet, item)
|
||||
resourceLogic.addResources(gameStore.currentPlanet.resources, refund)
|
||||
}
|
||||
gameStore.currentPlanet.buildQueue.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
confirmDialogOpen.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消研究
|
||||
*/
|
||||
const handleCancelResearch = (queueId: string) => {
|
||||
confirmDialogTitle.value = t('queue.cancelResearch')
|
||||
confirmDialogMessage.value = t('queue.confirmCancel')
|
||||
confirmDialogAction.value = () => {
|
||||
if (!gameStore.currentPlanet) return false
|
||||
const { item, index } = buildingValidation.findQueueItem(gameStore.player.researchQueue, queueId)
|
||||
if (!item) return false
|
||||
if (item.type === 'technology') {
|
||||
const refund = researchValidation.cancelTechnologyResearch(item)
|
||||
resourceLogic.addResources(gameStore.currentPlanet.resources, refund)
|
||||
}
|
||||
gameStore.player.researchQueue.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
confirmDialogOpen.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列项名称
|
||||
*/
|
||||
const getItemName = (item: BuildQueueItem): string => {
|
||||
if (item.type === 'building' || item.type === 'demolish') {
|
||||
const buildingName = t(`buildings.${item.itemType}`)
|
||||
return item.type === 'demolish' ? `${t('buildingsView.demolish')} - ${buildingName}` : buildingName
|
||||
} else if (item.type === 'technology') {
|
||||
return t(`technologies.${item.itemType}`)
|
||||
} else if (item.type === 'ship') {
|
||||
return t(`ships.${item.itemType}`)
|
||||
} else if (item.type === 'defense') {
|
||||
return t(`defenses.${item.itemType}`)
|
||||
}
|
||||
return t('common.unknown')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取剩余时间(秒)
|
||||
*/
|
||||
const getRemainingTime = (item: BuildQueueItem): number => {
|
||||
const now = Date.now()
|
||||
return Math.max(0, Math.floor((item.endTime - now) / 1000))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列进度(百分比)
|
||||
*/
|
||||
const getQueueProgress = (item: BuildQueueItem): number => {
|
||||
const now = Date.now()
|
||||
const total = item.endTime - item.startTime
|
||||
const elapsed = now - item.startTime
|
||||
return Math.min(100, Math.max(0, (elapsed / total) * 100))
|
||||
}
|
||||
|
||||
return {
|
||||
handleCancelBuild,
|
||||
handleCancelResearch,
|
||||
getItemName,
|
||||
getRemainingTime,
|
||||
getQueueProgress
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
|
||||
baseTime: 30, // 减少建造时间:60→30秒
|
||||
costMultiplier: 2,
|
||||
spaceUsage: 5,
|
||||
fleetStorageBonus: 1000, // 每级增加100舰队仓储
|
||||
fleetStorageBonus: 1000, // 每级增加1000舰队仓储
|
||||
requirements: { [BuildingType.RoboticsFactory]: 2 },
|
||||
levelRequirements: {
|
||||
8: { [BuildingType.RoboticsFactory]: 5, [BuildingType.ResearchLab]: 5 },
|
||||
@@ -251,7 +251,7 @@ export const BUILDINGS: Record<BuildingType, BuildingConfig> = {
|
||||
[BuildingType.Terraformer]: {
|
||||
id: BuildingType.Terraformer,
|
||||
name: '地形改造器',
|
||||
description: '改造行星地形,每级增加5个可用空间',
|
||||
description: '改造行星地形,每级增加30个可用空间',
|
||||
baseCost: { metal: 0, crystal: 50000, deuterium: 100000, darkMatter: 0, energy: 0 },
|
||||
baseTime: 60,
|
||||
costMultiplier: 2,
|
||||
@@ -433,7 +433,7 @@ export const TECHNOLOGIES: Record<TechnologyType, TechnologyConfig> = {
|
||||
baseCost: { metal: 0, crystal: 400, deuterium: 600, darkMatter: 0, energy: 0 },
|
||||
baseTime: 60,
|
||||
costMultiplier: 2,
|
||||
fleetStorageBonus: 500, // 每级全局增加50舰队仓储
|
||||
fleetStorageBonus: 500, // 每级全局增加500舰队仓储
|
||||
maxLevel: 10, // 最多10级(最多11个研究队列)
|
||||
requirements: { [BuildingType.ResearchLab]: 1 },
|
||||
levelRequirements: {
|
||||
@@ -588,7 +588,7 @@ export const TECHNOLOGIES: Record<TechnologyType, TechnologyConfig> = {
|
||||
[TechnologyType.TerraformingTechnology]: {
|
||||
id: TechnologyType.TerraformingTechnology,
|
||||
name: '地形改造技术',
|
||||
description: '研究行星地形改造技术,每级为所有行星增加5个可用空间',
|
||||
description: '研究行星地形改造技术,每级为所有行星增加30个可用空间',
|
||||
baseCost: { metal: 0, crystal: 20000, deuterium: 40000, darkMatter: 0, energy: 0 },
|
||||
baseTime: 90,
|
||||
costMultiplier: 2,
|
||||
@@ -867,7 +867,7 @@ export const SHIPS: Record<ShipType, ShipConfig> = {
|
||||
fuelConsumption: 1,
|
||||
storageUsage: 100,
|
||||
requirements: {
|
||||
[BuildingType.PlanetDestroyerFactory]: 10,
|
||||
[BuildingType.PlanetDestroyerFactory]: 3,
|
||||
[TechnologyType.PlanetDestructionTech]: 7,
|
||||
[TechnologyType.HyperspaceDrive]: 7
|
||||
}
|
||||
@@ -1085,15 +1085,15 @@ export const MOON_CONFIG = {
|
||||
baseChance: 1, // 基础1%概率
|
||||
maxChance: 20, // 最大20%概率
|
||||
chancePerDebris: 100000, // 每10万资源增加1%概率
|
||||
baseSize: 60, // 月球基础空间
|
||||
lunarBaseSpaceBonus: 5 // 每级月球基地增加的空间
|
||||
baseSize: 100, // 月球基础空间
|
||||
lunarBaseSpaceBonus: 30 // 每级月球基地增加的空间
|
||||
}
|
||||
|
||||
// 行星配置
|
||||
export const PLANET_CONFIG = {
|
||||
baseSize: 200, // 行星基础空间
|
||||
terraformerSpaceBonus: 5, // 每级地形改造器增加的空间
|
||||
terraformingTechSpaceBonus: 3 // 每级地形改造技术增加的空间
|
||||
baseSize: 300, // 行星基础空间
|
||||
terraformerSpaceBonus: 30, // 每级地形改造器增加的空间
|
||||
terraformingTechSpaceBonus: 30 // 每级地形改造技术增加的空间
|
||||
}
|
||||
|
||||
// 舰队仓储配置
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: 'Anforderungen anzeigen',
|
||||
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
||||
current: 'Aktuell',
|
||||
level: 'Stufe'
|
||||
level: 'Stufe',
|
||||
gmModeActivated: 'GM-Modus aktiviert! Überprüfen Sie das Navigationsmenü.'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
||||
insufficientResources: 'Unzureichende Ressourcen',
|
||||
insufficientFleetStorage: 'Unzureichender Flottenspeicher',
|
||||
shieldDomeLimit: 'Schildkuppel-Limit erreicht',
|
||||
missileSiloLimit: 'Raketensilokapazität überschritten',
|
||||
insufficientMissiles: 'Unzureichende Interkontinentalraketen',
|
||||
invalidMissileCount: 'Ungültige Raketenanzahl',
|
||||
targetOutOfRange: 'Ziel außer Reichweite',
|
||||
cannotAttackOwnPlanet: 'Eigenen Planeten kann nicht angegriffen werden',
|
||||
fleetMissionsFull: 'Flottenmissionsplätze voll',
|
||||
insufficientFleet: 'Unzureichende Flotte',
|
||||
insufficientFuel: 'Unzureichender Treibstoff',
|
||||
@@ -166,8 +172,8 @@ export default {
|
||||
darkMatterCollector: 'Sammelt seltene Dunkle-Materie-Ressourcen',
|
||||
darkMatterTank: 'Erhöht Dunkle-Materie-Speicherkapazität',
|
||||
missileSilo: 'Lagert und startet Raketen, 10 Raketen pro Stufe',
|
||||
terraformer: 'Terraformt Planetenoberfläche, erhöht verfügbaren Platz um 5 pro Stufe',
|
||||
lunarBase: 'Erhöht verfügbaren Platz auf dem Mond, +5 Platz pro Stufe',
|
||||
terraformer: 'Terraformt Planetenoberfläche, erhöht verfügbaren Platz um 30 pro Stufe',
|
||||
lunarBase: 'Erhöht verfügbaren Platz auf dem Mond, +30 Platz pro Stufe',
|
||||
sensorPhalanx: 'Erkennt Flottenaktivitäten in umliegenden Systemen',
|
||||
jumpGate: 'Überträgt Flotten sofort zu anderen Monden',
|
||||
planetDestroyerFactory: 'Konstruiert ultimative Waffen zur Zerstörung von Planeten'
|
||||
@@ -283,7 +289,7 @@ export default {
|
||||
impulseDrive: 'Mittlere Antriebstechnologie',
|
||||
hyperspaceDrive: 'Fortgeschrittene Antriebstechnologie',
|
||||
darkMatterTechnology: 'Forschung zu Eigenschaften und Anwendungen von Dunkler Materie',
|
||||
terraformingTechnology: 'Forschung zur Planeten-Terraforming-Technologie, erhöht verfügbaren Platz aller Planeten um 3 pro Stufe',
|
||||
terraformingTechnology: 'Forschung zur Planeten-Terraforming-Technologie, erhöht verfügbaren Platz aller Planeten um 30 pro Stufe',
|
||||
planetDestructionTech: 'Schreckliche Technologie zur Zerstörung ganzer Planeten'
|
||||
},
|
||||
officers: {
|
||||
@@ -316,6 +322,7 @@ export default {
|
||||
cancelResearch: 'Forschung abbrechen',
|
||||
confirmCancel: 'Möchten Sie wirklich abbrechen? 50% der Ressourcen werden zurückerstattet.',
|
||||
level: 'Stufe',
|
||||
gmModeActivated: '',
|
||||
upgradeToLevel: 'Auf Stufe aufrüsten'
|
||||
},
|
||||
overview: {
|
||||
@@ -336,6 +343,7 @@ export default {
|
||||
usedSpace: 'Verwendeter Platz',
|
||||
spaceUsage: 'Platzbedarf',
|
||||
level: 'Stufe',
|
||||
gmModeActivated: '',
|
||||
upgradeCost: 'Ausbaukosten',
|
||||
buildTime: 'Bauzeit',
|
||||
upgrade: 'Ausbauen',
|
||||
@@ -361,6 +369,7 @@ export default {
|
||||
},
|
||||
shipyard: {
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
shield: 'Schild',
|
||||
armor: 'Panzerung',
|
||||
speed: 'Geschwindigkeit',
|
||||
@@ -378,6 +387,7 @@ export default {
|
||||
title: 'Raumschiffwerft',
|
||||
fleetStorage: 'Flottenspeicher',
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
shield: 'Schild',
|
||||
speed: 'Geschwindigkeit',
|
||||
cargoCapacity: 'Ladekapazität',
|
||||
@@ -392,6 +402,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
shield: 'Schild',
|
||||
armor: 'Panzerung',
|
||||
buildCost: 'Baukosten',
|
||||
@@ -405,6 +416,7 @@ export default {
|
||||
defenseView: {
|
||||
title: 'Verteidigung',
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
shield: 'Schild',
|
||||
armor: 'Panzerung',
|
||||
buildTime: 'Bauzeit',
|
||||
@@ -414,6 +426,7 @@ export default {
|
||||
totalCost: 'Gesamtkosten',
|
||||
build: 'Bauen',
|
||||
shieldDomeBuilt: 'Schildkuppel bereits gebaut',
|
||||
missileCapacity: 'Raketenkapazität',
|
||||
inputError: 'Eingabefehler',
|
||||
inputErrorMessage: 'Bitte Baumenge eingeben!',
|
||||
buildFailed: 'Bau fehlgeschlagen',
|
||||
@@ -427,6 +440,7 @@ export default {
|
||||
flightMissions: 'Flugmissionen',
|
||||
currentPlanetFleet: 'Flotte auf diesem Planeten',
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
shield: 'Schild',
|
||||
armor: 'Panzerung',
|
||||
speed: 'Geschwindigkeit',
|
||||
@@ -521,27 +535,38 @@ export default {
|
||||
selectSystem: 'System auswählen',
|
||||
view: 'Anzeigen',
|
||||
myPlanet: 'Mein Planet',
|
||||
myPlanets: 'Meine Planeten',
|
||||
myPlanets: 'Meine Systeme ansehen',
|
||||
npcPlanets: 'NPC-Planeten',
|
||||
selectPlanetToView: 'Planet zum Anzeigen auswählen',
|
||||
selectPlanetToView: 'Planet auswählen, um sein System anzuzeigen',
|
||||
totalPositions: 'Insgesamt 10 Planetenpositionen',
|
||||
mine: 'Mein',
|
||||
hostile: 'Feindlich',
|
||||
emptySlot: 'Leer - Kolonisierbar',
|
||||
scout: 'Spähen',
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
colonize: 'Kolonisieren',
|
||||
switch: 'Wechseln',
|
||||
recycle: 'Recyceln',
|
||||
debrisField: 'Trümmerfeld',
|
||||
scoutPlanetTitle: 'Planet ausspionieren',
|
||||
attackPlanetTitle: 'Planet angreifen',
|
||||
missileAttackTitle: 'Raketenangriff',
|
||||
colonizePlanetTitle: 'Planet kolonisieren',
|
||||
recyclePlanetTitle: 'Trümmer recyceln',
|
||||
scoutPlanetMessage:
|
||||
'Möchten Sie wirklich Spionagesonden senden, um Planet [{coordinates}] auszuspionieren?\n\nBitte gehen Sie zur Flottenseite, um Schiffe auszuwählen und zu senden.',
|
||||
attackPlanetMessage:
|
||||
'Möchten Sie wirklich Planet [{coordinates}] angreifen?\n\nBitte gehen Sie zur Flottenseite, um Schiffe auszuwählen und zu senden.',
|
||||
missileAttackMessage: 'Interkontinentalraketen starten, um Planet [{coordinates}] anzugreifen',
|
||||
missileCount: 'Raketenanzahl',
|
||||
availableMissiles: 'Verfügbare Raketen',
|
||||
missileRange: 'Raketenreichweite',
|
||||
systems: 'Systeme',
|
||||
distance: 'Entfernung',
|
||||
flightTime: 'Flugzeit',
|
||||
launchMissile: 'Starten',
|
||||
cancel: 'Abbrechen',
|
||||
colonizePlanetMessage:
|
||||
'Möchten Sie wirklich Position [{coordinates}] kolonisieren?\n\nBitte gehen Sie zur Flottenseite, um ein Kolonieschiff zu senden.',
|
||||
recyclePlanetMessage:
|
||||
@@ -699,10 +724,24 @@ export default {
|
||||
gamePaused: 'Spiel pausiert',
|
||||
gameResumed: 'Spiel fortgesetzt',
|
||||
playerName: 'Spielername',
|
||||
gameSpeed: 'Spielgeschwindigkeit',
|
||||
gameSpeedDesc: 'Aktueller Spielgeschwindigkeitsmultiplikator',
|
||||
gameSpeed: 'Ressourcenproduktionsgeschwindigkeit',
|
||||
gameSpeedDesc: 'Aktueller Ressourcenproduktionsgeschwindigkeitsmultiplikator',
|
||||
speedChanged: 'Ressourcenproduktionsgeschwindigkeit auf {speed}x geändert',
|
||||
speedReset: 'Ressourcenproduktionsgeschwindigkeit auf 1x zurückgesetzt',
|
||||
reset: 'Zurücksetzen',
|
||||
about: 'Über',
|
||||
version: 'Version',
|
||||
latestVersion: 'Neueste Version',
|
||||
checkUpdate: 'Update prüfen',
|
||||
checking: 'Prüfen...',
|
||||
newVersionAvailable: 'Neue Version {version} verfügbar',
|
||||
upToDate: 'Bereits auf dem neuesten Stand',
|
||||
checkUpdateCooldown: 'Bitte versuchen Sie es später erneut (5 Minuten Abklingzeit)',
|
||||
checkUpdateFailed: 'Update-Prüfung fehlgeschlagen, bitte überprüfen Sie Ihre Netzwerkverbindung',
|
||||
viewUpdate: 'Update ansehen',
|
||||
updateAvailable: 'Eine neue Version ist verfügbar. Klicken Sie, um die Versionshinweise anzuzeigen.',
|
||||
download: 'Herunterladen',
|
||||
goToDownload: 'Zum Download',
|
||||
buildDate: 'Build-Datum',
|
||||
community: 'Community',
|
||||
github: 'GitHub-Repository',
|
||||
@@ -741,12 +780,22 @@ export default {
|
||||
testSpy: 'Spionage testen',
|
||||
testAttack: 'Angriff testen',
|
||||
testSpyAndAttack: 'Spionage & Angriff testen',
|
||||
testSpyMessage: 'Klicken Sie auf Bestätigen, um die Spionagemission zu beschleunigen',
|
||||
testAttackMessage: 'Klicken Sie auf Bestätigen, um die Angriffsmission zu beschleunigen',
|
||||
testSpyAndAttackMessage: 'Klicken Sie auf Bestätigen, um die Missionen zu beschleunigen',
|
||||
initializeFleet: 'NPC-Flotte initialisieren',
|
||||
accelerateMissions: 'Alle Missionen beschleunigen (5s)',
|
||||
selectNPCFirst: 'Bitte wählen Sie zuerst einen NPC',
|
||||
npcNoProbes: 'NPC hat keine Spionagesonden',
|
||||
npcNoSpyReport: 'NPC muss zuerst spionieren',
|
||||
npcMissionFailed: 'Mission konnte nicht erstellt werden',
|
||||
npcNoPlanets: 'NPC hat keine Planeten',
|
||||
npcWillSpyIn5s: '{npcName} wird in 5 Sekunden spionieren',
|
||||
npcWillAttackIn5s: '{npcName} wird in 5 Sekunden angreifen',
|
||||
npcWillSpyAndAttack: '{npcName} wird in 5s spionieren und in 10s angreifen',
|
||||
acceleratedMissions: '{count} Missionen auf 5 Sekunden beschleunigt',
|
||||
npcFleetInitialized: '{npcName} Flotte initialisiert',
|
||||
npcFleetDetails: '100 Spionagesonden\n500 Leichte Jäger\n300 Schwere Jäger\n200 Kreuzer\n100 Schlachtschiffe\n50 Bomber\n30 Zerstörer\n20 Schlachtkreuzer',
|
||||
dangerZone: 'Gefahrenzone',
|
||||
dangerZoneDesc: 'Die folgenden Vorgänge sind irreversibel',
|
||||
resetGame: 'Spiel zurücksetzen',
|
||||
@@ -798,9 +847,32 @@ export default {
|
||||
events: {
|
||||
gift: 'Geschenk gesendet',
|
||||
attack: 'Angriff',
|
||||
missileAttack: 'Raketenangriff',
|
||||
allyAttacked: 'Verbündeter angegriffen',
|
||||
spy: 'Spionage',
|
||||
stealDebris: 'Trümmer gestohlen'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: '{metal}M {crystal}K {deuterium}D geschenkt',
|
||||
receivedGiftFromPlayer: 'Geschenk von Spieler erhalten',
|
||||
giftedToNpc: 'Sie haben {npcName} Ressourcen geschenkt. Ansehen +{reputation}',
|
||||
rejectedPlayerGift: 'Geschenk des Spielers abgelehnt',
|
||||
npcRejectedGift: '{npcName} hat Ihr Geschenk abgelehnt. Ansehen {reputation}',
|
||||
attackedNpc: '{npcName} angegriffen',
|
||||
wasAttackedByPlayer: 'Wurde von Spieler angegriffen',
|
||||
youAttackedNpc: 'Sie haben {npcName} angegriffen',
|
||||
playerAttackedAlly: 'Spieler hat Verbündeten {allyName} angegriffen',
|
||||
allyDispleased: '{allyName} ist unzufrieden, dass Sie ihren Verbündeten {targetName} angegriffen haben',
|
||||
wasSpiedByPlayer: 'Wurde von Spieler ausspioniert (entdeckt: {detected})',
|
||||
spyDetected: 'Ihre Spionage wurde von {npcName} entdeckt',
|
||||
stoleDebrisFromTerritory: 'Trümmer aus {npcName}s Territorium gestohlen',
|
||||
playerStoleDebris: 'Spieler hat Trümmer aus Territorium gestohlen',
|
||||
recycledDebrisNearNpc: 'Sie haben Trümmer in der Nähe von {npcName}s Planeten recycelt. Sie sind unzufrieden.',
|
||||
giftedResourcesToPlayer: 'Ressourcen an Spieler geschenkt',
|
||||
receivedGiftFromNpc: 'Geschenk von {npcName} erhalten',
|
||||
acceptedGiftFromNpc: 'Sie haben ein Geschenk von {npcName} angenommen: {metal}M {crystal}K {deuterium}D',
|
||||
playerRejectedGift: 'Spieler hat Geschenk abgelehnt',
|
||||
rejectedGiftFromNpc: 'Sie haben ein Geschenk von {npcName} abgelehnt. Ansehen {reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -809,5 +881,10 @@ export default {
|
||||
first: 'Erste',
|
||||
last: 'Letzte',
|
||||
page: 'Seite {page}'
|
||||
},
|
||||
notFound: {
|
||||
title: 'Seite nicht gefunden',
|
||||
description: 'Entschuldigung, die gesuchte Seite existiert nicht',
|
||||
goHome: 'Zur Startseite'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: 'View Requirements',
|
||||
requirementsNotMet: 'Requirements Not Met',
|
||||
current: 'Current',
|
||||
level: 'Level'
|
||||
level: 'Level',
|
||||
gmModeActivated: 'GM Mode Activated! Check the navigation menu.'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: 'Requirements not met',
|
||||
insufficientResources: 'Insufficient resources',
|
||||
insufficientFleetStorage: 'Insufficient fleet storage',
|
||||
shieldDomeLimit: 'Shield dome limit reached',
|
||||
missileSiloLimit: 'Missile silo capacity exceeded',
|
||||
insufficientMissiles: 'Insufficient interplanetary missiles',
|
||||
invalidMissileCount: 'Invalid missile count',
|
||||
targetOutOfRange: 'Target out of range',
|
||||
cannotAttackOwnPlanet: 'Cannot attack your own planet',
|
||||
fleetMissionsFull: 'Fleet mission slots full',
|
||||
insufficientFleet: 'Insufficient fleet',
|
||||
insufficientFuel: 'Insufficient fuel',
|
||||
@@ -164,8 +170,8 @@ export default {
|
||||
darkMatterCollector: 'Collects rare dark matter resources',
|
||||
darkMatterTank: 'Increases dark matter storage capacity',
|
||||
missileSilo: 'Stores and launches missiles, 10 missiles per level',
|
||||
terraformer: 'Terraforms planet surface, adds 5 available space per level',
|
||||
lunarBase: 'Increases available space on the moon, +5 space per level',
|
||||
terraformer: 'Terraforms planet surface, adds 30 available space per level',
|
||||
lunarBase: 'Increases available space on the moon, +30 space per level',
|
||||
sensorPhalanx: 'Detects fleet activities in surrounding systems',
|
||||
jumpGate: 'Instantly transfers fleets to other moons',
|
||||
planetDestroyerFactory: 'Constructs ultimate weapons capable of destroying planets'
|
||||
@@ -283,7 +289,7 @@ export default {
|
||||
impulseDrive: 'Intermediate propulsion technology',
|
||||
hyperspaceDrive: 'Advanced propulsion technology',
|
||||
darkMatterTechnology: 'Research into dark matter properties and applications',
|
||||
terraformingTechnology: 'Research planet terraforming technology, adds 3 available space to all planets per level',
|
||||
terraformingTechnology: 'Research planet terraforming technology, adds 30 available space to all planets per level',
|
||||
planetDestructionTech: 'Terrifying technology for destroying entire planets'
|
||||
},
|
||||
officers: {
|
||||
@@ -364,6 +370,7 @@ export default {
|
||||
},
|
||||
shipyard: {
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
shield: 'Shield',
|
||||
armor: 'Armor',
|
||||
speed: 'Speed',
|
||||
@@ -381,6 +388,7 @@ export default {
|
||||
title: 'Shipyard',
|
||||
fleetStorage: 'Fleet Storage',
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
shield: 'Shield',
|
||||
speed: 'Speed',
|
||||
cargoCapacity: 'Cargo Capacity',
|
||||
@@ -395,6 +403,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
shield: 'Shield',
|
||||
armor: 'Armor',
|
||||
buildCost: 'Build Cost',
|
||||
@@ -408,6 +417,7 @@ export default {
|
||||
defenseView: {
|
||||
title: 'Defense',
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
shield: 'Shield',
|
||||
armor: 'Armor',
|
||||
buildTime: 'Build Time',
|
||||
@@ -417,6 +427,7 @@ export default {
|
||||
totalCost: 'Total Cost',
|
||||
build: 'Build',
|
||||
shieldDomeBuilt: 'Shield dome already built',
|
||||
missileCapacity: 'Missile Capacity',
|
||||
inputError: 'Input Error',
|
||||
inputErrorMessage: 'Please enter build quantity!',
|
||||
buildFailed: 'Build Failed',
|
||||
@@ -429,6 +440,7 @@ export default {
|
||||
flightMissions: 'Flight Missions',
|
||||
currentPlanetFleet: 'Current Planet Fleet',
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
shield: 'Shield',
|
||||
armor: 'Armor',
|
||||
speed: 'Speed',
|
||||
@@ -525,26 +537,37 @@ export default {
|
||||
selectSystem: 'Select System',
|
||||
view: 'View',
|
||||
myPlanet: 'My Planet',
|
||||
myPlanets: 'My Planets',
|
||||
myPlanets: 'View My Systems',
|
||||
npcPlanets: 'NPC Planets',
|
||||
selectPlanetToView: 'Select planet to view',
|
||||
selectPlanetToView: 'Select planet to view its system',
|
||||
totalPositions: '10 planet positions total',
|
||||
mine: 'Mine',
|
||||
hostile: 'Hostile',
|
||||
emptySlot: 'Empty - Colonizable',
|
||||
scout: 'Scout',
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
colonize: 'Colonize',
|
||||
switch: 'Switch',
|
||||
recycle: 'Recycle',
|
||||
debrisField: 'Debris Field',
|
||||
scoutPlanetTitle: 'Scout Planet',
|
||||
attackPlanetTitle: 'Attack Planet',
|
||||
missileAttackTitle: 'Missile Attack',
|
||||
colonizePlanetTitle: 'Colonize Planet',
|
||||
recyclePlanetTitle: 'Recycle Debris',
|
||||
scoutPlanetMessage:
|
||||
'Are you sure you want to send espionage probes to scout planet [{coordinates}]?\n\nPlease go to the fleet page to select ships and send.',
|
||||
attackPlanetMessage: 'Are you sure you want to attack planet [{coordinates}]?\n\nPlease go to the fleet page to select ships and send.',
|
||||
missileAttackMessage: 'Launch interplanetary missiles to attack planet [{coordinates}]',
|
||||
missileCount: 'Missile Count',
|
||||
availableMissiles: 'Available Missiles',
|
||||
missileRange: 'Missile Range',
|
||||
systems: 'systems',
|
||||
distance: 'Distance',
|
||||
flightTime: 'Flight Time',
|
||||
launchMissile: 'Launch',
|
||||
cancel: 'Cancel',
|
||||
colonizePlanetMessage:
|
||||
'Are you sure you want to colonize position [{coordinates}]?\n\nPlease go to the fleet page to send a colony ship.',
|
||||
recyclePlanetMessage:
|
||||
@@ -697,10 +720,24 @@ export default {
|
||||
gamePaused: 'Game paused',
|
||||
gameResumed: 'Game resumed',
|
||||
playerName: 'Player Name',
|
||||
gameSpeed: 'Game Speed',
|
||||
gameSpeedDesc: 'Current game speed multiplier',
|
||||
gameSpeed: 'Resource Production Speed',
|
||||
gameSpeedDesc: 'Current resource production speed multiplier',
|
||||
speedChanged: 'Resource production speed changed to {speed}x',
|
||||
speedReset: 'Resource production speed reset to 1x',
|
||||
reset: 'Reset',
|
||||
about: 'About',
|
||||
version: 'Version',
|
||||
latestVersion: 'Latest Version',
|
||||
checkUpdate: 'Check Update',
|
||||
checking: 'Checking...',
|
||||
newVersionAvailable: 'New version {version} available',
|
||||
upToDate: 'Already up to date',
|
||||
checkUpdateCooldown: 'Please try again later (5 minute cooldown)',
|
||||
checkUpdateFailed: 'Failed to check for updates, please check your network connection',
|
||||
viewUpdate: 'View Update',
|
||||
updateAvailable: 'A new version is available. Click to view release notes.',
|
||||
download: 'Download',
|
||||
goToDownload: 'Go to Download',
|
||||
buildDate: 'Build Date',
|
||||
community: 'Community',
|
||||
github: 'GitHub Repository',
|
||||
@@ -739,12 +776,22 @@ export default {
|
||||
testSpy: 'Test Spy',
|
||||
testAttack: 'Test Attack',
|
||||
testSpyAndAttack: 'Test Spy & Attack',
|
||||
testSpyMessage: 'Click confirm to accelerate the spy mission',
|
||||
testAttackMessage: 'Click confirm to accelerate the attack mission',
|
||||
testSpyAndAttackMessage: 'Click confirm to accelerate the missions',
|
||||
initializeFleet: 'Initialize NPC Fleet',
|
||||
accelerateMissions: 'Accelerate All Missions (5s)',
|
||||
selectNPCFirst: 'Please select an NPC first',
|
||||
npcNoProbes: 'NPC has no spy probes',
|
||||
npcNoSpyReport: 'NPC needs to spy first',
|
||||
npcMissionFailed: 'Failed to create mission',
|
||||
npcNoPlanets: 'NPC has no planets',
|
||||
npcWillSpyIn5s: '{npcName} will spy in 5 seconds',
|
||||
npcWillAttackIn5s: '{npcName} will attack in 5 seconds',
|
||||
npcWillSpyAndAttack: '{npcName} will spy in 5s and attack in 10s',
|
||||
acceleratedMissions: 'Accelerated {count} missions to 5 seconds',
|
||||
npcFleetInitialized: '{npcName} fleet initialized',
|
||||
npcFleetDetails: '100 Spy Probes\n500 Light Fighters\n300 Heavy Fighters\n200 Cruisers\n100 Battleships\n50 Bombers\n30 Destroyers\n20 Battlecruisers',
|
||||
dangerZone: 'Danger Zone',
|
||||
dangerZoneDesc: 'The following operations are irreversible',
|
||||
resetGame: 'Reset Game',
|
||||
@@ -796,9 +843,32 @@ export default {
|
||||
events: {
|
||||
gift: 'Sent Gift',
|
||||
attack: 'Attack',
|
||||
missileAttack: 'Missile Attack',
|
||||
allyAttacked: 'Ally Attacked',
|
||||
spy: 'Espionage',
|
||||
stealDebris: 'Debris Stolen'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: 'Gifted {metal}M {crystal}C {deuterium}D',
|
||||
receivedGiftFromPlayer: 'Received gift from player',
|
||||
giftedToNpc: 'You gifted resources to {npcName}. Reputation +{reputation}',
|
||||
rejectedPlayerGift: 'Rejected player\'s gift',
|
||||
npcRejectedGift: '{npcName} rejected your gift. Reputation {reputation}',
|
||||
attackedNpc: 'Attacked {npcName}',
|
||||
wasAttackedByPlayer: 'Was attacked by player',
|
||||
youAttackedNpc: 'You attacked {npcName}',
|
||||
playerAttackedAlly: 'Player attacked ally {allyName}',
|
||||
allyDispleased: '{allyName} is displeased that you attacked their ally {targetName}',
|
||||
wasSpiedByPlayer: 'Was spied by player (detected: {detected})',
|
||||
spyDetected: 'Your espionage was detected by {npcName}',
|
||||
stoleDebrisFromTerritory: 'Stole debris from {npcName}\'s territory',
|
||||
playerStoleDebris: 'Player stole debris from territory',
|
||||
recycledDebrisNearNpc: 'You recycled debris near {npcName}\'s planet. They are displeased.',
|
||||
giftedResourcesToPlayer: 'Gifted resources to player',
|
||||
receivedGiftFromNpc: 'Received gift from {npcName}',
|
||||
acceptedGiftFromNpc: 'You accepted a gift from {npcName}: {metal}M {crystal}C {deuterium}D',
|
||||
playerRejectedGift: 'Player rejected gift',
|
||||
rejectedGiftFromNpc: 'You rejected a gift from {npcName}. Reputation {reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -807,5 +877,10 @@ export default {
|
||||
first: 'First',
|
||||
last: 'Last',
|
||||
page: 'Page {page}'
|
||||
},
|
||||
notFound: {
|
||||
title: 'Page Not Found',
|
||||
description: 'Sorry, the page you are looking for does not exist',
|
||||
goHome: 'Go Home'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: '必要条件を表示',
|
||||
requirementsNotMet: '必要条件が満たされていません',
|
||||
current: '現在',
|
||||
level: 'レベル'
|
||||
level: 'レベル',
|
||||
gmModeActivated: 'GMモードが有効になりました!ナビゲーションメニューをご確認ください。'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: '前提条件を満たしていません',
|
||||
insufficientResources: '資源が不足しています',
|
||||
insufficientFleetStorage: '艦隊ストレージが不足しています',
|
||||
shieldDomeLimit: 'シールドドームの上限に達しました',
|
||||
missileSiloLimit: 'ミサイル格納庫の容量を超えています',
|
||||
insufficientMissiles: '惑星間ミサイルが不足しています',
|
||||
invalidMissileCount: 'ミサイル数が無効です',
|
||||
targetOutOfRange: 'ターゲットが射程外です',
|
||||
cannotAttackOwnPlanet: '自分の惑星を攻撃できません',
|
||||
fleetMissionsFull: '艦隊ミッションスロットが満杯です',
|
||||
insufficientFleet: '艦隊が不足しています',
|
||||
insufficientFuel: '燃料が不足しています',
|
||||
@@ -166,8 +172,8 @@ export default {
|
||||
darkMatterCollector: '希少なダークマター資源を収集',
|
||||
darkMatterTank: 'ダークマターの貯蔵上限を増加',
|
||||
missileSilo: 'ミサイルを保管・発射、レベル毎に10発',
|
||||
terraformer: '惑星地形を改造、レベル毎に利用可能スペース5増加',
|
||||
lunarBase: '月の利用可能スペースを増加、レベル毎に+5スペース',
|
||||
terraformer: '惑星地形を改造、レベル毎に利用可能スペース30増加',
|
||||
lunarBase: '月の利用可能スペースを増加、レベル毎に+30スペース',
|
||||
sensorPhalanx: '周辺星系の艦隊活動を探知',
|
||||
jumpGate: '他の月へ艦隊を瞬間移動',
|
||||
planetDestroyerFactory: '惑星を破壊できる究極兵器を建造'
|
||||
@@ -283,7 +289,7 @@ export default {
|
||||
impulseDrive: '中級推進技術',
|
||||
hyperspaceDrive: '高級推進技術',
|
||||
darkMatterTechnology: 'ダークマターの性質と応用を研究',
|
||||
terraformingTechnology: '惑星地形改造技術を研究、レベル毎に全惑星の利用可能スペース3増加',
|
||||
terraformingTechnology: '惑星地形改造技術を研究、レベル毎に全惑星の利用可能スペース30増加',
|
||||
planetDestructionTech: '惑星全体を破壊する恐怖の技術を研究'
|
||||
},
|
||||
officers: {
|
||||
@@ -316,10 +322,12 @@ export default {
|
||||
cancelResearch: '研究キャンセル',
|
||||
confirmCancel: 'キャンセルしますか?資源の50%が返還されます。',
|
||||
level: 'レベル',
|
||||
gmModeActivated: '',
|
||||
upgradeToLevel: 'レベルにアップグレード'
|
||||
},
|
||||
shipyard: {
|
||||
attack: '攻撃力',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
shield: 'シールド',
|
||||
armor: '装甲',
|
||||
speed: '速度',
|
||||
@@ -351,6 +359,7 @@ export default {
|
||||
usedSpace: '使用済みスペース',
|
||||
spaceUsage: 'スペース使用量',
|
||||
level: 'レベル',
|
||||
gmModeActivated: '',
|
||||
upgradeCost: 'アップグレードコスト',
|
||||
buildTime: '建設時間',
|
||||
upgrade: 'アップグレード',
|
||||
@@ -362,8 +371,8 @@ export default {
|
||||
demolishRefund: '解体返還',
|
||||
demolishFailed: '解体失敗',
|
||||
demolishFailedMessage: 'この建物を解体できません。建設キューが満杯か、建物レベルが0でないか確認してください。',
|
||||
confirmDemolish: '',
|
||||
confirmDemolishMessage: ''
|
||||
confirmDemolish: '解体確認',
|
||||
confirmDemolishMessage: '以下の建物を解体しますか?'
|
||||
},
|
||||
researchView: {
|
||||
title: '研究',
|
||||
@@ -375,6 +384,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: '攻撃力',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
shield: 'シールド',
|
||||
armor: '装甲',
|
||||
buildCost: '建設コスト',
|
||||
@@ -389,6 +399,7 @@ export default {
|
||||
title: '造船所',
|
||||
fleetStorage: '艦隊ストレージ',
|
||||
attack: '攻撃力',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
shield: 'シールド',
|
||||
speed: '速度',
|
||||
cargoCapacity: '積載量',
|
||||
@@ -404,6 +415,7 @@ export default {
|
||||
defenseView: {
|
||||
title: '防衛施設',
|
||||
attack: '攻撃力',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
shield: 'シールド',
|
||||
armor: '装甲',
|
||||
buildTime: '建設時間',
|
||||
@@ -413,6 +425,7 @@ export default {
|
||||
totalCost: '総コスト',
|
||||
build: '建造',
|
||||
shieldDomeBuilt: 'シールドドーム建設済み',
|
||||
missileCapacity: 'ミサイル容量',
|
||||
inputError: '入力エラー',
|
||||
inputErrorMessage: '建造数を入力してください!',
|
||||
buildFailed: '建造失敗',
|
||||
@@ -425,6 +438,7 @@ export default {
|
||||
flightMissions: '飛行ミッション',
|
||||
currentPlanetFleet: '現在の惑星艦隊',
|
||||
attack: '攻撃',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
shield: 'シールド',
|
||||
armor: '装甲',
|
||||
speed: '速度',
|
||||
@@ -519,25 +533,36 @@ export default {
|
||||
selectSystem: '星系を選択',
|
||||
view: '表示',
|
||||
myPlanet: '自分の惑星',
|
||||
myPlanets: '私の惑星',
|
||||
myPlanets: '自分の星系を表示',
|
||||
npcPlanets: 'NPCの惑星',
|
||||
selectPlanetToView: '表示する惑星を選択',
|
||||
selectPlanetToView: '惑星を選択して星系を表示',
|
||||
totalPositions: '全10惑星位置',
|
||||
mine: '自分',
|
||||
hostile: '敵対',
|
||||
emptySlot: '空き - 植民可能',
|
||||
scout: '偵察',
|
||||
attack: '攻撃',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
colonize: '植民',
|
||||
switch: '切り替え',
|
||||
recycle: '回収',
|
||||
debrisField: 'デブリフィールド',
|
||||
scoutPlanetTitle: '惑星偵察',
|
||||
attackPlanetTitle: '惑星攻撃',
|
||||
missileAttackTitle: 'ミサイル攻撃',
|
||||
colonizePlanetTitle: '惑星植民',
|
||||
recyclePlanetTitle: 'デブリ回収',
|
||||
scoutPlanetMessage: '惑星[{coordinates}]にスパイプローブを送りますか?\n\n艦隊ページに移動して艦船を選択して派遣してください。',
|
||||
attackPlanetMessage: '惑星[{coordinates}]を攻撃しますか?\n\n艦隊ページに移動して艦船を選択して派遣してください。',
|
||||
missileAttackMessage: '惑星[{coordinates}]に惑星間ミサイルを発射',
|
||||
missileCount: 'ミサイル数',
|
||||
availableMissiles: '利用可能なミサイル',
|
||||
missileRange: 'ミサイル射程',
|
||||
systems: 'システム',
|
||||
distance: '距離',
|
||||
flightTime: '飛行時間',
|
||||
launchMissile: '発射',
|
||||
cancel: 'キャンセル',
|
||||
colonizePlanetMessage: '位置[{coordinates}]を植民しますか?\n\n艦隊ページに移動してコロニーシップを派遣してください。',
|
||||
recyclePlanetMessage: '位置[{coordinates}]のデブリを回収しますか?\n\n艦隊ページに移動してリサイクラーを派遣してください。',
|
||||
sendGift: 'ギフト送信',
|
||||
@@ -690,10 +715,24 @@ export default {
|
||||
gamePaused: 'ゲームを一時停止しました',
|
||||
gameResumed: 'ゲームを再開しました',
|
||||
playerName: 'プレイヤー名',
|
||||
gameSpeed: 'ゲーム速度',
|
||||
gameSpeedDesc: '現在のゲーム速度倍率',
|
||||
gameSpeed: '資源生産速度',
|
||||
gameSpeedDesc: '現在の資源生産速度倍率',
|
||||
speedChanged: '資源生産速度を{speed}xに変更しました',
|
||||
speedReset: '資源生産速度を1xにリセットしました',
|
||||
reset: 'リセット',
|
||||
about: 'について',
|
||||
version: 'バージョン',
|
||||
latestVersion: '最新バージョン',
|
||||
checkUpdate: 'アップデート確認',
|
||||
checking: '確認中...',
|
||||
newVersionAvailable: '新バージョン{version}が利用可能です',
|
||||
upToDate: '最新バージョンです',
|
||||
checkUpdateCooldown: 'しばらくしてから再度お試しください(5分間のクールダウン)',
|
||||
checkUpdateFailed: 'アップデートの確認に失敗しました。ネットワーク接続を確認してください',
|
||||
viewUpdate: '更新を表示',
|
||||
updateAvailable: '新しいバージョンが利用可能です。クリックしてリリースノートを表示します。',
|
||||
download: 'ダウンロード',
|
||||
goToDownload: 'ダウンロードへ',
|
||||
buildDate: 'ビルド日',
|
||||
community: 'コミュニティ',
|
||||
github: 'GitHubリポジトリ',
|
||||
@@ -732,12 +771,22 @@ export default {
|
||||
testSpy: '偵察テスト',
|
||||
testAttack: '攻撃テスト',
|
||||
testSpyAndAttack: '偵察&攻撃テスト',
|
||||
testSpyMessage: '確認をクリックして偵察ミッションを加速',
|
||||
testAttackMessage: '確認をクリックして攻撃ミッションを加速',
|
||||
testSpyAndAttackMessage: '確認をクリックしてミッションを加速',
|
||||
initializeFleet: 'NPC艦隊を初期化',
|
||||
accelerateMissions: 'すべてのミッションを加速(5秒)',
|
||||
selectNPCFirst: '最初にNPCを選択してください',
|
||||
npcNoProbes: 'NPCには偵察プローブがありません',
|
||||
npcNoSpyReport: 'NPCは最初に偵察する必要があります',
|
||||
npcMissionFailed: 'ミッションの作成に失敗しました',
|
||||
npcNoPlanets: 'NPCに惑星がありません',
|
||||
npcWillSpyIn5s: '{npcName}は5秒後に偵察します',
|
||||
npcWillAttackIn5s: '{npcName}は5秒後に攻撃します',
|
||||
npcWillSpyAndAttack: '{npcName}は5秒後に偵察し、10秒後に攻撃します',
|
||||
acceleratedMissions: '{count}個のミッションを5秒後に加速しました',
|
||||
npcFleetInitialized: '{npcName}艦隊が初期化されました',
|
||||
npcFleetDetails: '100 偵察プローブ\n500 軽戦闘機\n300 重戦闘機\n200 巡洋艦\n100 戦艦\n50 爆撃機\n30 駆逐艦\n20 巡洋戦艦',
|
||||
dangerZone: '危険ゾーン',
|
||||
dangerZoneDesc: '以下の操作は元に戻せません',
|
||||
resetGame: 'ゲームをリセット',
|
||||
@@ -789,9 +838,32 @@ export default {
|
||||
events: {
|
||||
gift: 'ギフト送信',
|
||||
attack: '攻撃',
|
||||
missileAttack: 'ミサイル攻撃',
|
||||
allyAttacked: '同盟が攻撃された',
|
||||
spy: '諜報活動',
|
||||
stealDebris: '残骸を略奪'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: '{metal}M {crystal}C {deuterium}Dを贈呈',
|
||||
receivedGiftFromPlayer: 'プレイヤーからギフトを受け取りました',
|
||||
giftedToNpc: '{npcName}にリソースを贈呈しました。評判+{reputation}',
|
||||
rejectedPlayerGift: 'プレイヤーのギフトを拒否しました',
|
||||
npcRejectedGift: '{npcName}があなたのギフトを拒否しました。評判{reputation}',
|
||||
attackedNpc: '{npcName}を攻撃しました',
|
||||
wasAttackedByPlayer: 'プレイヤーに攻撃されました',
|
||||
youAttackedNpc: 'あなたは{npcName}を攻撃しました',
|
||||
playerAttackedAlly: 'プレイヤーが同盟{allyName}を攻撃しました',
|
||||
allyDispleased: '{allyName}はあなたが同盟{targetName}を攻撃したことに不満です',
|
||||
wasSpiedByPlayer: 'プレイヤーに偵察されました(発見:{detected})',
|
||||
spyDetected: 'あなたの偵察が{npcName}に発見されました',
|
||||
stoleDebrisFromTerritory: '{npcName}の領域から残骸を略奪しました',
|
||||
playerStoleDebris: 'プレイヤーが領域から残骸を略奪しました',
|
||||
recycledDebrisNearNpc: '{npcName}の惑星近くで残骸を回収しました。彼らは不満です。',
|
||||
giftedResourcesToPlayer: 'プレイヤーにリソースを贈呈しました',
|
||||
receivedGiftFromNpc: '{npcName}からギフトを受け取りました',
|
||||
acceptedGiftFromNpc: '{npcName}からのギフトを受け取りました:{metal}M {crystal}C {deuterium}D',
|
||||
playerRejectedGift: 'プレイヤーがギフトを拒否しました',
|
||||
rejectedGiftFromNpc: '{npcName}からのギフトを拒否しました。評判{reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -800,5 +872,10 @@ export default {
|
||||
first: '最初',
|
||||
last: '最後',
|
||||
page: '{page}ページ'
|
||||
},
|
||||
notFound: {
|
||||
title: 'ページが見つかりません',
|
||||
description: '申し訳ございません。お探しのページは存在しません',
|
||||
goHome: 'ホームに戻る'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: '요구사항 보기',
|
||||
requirementsNotMet: '요구사항 미충족',
|
||||
current: '현재',
|
||||
level: '레벨'
|
||||
level: '레벨',
|
||||
gmModeActivated: 'GM 모드가 활성화되었습니다! 탐색 메뉴를 확인하세요.'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: '전제 조건 미충족',
|
||||
insufficientResources: '자원 부족',
|
||||
insufficientFleetStorage: '함대 저장소 부족',
|
||||
shieldDomeLimit: '실드 돔 한도 도달',
|
||||
missileSiloLimit: '미사일 사일로 용량 초과',
|
||||
insufficientMissiles: '행성간 미사일 부족',
|
||||
invalidMissileCount: '잘못된 미사일 수량',
|
||||
targetOutOfRange: '목표가 사정거리 밖',
|
||||
cannotAttackOwnPlanet: '자신의 행성 공격 불가',
|
||||
fleetMissionsFull: '함대 임무 슬롯 가득 참',
|
||||
insufficientFleet: '함대 부족',
|
||||
insufficientFuel: '연료 부족',
|
||||
@@ -166,8 +172,8 @@ export default {
|
||||
darkMatterCollector: '희귀한 암흑 물질 자원 수집',
|
||||
darkMatterTank: '암흑 물질 저장 용량 증가',
|
||||
missileSilo: '미사일을 저장 및 발사, 레벨당 10발',
|
||||
terraformer: '행성 지형 개조, 레벨당 가용 공간 5 증가',
|
||||
lunarBase: '달 가용 공간 증가, 레벨당 +5 공간',
|
||||
terraformer: '행성 지형 개조, 레벨당 가용 공간 30 증가',
|
||||
lunarBase: '달 가용 공간 증가, 레벨당 +30 공간',
|
||||
sensorPhalanx: '주변 행성계의 함대 활동 감지',
|
||||
jumpGate: '다른 위성으로 함대 순간 이동',
|
||||
planetDestroyerFactory: '행성을 파괴할 수 있는 궁극 병기 건조'
|
||||
@@ -283,7 +289,7 @@ export default {
|
||||
impulseDrive: '중급 추진 기술',
|
||||
hyperspaceDrive: '고급 추진 기술',
|
||||
darkMatterTechnology: '암흑 물질의 성질과 응용 연구',
|
||||
terraformingTechnology: '행성 지형 개조 기술 연구, 레벨당 모든 행성의 가용 공간 3 증가',
|
||||
terraformingTechnology: '행성 지형 개조 기술 연구, 레벨당 모든 행성의 가용 공간 30 증가',
|
||||
planetDestructionTech: '행성 전체를 파괴하는 공포의 기술 연구'
|
||||
},
|
||||
officers: {
|
||||
@@ -316,6 +322,7 @@ export default {
|
||||
cancelResearch: '연구 취소',
|
||||
confirmCancel: '취소하시겠습니까? 자원의 50%가 환불됩니다.',
|
||||
level: '레벨',
|
||||
gmModeActivated: '',
|
||||
upgradeToLevel: '레벨로 업그레이드'
|
||||
},
|
||||
overview: {
|
||||
@@ -336,6 +343,7 @@ export default {
|
||||
usedSpace: '사용된 공간',
|
||||
spaceUsage: '공간 사용',
|
||||
level: '레벨',
|
||||
gmModeActivated: '',
|
||||
upgradeCost: '업그레이드 비용',
|
||||
buildTime: '건설 시간',
|
||||
upgrade: '업그레이드',
|
||||
@@ -347,8 +355,8 @@ export default {
|
||||
demolishRefund: '철거 환불',
|
||||
demolishFailed: '철거 실패',
|
||||
demolishFailedMessage: '이 건물을 철거할 수 없습니다. 건설 대기열이 가득 찼거나 건물 레벨이 0인지 확인하세요.',
|
||||
confirmDemolish: '',
|
||||
confirmDemolishMessage: ''
|
||||
confirmDemolish: '철거 확인',
|
||||
confirmDemolishMessage: '다음 건물을 철거하시겠습니까?'
|
||||
},
|
||||
researchView: {
|
||||
title: '연구',
|
||||
@@ -360,6 +368,7 @@ export default {
|
||||
},
|
||||
shipyard: {
|
||||
attack: '공격력',
|
||||
missileAttack: '미사일 공격',
|
||||
shield: '쉴드',
|
||||
armor: '장갑',
|
||||
speed: '속도',
|
||||
@@ -377,6 +386,7 @@ export default {
|
||||
title: '조선소',
|
||||
fleetStorage: '함대 저장소',
|
||||
attack: '공격력',
|
||||
missileAttack: '미사일 공격',
|
||||
shield: '실드',
|
||||
speed: '속도',
|
||||
cargoCapacity: '적재량',
|
||||
@@ -391,6 +401,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: '공격력',
|
||||
missileAttack: '미사일 공격',
|
||||
shield: '쉴드',
|
||||
armor: '장갑',
|
||||
buildCost: '건설 비용',
|
||||
@@ -404,6 +415,7 @@ export default {
|
||||
defenseView: {
|
||||
title: '방어 시설',
|
||||
attack: '공격력',
|
||||
missileAttack: '미사일 공격',
|
||||
shield: '실드',
|
||||
armor: '장갑',
|
||||
buildTime: '건설 시간',
|
||||
@@ -413,6 +425,7 @@ export default {
|
||||
totalCost: '총 비용',
|
||||
build: '건조',
|
||||
shieldDomeBuilt: '실드 돔이 이미 건설됨',
|
||||
missileCapacity: '미사일 용량',
|
||||
inputError: '입력 오류',
|
||||
inputErrorMessage: '건조 수량을 입력하세요!',
|
||||
buildFailed: '건조 실패',
|
||||
@@ -425,6 +438,7 @@ export default {
|
||||
flightMissions: '비행 임무',
|
||||
currentPlanetFleet: '현재 행성 함대',
|
||||
attack: '공격',
|
||||
missileAttack: '미사일 공격',
|
||||
shield: '실드',
|
||||
armor: '장갑',
|
||||
speed: '속도',
|
||||
@@ -519,26 +533,37 @@ export default {
|
||||
selectSystem: '행성계 선택',
|
||||
view: '보기',
|
||||
myPlanet: '내 행성',
|
||||
myPlanets: '내 행성들',
|
||||
myPlanets: '내 행성계 보기',
|
||||
npcPlanets: 'NPC 행성들',
|
||||
selectPlanetToView: '볼 행성 선택',
|
||||
selectPlanetToView: '행성을 선택하여 행성계 보기',
|
||||
totalPositions: '총 10개 행성 위치',
|
||||
mine: '내 것',
|
||||
hostile: '적대',
|
||||
emptySlot: '빈 자리 - 식민 가능',
|
||||
scout: '정찰',
|
||||
attack: '공격',
|
||||
missileAttack: '미사일 공격',
|
||||
colonize: '식민',
|
||||
switch: '전환',
|
||||
recycle: '회수',
|
||||
debrisField: '잔해 필드',
|
||||
scoutPlanetTitle: '행성 정찰',
|
||||
attackPlanetTitle: '행성 공격',
|
||||
missileAttackTitle: '미사일 공격',
|
||||
colonizePlanetTitle: '행성 식민',
|
||||
recyclePlanetTitle: '잔해 회수',
|
||||
scoutPlanetMessage:
|
||||
'행성 [{coordinates}]을(를) 정찰하기 위해 정찰기를 보내시겠습니까?\n\n함대 페이지로 이동하여 함선을 선택하고 파견하세요.',
|
||||
attackPlanetMessage: '행성 [{coordinates}]을(를) 공격하시겠습니까?\n\n함대 페이지로 이동하여 함선을 선택하고 파견하세요.',
|
||||
missileAttackMessage: '행성 [{coordinates}]에 행성간 미사일 발사',
|
||||
missileCount: '미사일 수량',
|
||||
availableMissiles: '사용 가능한 미사일',
|
||||
missileRange: '미사일 사정거리',
|
||||
systems: '시스템',
|
||||
distance: '거리',
|
||||
flightTime: '비행 시간',
|
||||
launchMissile: '발사',
|
||||
cancel: '취소',
|
||||
colonizePlanetMessage: '위치 [{coordinates}]을(를) 식민하시겠습니까?\n\n함대 페이지로 이동하여 식민선을 파견하세요.',
|
||||
recyclePlanetMessage: '위치 [{coordinates}]의 잔해를 회수하시겠습니까?\n\n함대 페이지로 이동하여 회수선을 파견하세요.',
|
||||
sendGift: '선물 보내기',
|
||||
@@ -691,10 +716,24 @@ export default {
|
||||
gamePaused: '게임이 일시정지되었습니다',
|
||||
gameResumed: '게임이 재개되었습니다',
|
||||
playerName: '플레이어 이름',
|
||||
gameSpeed: '게임 속도',
|
||||
gameSpeedDesc: '현재 게임 속도 배율',
|
||||
gameSpeed: '자원 생산 속도',
|
||||
gameSpeedDesc: '현재 자원 생산 속도 배율',
|
||||
speedChanged: '자원 생산 속도가 {speed}x로 변경되었습니다',
|
||||
speedReset: '자원 생산 속도가 1x로 재설정되었습니다',
|
||||
reset: '재설정',
|
||||
about: '정보',
|
||||
version: '버전',
|
||||
latestVersion: '최신 버전',
|
||||
checkUpdate: '업데이트 확인',
|
||||
checking: '확인 중...',
|
||||
newVersionAvailable: '새 버전 {version} 사용 가능',
|
||||
upToDate: '이미 최신 버전입니다',
|
||||
checkUpdateCooldown: '나중에 다시 시도해주세요 (5분 쿨다운)',
|
||||
checkUpdateFailed: '업데이트 확인 실패, 네트워크 연결을 확인하세요',
|
||||
viewUpdate: '업데이트 보기',
|
||||
updateAvailable: '새 버전이 사용 가능합니다. 릴리스 노트를 보려면 클릭하세요.',
|
||||
download: '다운로드',
|
||||
goToDownload: '다운로드로 이동',
|
||||
buildDate: '빌드 날짜',
|
||||
community: '커뮤니티',
|
||||
github: 'GitHub 저장소',
|
||||
@@ -733,12 +772,22 @@ export default {
|
||||
testSpy: '정찰 테스트',
|
||||
testAttack: '공격 테스트',
|
||||
testSpyAndAttack: '정찰 & 공격 테스트',
|
||||
initializeFleet: 'NPC 함대 초기화',
|
||||
testSpyMessage: '확인을 클릭하여 정찰 임무를 가속화',
|
||||
testAttackMessage: '확인을 클릭하여 공격 임무를 가속화',
|
||||
testSpyAndAttackMessage: '확인을 클릭하여 임무를 가속화',
|
||||
initializeFleet: 'NPC 함대 초기化',
|
||||
accelerateMissions: '모든 임무 가속(5초)',
|
||||
selectNPCFirst: '먼저 NPC를 선택하세요',
|
||||
npcNoProbes: 'NPC에 정찰 프로브가 없습니다',
|
||||
npcNoSpyReport: 'NPC가 먼저 정찰해야 합니다',
|
||||
npcMissionFailed: '임무 생성 실패',
|
||||
npcNoPlanets: 'NPC에 행성이 없습니다',
|
||||
npcWillSpyIn5s: '{npcName}이(가) 5초 후에 정찰합니다',
|
||||
npcWillAttackIn5s: '{npcName}이(가) 5초 후에 공격합니다',
|
||||
npcWillSpyAndAttack: '{npcName}이(가) 5초 후에 정찰하고 10초 후에 공격합니다',
|
||||
acceleratedMissions: '{count}개의 임무를 5초로 가속화했습니다',
|
||||
npcFleetInitialized: '{npcName} 함대가 초기화되었습니다',
|
||||
npcFleetDetails: '100 정찰 프로브\n500 경전투기\n300 중전투기\n200 순양함\n100 전함\n50 폭격기\n30 구축함\n20 순양전함',
|
||||
dangerZone: '위험 구역',
|
||||
dangerZoneDesc: '다음 작업은 되돌릴 수 없습니다',
|
||||
resetGame: '게임 초기화',
|
||||
@@ -790,9 +839,32 @@ export default {
|
||||
events: {
|
||||
gift: '선물 전송',
|
||||
attack: '공격',
|
||||
missileAttack: '미사일 공격',
|
||||
allyAttacked: '동맹 공격당함',
|
||||
spy: '정찰',
|
||||
stealDebris: '잔해 약탈'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: '{metal}M {crystal}C {deuterium}D 선물함',
|
||||
receivedGiftFromPlayer: '플레이어로부터 선물을 받았습니다',
|
||||
giftedToNpc: '{npcName}에게 자원을 선물했습니다. 평판 +{reputation}',
|
||||
rejectedPlayerGift: '플레이어의 선물을 거부했습니다',
|
||||
npcRejectedGift: '{npcName}이(가) 당신의 선물을 거부했습니다. 평판 {reputation}',
|
||||
attackedNpc: '{npcName}을(를) 공격했습니다',
|
||||
wasAttackedByPlayer: '플레이어에게 공격당했습니다',
|
||||
youAttackedNpc: '당신은 {npcName}을(를) 공격했습니다',
|
||||
playerAttackedAlly: '플레이어가 동맹 {allyName}을(를) 공격했습니다',
|
||||
allyDispleased: '{allyName}은(는) 당신이 동맹 {targetName}을(를) 공격한 것에 불만입니다',
|
||||
wasSpiedByPlayer: '플레이어에게 정찰당했습니다 (발견: {detected})',
|
||||
spyDetected: '당신의 정찰이 {npcName}에게 발견되었습니다',
|
||||
stoleDebrisFromTerritory: '{npcName}의 영역에서 잔해를 약탈했습니다',
|
||||
playerStoleDebris: '플레이어가 영역에서 잔해를 약탈했습니다',
|
||||
recycledDebrisNearNpc: '{npcName}의 행성 근처에서 잔해를 수집했습니다. 그들은 불만족스러워합니다.',
|
||||
giftedResourcesToPlayer: '플레이어에게 자원을 선물했습니다',
|
||||
receivedGiftFromNpc: '{npcName}로부터 선물을 받았습니다',
|
||||
acceptedGiftFromNpc: '{npcName}의 선물을 받았습니다: {metal}M {crystal}C {deuterium}D',
|
||||
playerRejectedGift: '플레이어가 선물을 거부했습니다',
|
||||
rejectedGiftFromNpc: '{npcName}의 선물을 거부했습니다. 평판 {reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -801,5 +873,10 @@ export default {
|
||||
first: '처음',
|
||||
last: '마지막',
|
||||
page: '{page}페이지'
|
||||
},
|
||||
notFound: {
|
||||
title: '페이지를 찾을 수 없습니다',
|
||||
description: '죄송합니다. 찾으시는 페이지가 존재하지 않습니다',
|
||||
goHome: '홈으로 이동'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: 'Просмотр требований',
|
||||
requirementsNotMet: 'Требования не выполнены',
|
||||
current: 'Текущий',
|
||||
level: 'Уровень'
|
||||
level: 'Уровень',
|
||||
gmModeActivated: 'Режим GM активирован! Проверьте навигационное меню.'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: 'Требования не выполнены',
|
||||
insufficientResources: 'Недостаточно ресурсов',
|
||||
insufficientFleetStorage: 'Недостаточно места для флота',
|
||||
shieldDomeLimit: 'Достигнут лимит щитовых куполов',
|
||||
missileSiloLimit: 'Превышена вместимость ракетной шахты',
|
||||
insufficientMissiles: 'Недостаточно межпланетных ракет',
|
||||
invalidMissileCount: 'Неверное количество ракет',
|
||||
targetOutOfRange: 'Цель вне дальности',
|
||||
cannotAttackOwnPlanet: 'Нельзя атаковать свою планету',
|
||||
fleetMissionsFull: 'Слоты миссий флота заполнены',
|
||||
insufficientFleet: 'Недостаточно флота',
|
||||
insufficientFuel: 'Недостаточно топлива',
|
||||
@@ -166,8 +172,8 @@ export default {
|
||||
darkMatterCollector: 'Собирает редкие ресурсы тёмной материи',
|
||||
darkMatterTank: 'Увеличивает ёмкость хранилища тёмной материи',
|
||||
missileSilo: 'Хранит и запускает ракеты, 10 ракет на уровень',
|
||||
terraformer: 'Терраформирует поверхность планеты, увеличивает доступное пространство на 5 за уровень',
|
||||
lunarBase: 'Увеличивает доступное пространство на луне, +5 пространства за уровень',
|
||||
terraformer: 'Терраформирует поверхность планеты, увеличивает доступное пространство на 30 за уровень',
|
||||
lunarBase: 'Увеличивает доступное пространство на луне, +30 пространства за уровень',
|
||||
sensorPhalanx: 'Обнаруживает активность флота в окружающих системах',
|
||||
jumpGate: 'Мгновенно переносит флоты на другие луны',
|
||||
planetDestroyerFactory: 'Производит абсолютное оружие, способное уничтожать планеты'
|
||||
@@ -284,7 +290,7 @@ export default {
|
||||
hyperspaceDrive: 'Продвинутая технология двигателей',
|
||||
darkMatterTechnology: 'Исследование свойств и применения тёмной материи',
|
||||
terraformingTechnology:
|
||||
'Исследование технологии терраформирования планет, увеличивает доступное пространство всех планет на 3 за уровень',
|
||||
'Исследование технологии терраформирования планет, увеличивает доступное пространство всех планет на 30 за уровень',
|
||||
planetDestructionTech: 'Исследование ужасающей технологии уничтожения целых планет'
|
||||
},
|
||||
officers: {
|
||||
@@ -317,6 +323,7 @@ export default {
|
||||
cancelResearch: 'Отменить исследование',
|
||||
confirmCancel: 'Вы уверены, что хотите отменить? 50% ресурсов будет возвращено.',
|
||||
level: 'Уровень',
|
||||
gmModeActivated: '',
|
||||
upgradeToLevel: 'Улучшить до уровня'
|
||||
},
|
||||
overview: {
|
||||
@@ -337,6 +344,7 @@ export default {
|
||||
usedSpace: 'Использовано полей',
|
||||
spaceUsage: 'Использование полей',
|
||||
level: 'Уровень',
|
||||
gmModeActivated: '',
|
||||
upgradeCost: 'Стоимость улучшения',
|
||||
buildTime: 'Время строительства',
|
||||
upgrade: 'Улучшить',
|
||||
@@ -348,8 +356,8 @@ export default {
|
||||
demolishRefund: 'Возврат от сноса',
|
||||
demolishFailed: 'Снос не удался',
|
||||
demolishFailedMessage: 'Невозможно снести это здание. Проверьте, не заполнена ли очередь строительства или уровень здания не равен 0.',
|
||||
confirmDemolish: '',
|
||||
confirmDemolishMessage: ''
|
||||
confirmDemolish: 'Подтвердить снос',
|
||||
confirmDemolishMessage: 'Вы уверены, что хотите снести следующее здание?'
|
||||
},
|
||||
researchView: {
|
||||
title: 'Исследования',
|
||||
@@ -362,6 +370,7 @@ export default {
|
||||
},
|
||||
shipyard: {
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
shield: 'Щит',
|
||||
armor: 'Броня',
|
||||
speed: 'Скорость',
|
||||
@@ -379,6 +388,7 @@ export default {
|
||||
title: 'Верфь',
|
||||
fleetStorage: 'Хранилище флота',
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
shield: 'Щит',
|
||||
speed: 'Скорость',
|
||||
cargoCapacity: 'Грузоподъёмность',
|
||||
@@ -393,6 +403,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
shield: 'Щит',
|
||||
armor: 'Броня',
|
||||
buildCost: 'Стоимость постройки',
|
||||
@@ -406,6 +417,7 @@ export default {
|
||||
defenseView: {
|
||||
title: 'Оборона',
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
shield: 'Щит',
|
||||
armor: 'Броня',
|
||||
buildTime: 'Время постройки',
|
||||
@@ -415,6 +427,7 @@ export default {
|
||||
totalCost: 'Общая стоимость',
|
||||
build: 'Построить',
|
||||
shieldDomeBuilt: 'Щитовой купол уже построен',
|
||||
missileCapacity: 'Вместимость ракет',
|
||||
inputError: 'Ошибка ввода',
|
||||
inputErrorMessage: 'Пожалуйста, введите количество для постройки!',
|
||||
buildFailed: 'Постройка не удалась',
|
||||
@@ -428,6 +441,7 @@ export default {
|
||||
flightMissions: 'Полетные миссии',
|
||||
currentPlanetFleet: 'Флот на этой планете',
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
shield: 'Щит',
|
||||
armor: 'Броня',
|
||||
speed: 'Скорость',
|
||||
@@ -522,27 +536,38 @@ export default {
|
||||
selectSystem: 'Выбрать систему',
|
||||
view: 'Показать',
|
||||
myPlanet: 'Моя планета',
|
||||
myPlanets: 'Мои планеты',
|
||||
myPlanets: 'Просмотр моих систем',
|
||||
npcPlanets: 'Планеты NPC',
|
||||
selectPlanetToView: 'Выберите планету для просмотра',
|
||||
selectPlanetToView: 'Выберите планету для просмотра её системы',
|
||||
totalPositions: 'Всего 10 позиций планет',
|
||||
mine: 'Моя',
|
||||
hostile: 'Враждебная',
|
||||
emptySlot: 'Пусто - можно колонизировать',
|
||||
scout: 'Разведка',
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
colonize: 'Колонизация',
|
||||
switch: 'Переключить',
|
||||
recycle: 'Переработка',
|
||||
debrisField: 'Поле обломков',
|
||||
scoutPlanetTitle: 'Разведать планету',
|
||||
attackPlanetTitle: 'Атаковать планету',
|
||||
missileAttackTitle: 'Ракетная атака',
|
||||
colonizePlanetTitle: 'Колонизировать планету',
|
||||
recyclePlanetTitle: 'Переработать обломки',
|
||||
scoutPlanetMessage:
|
||||
'Вы уверены, что хотите отправить шпионские зонды для разведки планеты [{coordinates}]?\n\nПерейдите на страницу флота, чтобы выбрать корабли и отправить.',
|
||||
attackPlanetMessage:
|
||||
'Вы уверены, что хотите атаковать планету [{coordinates}]?\n\nПерейдите на страницу флота, чтобы выбрать корабли и отправить.',
|
||||
missileAttackMessage: 'Запустить межпланетные ракеты по планете [{coordinates}]',
|
||||
missileCount: 'Количество ракет',
|
||||
availableMissiles: 'Доступно ракет',
|
||||
missileRange: 'Дальность ракет',
|
||||
systems: 'систем',
|
||||
distance: 'Расстояние',
|
||||
flightTime: 'Время полета',
|
||||
launchMissile: 'Запустить',
|
||||
cancel: 'Отмена',
|
||||
colonizePlanetMessage:
|
||||
'Вы уверены, что хотите колонизировать позицию [{coordinates}]?\n\nПерейдите на страницу флота, чтобы отправить колонизационный корабль.',
|
||||
recyclePlanetMessage:
|
||||
@@ -698,10 +723,24 @@ export default {
|
||||
gamePaused: 'Игра приостановлена',
|
||||
gameResumed: 'Игра возобновлена',
|
||||
playerName: 'Имя игрока',
|
||||
gameSpeed: 'Скорость игры',
|
||||
gameSpeedDesc: 'Текущий множитель скорости игры',
|
||||
gameSpeed: 'Скорость производства ресурсов',
|
||||
gameSpeedDesc: 'Текущий множитель скорости производства ресурсов',
|
||||
speedChanged: 'Скорость производства ресурсов изменена на {speed}x',
|
||||
speedReset: 'Скорость производства ресурсов сброшена на 1x',
|
||||
reset: 'Сбросить',
|
||||
about: 'О программе',
|
||||
version: 'Версия',
|
||||
latestVersion: 'Последняя версия',
|
||||
checkUpdate: 'Проверить обновление',
|
||||
checking: 'Проверка...',
|
||||
newVersionAvailable: 'Доступна новая версия {version}',
|
||||
upToDate: 'Уже актуальная версия',
|
||||
checkUpdateCooldown: 'Пожалуйста, попробуйте позже (5 минут перезарядки)',
|
||||
checkUpdateFailed: 'Не удалось проверить обновления, проверьте подключение к Интернету',
|
||||
viewUpdate: 'Просмотреть обновление',
|
||||
updateAvailable: 'Доступна новая версия. Нажмите, чтобы просмотреть примечания к выпуску.',
|
||||
download: 'Скачать',
|
||||
goToDownload: 'Перейти к загрузке',
|
||||
buildDate: 'Дата сборки',
|
||||
community: 'Сообщество',
|
||||
github: 'Репозиторий GitHub',
|
||||
@@ -740,12 +779,22 @@ export default {
|
||||
testSpy: 'Тест разведки',
|
||||
testAttack: 'Тест атаки',
|
||||
testSpyAndAttack: 'Тест разведки и атаки',
|
||||
testSpyMessage: 'Нажмите подтвердить, чтобы ускорить миссию разведки',
|
||||
testAttackMessage: 'Нажмите подтвердить, чтобы ускорить миссию атаки',
|
||||
testSpyAndAttackMessage: 'Нажмите подтвердить, чтобы ускорить миссии',
|
||||
initializeFleet: 'Инициализировать флот NPC',
|
||||
accelerateMissions: 'Ускорить все миссии (5с)',
|
||||
selectNPCFirst: 'Сначала выберите NPC',
|
||||
npcNoProbes: 'У NPC нет шпионских зондов',
|
||||
npcNoSpyReport: 'NPC нужно сначала разведать',
|
||||
npcMissionFailed: 'Не удалось создать миссию',
|
||||
npcNoPlanets: 'У NPC нет планет',
|
||||
npcWillSpyIn5s: '{npcName} проведет разведку через 5 секунд',
|
||||
npcWillAttackIn5s: '{npcName} атакует через 5 секунд',
|
||||
npcWillSpyAndAttack: '{npcName} проведет разведку через 5с и атакует через 10с',
|
||||
acceleratedMissions: 'Ускорено {count} миссий до 5 секунд',
|
||||
npcFleetInitialized: 'Флот {npcName} инициализирован',
|
||||
npcFleetDetails: '100 шпионских зондов\n500 легких истребителей\n300 тяжелых истребителей\n200 крейсеров\n100 линкоров\n50 бомбардировщиков\n30 эсминцев\n20 линейных крейсеров',
|
||||
dangerZone: 'Опасная зона',
|
||||
dangerZoneDesc: 'Следующие операции необратимы',
|
||||
resetGame: 'Сбросить игру',
|
||||
@@ -797,9 +846,32 @@ export default {
|
||||
events: {
|
||||
gift: 'Подарок отправлен',
|
||||
attack: 'Атака',
|
||||
missileAttack: 'Ракетная атака',
|
||||
allyAttacked: 'Союзник атакован',
|
||||
spy: 'Шпионаж',
|
||||
stealDebris: 'Обломки украдены'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: 'Подарено {metal}M {crystal}C {deuterium}D',
|
||||
receivedGiftFromPlayer: 'Получен подарок от игрока',
|
||||
giftedToNpc: 'Вы подарили ресурсы {npcName}. Репутация +{reputation}',
|
||||
rejectedPlayerGift: 'Отклонен подарок игрока',
|
||||
npcRejectedGift: '{npcName} отклонил ваш подарок. Репутация {reputation}',
|
||||
attackedNpc: 'Атакован {npcName}',
|
||||
wasAttackedByPlayer: 'Был атакован игроком',
|
||||
youAttackedNpc: 'Вы атаковали {npcName}',
|
||||
playerAttackedAlly: 'Игрок атаковал союзника {allyName}',
|
||||
allyDispleased: '{allyName} недоволен тем, что вы атаковали их союзника {targetName}',
|
||||
wasSpiedByPlayer: 'Был разведан игроком (обнаружен: {detected})',
|
||||
spyDetected: 'Ваш шпионаж был обнаружен {npcName}',
|
||||
stoleDebrisFromTerritory: 'Украдены обломки с территории {npcName}',
|
||||
playerStoleDebris: 'Игрок украл обломки с территории',
|
||||
recycledDebrisNearNpc: 'Вы переработали обломки возле планеты {npcName}. Они недовольны.',
|
||||
giftedResourcesToPlayer: 'Подарены ресурсы игроку',
|
||||
receivedGiftFromNpc: 'Получен подарок от {npcName}',
|
||||
acceptedGiftFromNpc: 'Вы приняли подарок от {npcName}: {metal}M {crystal}C {deuterium}D',
|
||||
playerRejectedGift: 'Игрок отклонил подарок',
|
||||
rejectedGiftFromNpc: 'Вы отклонили подарок от {npcName}. Репутация {reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -808,5 +880,10 @@ export default {
|
||||
first: 'Первая',
|
||||
last: 'Последняя',
|
||||
page: 'Страница {page}'
|
||||
},
|
||||
notFound: {
|
||||
title: 'Страница не найдена',
|
||||
description: 'Извините, страница, которую вы ищете, не существует',
|
||||
goHome: 'На главную'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,20 @@ export default {
|
||||
viewRequirements: '查看前置条件',
|
||||
requirementsNotMet: '前置条件未满足',
|
||||
current: '当前',
|
||||
level: '等级'
|
||||
level: '等级',
|
||||
gmModeActivated: 'GM 模式已激活!请查看导航菜单。'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: '不满足前置条件',
|
||||
insufficientResources: '资源不足',
|
||||
insufficientFleetStorage: '舰队仓储空间不足',
|
||||
shieldDomeLimit: '护盾罩数量限制',
|
||||
missileSiloLimit: '导弹发射井容量不足',
|
||||
insufficientMissiles: '星际导弹数量不足',
|
||||
invalidMissileCount: '导弹数量无效',
|
||||
targetOutOfRange: '目标超出射程',
|
||||
cannotAttackOwnPlanet: '不能攻击自己的星球',
|
||||
launchFailed: '发射失败',
|
||||
fleetMissionsFull: '舰队任务槽位已满',
|
||||
insufficientFleet: '舰队数量不足',
|
||||
insufficientFuel: '燃料不足',
|
||||
@@ -164,8 +171,8 @@ export default {
|
||||
darkMatterCollector: '收集稀有的暗物质资源',
|
||||
darkMatterTank: '增加暗物质存储上限',
|
||||
missileSilo: '存储和发射导弹,每级可存储10枚导弹',
|
||||
terraformer: '改造行星地形,每级增加5个可用空间',
|
||||
lunarBase: '增加月球可用空间,每级+5空间',
|
||||
terraformer: '改造行星地形,每级增加30个可用空间',
|
||||
lunarBase: '增加月球可用空间,每级+30空间',
|
||||
sensorPhalanx: '侦测周围星系的舰队活动',
|
||||
jumpGate: '瞬间传送舰队到其他月球',
|
||||
planetDestroyerFactory: '建造能够摧毁行星的终极武器'
|
||||
@@ -283,7 +290,7 @@ export default {
|
||||
impulseDrive: '中级推进技术',
|
||||
hyperspaceDrive: '高级推进技术',
|
||||
darkMatterTechnology: '研究暗物质的性质和应用',
|
||||
terraformingTechnology: '研究行星地形改造技术,每级为所有行星增加3个可用空间',
|
||||
terraformingTechnology: '研究行星地形改造技术,每级为所有行星增加30个可用空间',
|
||||
planetDestructionTech: '研究如何摧毁整个行星的恐怖技术'
|
||||
},
|
||||
officers: {
|
||||
@@ -415,6 +422,7 @@ export default {
|
||||
totalCost: '总成本',
|
||||
build: '建造',
|
||||
shieldDomeBuilt: '护盾罩已建造',
|
||||
missileCapacity: '导弹容量',
|
||||
inputError: '输入错误',
|
||||
inputErrorMessage: '请输入建造数量!',
|
||||
buildFailed: '建造失败',
|
||||
@@ -520,15 +528,16 @@ export default {
|
||||
selectSystem: '选择星系',
|
||||
view: '查看',
|
||||
myPlanet: '我的星球',
|
||||
myPlanets: '我的星球',
|
||||
myPlanets: '查看我的星系',
|
||||
npcPlanets: 'NPC星球',
|
||||
selectPlanetToView: '选择要查看的星球',
|
||||
selectPlanetToView: '选择星球以查看其所在星系',
|
||||
totalPositions: '共10个星球位置',
|
||||
mine: '我的',
|
||||
hostile: '敌对',
|
||||
emptySlot: '空位 - 可殖民',
|
||||
scout: '侦察',
|
||||
attack: '攻击',
|
||||
missileAttack: '导弹攻击',
|
||||
colonize: '殖民',
|
||||
switch: '切换',
|
||||
recycle: '回收',
|
||||
@@ -537,11 +546,22 @@ export default {
|
||||
debrisField: '残骸场',
|
||||
scoutPlanetTitle: '侦察星球',
|
||||
attackPlanetTitle: '攻击星球',
|
||||
missileAttackTitle: '导弹攻击',
|
||||
colonizePlanetTitle: '殖民星球',
|
||||
recyclePlanetTitle: '回收残骸',
|
||||
giftPlanetTitle: '赠送礼物',
|
||||
scoutPlanetMessage: '确定要派遣间谍探测器侦察星球 [{coordinates}] 吗?\n\n请前往舰队页面选择舰船并派遣。',
|
||||
attackPlanetMessage: '确定要攻击星球 [{coordinates}] 吗?\n\n请前往舰队页面选择舰船并派遣。',
|
||||
missileAttackMessage: '向星球 [{coordinates}] 发射导弹',
|
||||
missileCount: '导弹数量',
|
||||
availableMissiles: '可用导弹',
|
||||
missileRange: '射程',
|
||||
systems: '系统',
|
||||
distance: '距离',
|
||||
flightTime: '飞行时间',
|
||||
launchMissile: '发射',
|
||||
missileLaunched: '导弹已发射',
|
||||
cancel: '取消',
|
||||
colonizePlanetMessage: '确定要殖民位置 [{coordinates}] 吗?\n\n请前往舰队页面派遣殖民船。',
|
||||
recyclePlanetMessage: '确定要回收位置 [{coordinates}] 的残骸吗?\n\n请前往舰队页面派遣回收船。',
|
||||
giftPlanetMessage: '确定要向星球 [{coordinates}] 赠送资源吗?\n\n请前往舰队页面选择运输船并装载资源。'
|
||||
@@ -622,7 +642,11 @@ export default {
|
||||
recycleSuccess: '回收任务成功完成',
|
||||
recycleFailed: '回收任务失败,目标位置没有残骸',
|
||||
destroySuccess: '行星毁灭任务成功执行',
|
||||
destroyFailed: '行星毁灭任务失败'
|
||||
destroyFailed: '行星毁灭任务失败',
|
||||
missileAttackSuccess: '导弹攻击成功',
|
||||
missileAttackFailed: '导弹攻击失败,目标星球不存在',
|
||||
missileAttackIntercepted: '所有导弹被拦截',
|
||||
hits: '枚命中'
|
||||
},
|
||||
simulatorView: {
|
||||
title: '战斗模拟器',
|
||||
@@ -691,10 +715,24 @@ export default {
|
||||
gamePaused: '游戏已暂停',
|
||||
gameResumed: '游戏已恢复',
|
||||
playerName: '玩家名称',
|
||||
gameSpeed: '游戏速度',
|
||||
gameSpeedDesc: '当前游戏速度倍率',
|
||||
gameSpeed: '资源产出速度',
|
||||
gameSpeedDesc: '当前资源产出速度倍率',
|
||||
speedChanged: '资源产出速度已更改为 {speed}x',
|
||||
speedReset: '资源产出速度已重置为 1x',
|
||||
reset: '重置',
|
||||
about: '关于',
|
||||
version: '版本',
|
||||
latestVersion: '最新版本',
|
||||
checkUpdate: '检查更新',
|
||||
checking: '检查中...',
|
||||
newVersionAvailable: '发现新版本 {version}',
|
||||
upToDate: '已是最新版本',
|
||||
checkUpdateCooldown: '请稍后再试(5分钟冷却时间)',
|
||||
checkUpdateFailed: '检查更新失败,请检查网络连接',
|
||||
viewUpdate: '查看更新',
|
||||
updateAvailable: '有新版本可用。点击查看更新内容。',
|
||||
download: '下载',
|
||||
goToDownload: '前往下载',
|
||||
buildDate: '构建日期',
|
||||
community: '社区',
|
||||
github: 'GitHub 仓库',
|
||||
@@ -733,12 +771,22 @@ export default {
|
||||
testSpy: '测试侦查',
|
||||
testAttack: '测试攻击',
|
||||
testSpyAndAttack: '测试侦查&攻击',
|
||||
testSpyMessage: '点击确认以加速侦查任务',
|
||||
testAttackMessage: '点击确认以加速攻击任务',
|
||||
testSpyAndAttackMessage: '点击确认以加速任务执行',
|
||||
initializeFleet: '初始化NPC舰队',
|
||||
accelerateMissions: '加速所有任务(5秒)',
|
||||
selectNPCFirst: '请先选择一个NPC',
|
||||
npcNoProbes: 'NPC没有间谍探测器',
|
||||
npcNoSpyReport: 'NPC需要先侦查',
|
||||
npcMissionFailed: '创建任务失败',
|
||||
npcNoPlanets: 'NPC没有星球',
|
||||
npcWillSpyIn5s: '{npcName}将在5秒后侦查',
|
||||
npcWillAttackIn5s: '{npcName}将在5秒后攻击',
|
||||
npcWillSpyAndAttack: '{npcName}将在5秒后侦查,10秒后攻击',
|
||||
acceleratedMissions: '已加速{count}个任务至5秒后',
|
||||
npcFleetInitialized: '{npcName}舰队已初始化',
|
||||
npcFleetDetails: '100 间谍探测器\n500 轻型战机\n300 重型战机\n200 巡洋舰\n100 战列舰\n50 轰炸机\n30 毁灭者\n20 战列巡洋舰',
|
||||
dangerZone: '危险区域',
|
||||
dangerZoneDesc: '以下操作不可撤销,请谨慎操作',
|
||||
resetGame: '重置游戏',
|
||||
@@ -793,6 +841,28 @@ export default {
|
||||
allyAttacked: '攻击盟友',
|
||||
spy: '侦查',
|
||||
stealDebris: '抢夺残骸'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: '赠送了 {metal}金属 {crystal}晶体 {deuterium}氘',
|
||||
receivedGiftFromPlayer: '收到玩家的礼物',
|
||||
giftedToNpc: '你向{npcName}赠送了资源。好感度+{reputation}',
|
||||
rejectedPlayerGift: '拒绝了玩家的礼物',
|
||||
npcRejectedGift: '{npcName}拒绝了你的礼物。好感度{reputation}',
|
||||
attackedNpc: '攻击了{npcName}',
|
||||
wasAttackedByPlayer: '被玩家攻击',
|
||||
youAttackedNpc: '你攻击了{npcName}',
|
||||
playerAttackedAlly: '玩家攻击了盟友{allyName}',
|
||||
allyDispleased: '{allyName}对你攻击盟友{targetName}感到不满',
|
||||
wasSpiedByPlayer: '被玩家侦查(被发现:{detected})',
|
||||
spyDetected: '你的侦查被{npcName}发现了',
|
||||
stoleDebrisFromTerritory: '从{npcName}的领地抢夺了残骸',
|
||||
playerStoleDebris: '玩家从领地抢夺了残骸',
|
||||
recycledDebrisNearNpc: '你在{npcName}的星球附近回收了残骸。他们很不高兴。',
|
||||
giftedResourcesToPlayer: '向玩家赠送了资源',
|
||||
receivedGiftFromNpc: '收到了{npcName}的礼物',
|
||||
acceptedGiftFromNpc: '你接受了{npcName}的礼物:{metal}金属 {crystal}晶体 {deuterium}氘',
|
||||
playerRejectedGift: '玩家拒绝了礼物',
|
||||
rejectedGiftFromNpc: '你拒绝了{npcName}的礼物。好感度{reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -801,5 +871,10 @@ export default {
|
||||
first: '首页',
|
||||
last: '末页',
|
||||
page: '第 {page} 页'
|
||||
},
|
||||
notFound: {
|
||||
title: '页面未找到',
|
||||
description: '抱歉,您访问的页面不存在',
|
||||
goHome: '返回首页'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,13 +33,19 @@ export default {
|
||||
viewRequirements: '查看前置條件',
|
||||
requirementsNotMet: '前置條件未滿足',
|
||||
current: '當前',
|
||||
level: '等級'
|
||||
level: '等級',
|
||||
gmModeActivated: 'GM 模式已啟用!請查看導航選單。'
|
||||
},
|
||||
errors: {
|
||||
requirementsNotMet: '不滿足前置條件',
|
||||
insufficientResources: '資源不足',
|
||||
insufficientFleetStorage: '艦隊倉儲空間不足',
|
||||
shieldDomeLimit: '護盾罩數量限制',
|
||||
missileSiloLimit: '導彈發射井容量已滿',
|
||||
insufficientMissiles: '星際導彈數量不足',
|
||||
invalidMissileCount: '導彈數量無效',
|
||||
targetOutOfRange: '目標超出射程',
|
||||
cannotAttackOwnPlanet: '不能攻擊自己的星球',
|
||||
fleetMissionsFull: '艦隊任務槽位已滿',
|
||||
insufficientFleet: '艦隊數量不足',
|
||||
insufficientFuel: '燃料不足',
|
||||
@@ -166,8 +172,8 @@ export default {
|
||||
darkMatterCollector: '收集稀有的暗物質資源',
|
||||
darkMatterTank: '增加暗物質儲存上限',
|
||||
missileSilo: '存儲和發射導彈,每級可存儲10枚導彈',
|
||||
terraformer: '改造行星地形,每級增加5個可用空間',
|
||||
lunarBase: '增加月球可用空間,每級+5空間',
|
||||
terraformer: '改造行星地形,每級增加30個可用空間',
|
||||
lunarBase: '增加月球可用空間,每級+30空間',
|
||||
sensorPhalanx: '偵測周圍星系的艦隊活動',
|
||||
jumpGate: '瞬間傳送艦隊到其他月球',
|
||||
planetDestroyerFactory: '建造能夠摧毀行星的終極武器'
|
||||
@@ -285,7 +291,7 @@ export default {
|
||||
impulseDrive: '中級推進技術',
|
||||
hyperspaceDrive: '高級推進技術',
|
||||
darkMatterTechnology: '研究暗物質的性質和應用',
|
||||
terraformingTechnology: '研究行星地形改造技術,每級為所有行星增加3個可用空間',
|
||||
terraformingTechnology: '研究行星地形改造技術,每級為所有行星增加30個可用空間',
|
||||
planetDestructionTech: '研究如何摧毀整個行星的恐怖技術'
|
||||
},
|
||||
officers: {
|
||||
@@ -318,6 +324,7 @@ export default {
|
||||
cancelResearch: '取消研究',
|
||||
confirmCancel: '確定要取消嗎?將返還50%的資源。',
|
||||
level: '等級',
|
||||
gmModeActivated: '',
|
||||
upgradeToLevel: '升級到等級'
|
||||
},
|
||||
overview: {
|
||||
@@ -338,6 +345,7 @@ export default {
|
||||
usedSpace: '已用空間',
|
||||
spaceUsage: '佔用空間',
|
||||
level: '等級',
|
||||
gmModeActivated: '',
|
||||
upgradeCost: '升級消耗',
|
||||
buildTime: '建造時間',
|
||||
upgrade: '升級',
|
||||
@@ -349,8 +357,8 @@ export default {
|
||||
demolishRefund: '拆除返還',
|
||||
demolishFailed: '拆除失敗',
|
||||
demolishFailedMessage: '無法拆除該建築,請檢查建造隊列是否已滿或建築等級是否為0。',
|
||||
confirmDemolish: '',
|
||||
confirmDemolishMessage: ''
|
||||
confirmDemolish: '確認拆除',
|
||||
confirmDemolishMessage: '確定要拆除以下建築嗎?'
|
||||
},
|
||||
researchView: {
|
||||
title: '研究',
|
||||
@@ -362,6 +370,7 @@ export default {
|
||||
},
|
||||
shipyard: {
|
||||
attack: '攻擊力',
|
||||
missileAttack: '導彈攻擊',
|
||||
shield: '護盾',
|
||||
armor: '裝甲',
|
||||
speed: '速度',
|
||||
@@ -379,6 +388,7 @@ export default {
|
||||
title: '船塢',
|
||||
fleetStorage: '艦隊倉儲',
|
||||
attack: '攻擊力',
|
||||
missileAttack: '導彈攻擊',
|
||||
shield: '護盾',
|
||||
speed: '速度',
|
||||
cargoCapacity: '載貨量',
|
||||
@@ -393,6 +403,7 @@ export default {
|
||||
},
|
||||
defense: {
|
||||
attack: '攻擊力',
|
||||
missileAttack: '導彈攻擊',
|
||||
shield: '護盾',
|
||||
armor: '裝甲',
|
||||
buildCost: '建造成本',
|
||||
@@ -406,6 +417,7 @@ export default {
|
||||
defenseView: {
|
||||
title: '防禦設施',
|
||||
attack: '攻擊力',
|
||||
missileAttack: '導彈攻擊',
|
||||
shield: '護盾',
|
||||
armor: '裝甲',
|
||||
buildTime: '建造時間',
|
||||
@@ -415,6 +427,7 @@ export default {
|
||||
totalCost: '總成本',
|
||||
build: '建造',
|
||||
shieldDomeBuilt: '護盾罩已建造',
|
||||
missileCapacity: '導彈容量',
|
||||
inputError: '輸入錯誤',
|
||||
inputErrorMessage: '請輸入建造數量!',
|
||||
buildFailed: '建造失敗',
|
||||
@@ -427,6 +440,7 @@ export default {
|
||||
flightMissions: '飛行任務',
|
||||
currentPlanetFleet: '當前星球艦隊',
|
||||
attack: '攻擊',
|
||||
missileAttack: '導彈攻擊',
|
||||
shield: '護盾',
|
||||
armor: '裝甲',
|
||||
speed: '速度',
|
||||
@@ -521,25 +535,36 @@ export default {
|
||||
selectSystem: '選擇星系',
|
||||
view: '查看',
|
||||
myPlanet: '我的星球',
|
||||
myPlanets: '我的星球',
|
||||
myPlanets: '查看我的星系',
|
||||
npcPlanets: 'NPC星球',
|
||||
selectPlanetToView: '選擇要查看的星球',
|
||||
selectPlanetToView: '選擇星球以查看其所在星系',
|
||||
totalPositions: '共10個星球位置',
|
||||
mine: '我的',
|
||||
hostile: '敵對',
|
||||
emptySlot: '空位 - 可殖民',
|
||||
scout: '偵察',
|
||||
attack: '攻擊',
|
||||
missileAttack: '導彈攻擊',
|
||||
colonize: '殖民',
|
||||
switch: '切換',
|
||||
recycle: '回收',
|
||||
debrisField: '殘骸場',
|
||||
scoutPlanetTitle: '偵察星球',
|
||||
attackPlanetTitle: '攻擊星球',
|
||||
missileAttackTitle: '導彈攻擊',
|
||||
colonizePlanetTitle: '殖民星球',
|
||||
recyclePlanetTitle: '回收殘骸',
|
||||
scoutPlanetMessage: '確定要派遣間諜探測器偵察星球 [{coordinates}] 嗎?\n\n請前往艦隊頁面選擇艦船並派遣。',
|
||||
attackPlanetMessage: '確定要攻擊星球 [{coordinates}] 嗎?\n\n請前往艦隊頁面選擇艦船並派遣。',
|
||||
missileAttackMessage: '向星球 [{coordinates}] 發射星際導彈',
|
||||
missileCount: '導彈數量',
|
||||
availableMissiles: '可用導彈',
|
||||
missileRange: '導彈射程',
|
||||
systems: '星系',
|
||||
distance: '距離',
|
||||
flightTime: '飛行時間',
|
||||
launchMissile: '發射',
|
||||
cancel: '取消',
|
||||
colonizePlanetMessage: '確定要殖民位置 [{coordinates}] 嗎?\n\n請前往艦隊頁面派遣殖民船。',
|
||||
recyclePlanetMessage: '確定要回收位置 [{coordinates}] 的殘骸嗎?\n\n請前往艦隊頁面派遣回收船。',
|
||||
sendGift: '贈送禮物',
|
||||
@@ -692,10 +717,24 @@ export default {
|
||||
gamePaused: '遊戲已暫停',
|
||||
gameResumed: '遊戲已恢復',
|
||||
playerName: '玩家名稱',
|
||||
gameSpeed: '遊戲速度',
|
||||
gameSpeedDesc: '目前遊戲速度倍率',
|
||||
gameSpeed: '資源產出速度',
|
||||
gameSpeedDesc: '目前資源產出速度倍率',
|
||||
speedChanged: '資源產出速度已更改為 {speed}x',
|
||||
speedReset: '資源產出速度已重置為 1x',
|
||||
reset: '重置',
|
||||
about: '關於',
|
||||
version: '版本',
|
||||
latestVersion: '最新版本',
|
||||
checkUpdate: '檢查更新',
|
||||
checking: '檢查中...',
|
||||
newVersionAvailable: '發現新版本 {version}',
|
||||
upToDate: '已是最新版本',
|
||||
checkUpdateCooldown: '請稍後再試(5分鐘冷卻時間)',
|
||||
checkUpdateFailed: '檢查更新失敗,請檢查網路連線',
|
||||
viewUpdate: '查看更新',
|
||||
updateAvailable: '有新版本可用。點擊查看更新內容。',
|
||||
download: '下載',
|
||||
goToDownload: '前往下載',
|
||||
buildDate: '建置日期',
|
||||
community: '社群',
|
||||
github: 'GitHub 儲存庫',
|
||||
@@ -734,12 +773,22 @@ export default {
|
||||
testSpy: '測試偵查',
|
||||
testAttack: '測試攻擊',
|
||||
testSpyAndAttack: '測試偵查&攻擊',
|
||||
testSpyMessage: '點擊確認以加速偵查任務',
|
||||
testAttackMessage: '點擊確認以加速攻擊任務',
|
||||
testSpyAndAttackMessage: '點擊確認以加速任務執行',
|
||||
initializeFleet: '初始化NPC艦隊',
|
||||
accelerateMissions: '加速所有任務(5秒)',
|
||||
selectNPCFirst: '請先選擇一個NPC',
|
||||
npcNoProbes: 'NPC沒有間諜探測器',
|
||||
npcNoSpyReport: 'NPC需要先偵查',
|
||||
npcMissionFailed: '創建任務失敗',
|
||||
npcNoPlanets: 'NPC沒有星球',
|
||||
npcWillSpyIn5s: '{npcName}將在5秒後偵查',
|
||||
npcWillAttackIn5s: '{npcName}將在5秒後攻擊',
|
||||
npcWillSpyAndAttack: '{npcName}將在5秒後偵查,10秒後攻擊',
|
||||
acceleratedMissions: '已加速{count}個任務至5秒後',
|
||||
npcFleetInitialized: '{npcName}艦隊已初始化',
|
||||
npcFleetDetails: '100 間諜探測器\n500 輕型戰機\n300 重型戰機\n200 巡洋艦\n100 戰列艦\n50 轟炸機\n30 毀滅者\n20 戰列巡洋艦',
|
||||
dangerZone: '危險區域',
|
||||
dangerZoneDesc: '以下操作不可撤銷,請謹慎操作',
|
||||
resetGame: '重置遊戲',
|
||||
@@ -791,9 +840,32 @@ export default {
|
||||
events: {
|
||||
gift: '已贈送禮物',
|
||||
attack: '攻擊',
|
||||
missileAttack: '導彈攻擊',
|
||||
allyAttacked: '盟友被攻擊',
|
||||
spy: '間諜活動',
|
||||
stealDebris: '掠奪殘骸'
|
||||
},
|
||||
reports: {
|
||||
giftedResources: '贈送了 {metal}金屬 {crystal}晶體 {deuterium}氘',
|
||||
receivedGiftFromPlayer: '收到玩家的禮物',
|
||||
giftedToNpc: '你向{npcName}贈送了資源。好感度+{reputation}',
|
||||
rejectedPlayerGift: '拒絕了玩家的禮物',
|
||||
npcRejectedGift: '{npcName}拒絕了你的禮物。好感度{reputation}',
|
||||
attackedNpc: '攻擊了{npcName}',
|
||||
wasAttackedByPlayer: '被玩家攻擊',
|
||||
youAttackedNpc: '你攻擊了{npcName}',
|
||||
playerAttackedAlly: '玩家攻擊了盟友{allyName}',
|
||||
allyDispleased: '{allyName}對你攻擊盟友{targetName}感到不滿',
|
||||
wasSpiedByPlayer: '被玩家偵查(被發現:{detected})',
|
||||
spyDetected: '你的偵查被{npcName}發現了',
|
||||
stoleDebrisFromTerritory: '從{npcName}的領地掠奪了殘骸',
|
||||
playerStoleDebris: '玩家從領地掠奪了殘骸',
|
||||
recycledDebrisNearNpc: '你在{npcName}的星球附近回收了殘骸。他們很不高興。',
|
||||
giftedResourcesToPlayer: '向玩家贈送了資源',
|
||||
receivedGiftFromNpc: '收到了{npcName}的禮物',
|
||||
acceptedGiftFromNpc: '你接受了{npcName}的禮物:{metal}金屬 {crystal}晶體 {deuterium}氘',
|
||||
playerRejectedGift: '玩家拒絕了禮物',
|
||||
rejectedGiftFromNpc: '你拒絕了{npcName}的禮物。好感度{reputation}'
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
@@ -802,5 +874,10 @@ export default {
|
||||
first: '首頁',
|
||||
last: '末頁',
|
||||
page: '第 {page} 頁'
|
||||
},
|
||||
notFound: {
|
||||
title: '找不到頁面',
|
||||
description: '抱歉,您訪問的頁面不存在',
|
||||
goHome: '返回首頁'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Fleet, Resources, BattleResult, Officer, TechnologyType } from '@/types/game'
|
||||
import { DefenseType, OfficerType } from '@/types/game'
|
||||
import { workerManager } from '@/workers/workerManager'
|
||||
import { MOON_CONFIG } from '@/config/gameConfig'
|
||||
|
||||
/**
|
||||
* 执行战斗模拟
|
||||
@@ -60,7 +61,10 @@ export const simulateBattle = async (
|
||||
|
||||
// 计算月球生成概率(根据残骸场总量)
|
||||
const totalDebris = debrisField.metal + debrisField.crystal
|
||||
const moonChance = Math.min(totalDebris / 100000, 0.2) // 最高20%概率
|
||||
const moonChance = Math.min(
|
||||
(MOON_CONFIG.baseChance + Math.floor(totalDebris / MOON_CONFIG.chancePerDebris)),
|
||||
MOON_CONFIG.maxChance
|
||||
) / 100 // 转换为0-1的概率
|
||||
|
||||
// 生成战斗报告
|
||||
const battleResult: BattleResult = {
|
||||
|
||||
@@ -3,6 +3,9 @@ import { BuildingType, TechnologyType, ShipType, DefenseType } from '@/types/gam
|
||||
import { BUILDINGS } from '@/config/gameConfig'
|
||||
import * as pointsLogic from './pointsLogic'
|
||||
|
||||
// 用于生成唯一ID的计数器
|
||||
let queueIdCounter = 0
|
||||
|
||||
/**
|
||||
* 计算建筑升级成本
|
||||
*/
|
||||
@@ -102,8 +105,9 @@ export const checkSpaceAvailable = (planet: Planet, buildingType: BuildingType):
|
||||
*/
|
||||
export const createBuildQueueItem = (buildingType: BuildingType, targetLevel: number, buildTime: number): BuildQueueItem => {
|
||||
const now = Date.now()
|
||||
queueIdCounter++
|
||||
return {
|
||||
id: `build_${now}`,
|
||||
id: `build_${now}_${queueIdCounter}`,
|
||||
type: 'building',
|
||||
itemType: buildingType,
|
||||
targetLevel,
|
||||
@@ -212,8 +216,9 @@ export const calculateDemolishTime = (
|
||||
*/
|
||||
export const createDemolishQueueItem = (buildingType: BuildingType, currentLevel: number, demolishTime: number): BuildQueueItem => {
|
||||
const now = Date.now()
|
||||
queueIdCounter++
|
||||
return {
|
||||
id: `demolish_${now}`,
|
||||
id: `demolish_${now}_${queueIdCounter}`,
|
||||
type: 'demolish',
|
||||
itemType: buildingType,
|
||||
targetLevel: currentLevel - 1, // 目标等级为当前等级-1
|
||||
|
||||
@@ -23,6 +23,14 @@ export const validateBuildingUpgrade = (
|
||||
const cost = buildingLogic.calculateBuildingCost(buildingType, targetLevel)
|
||||
const buildingConfig = BUILDINGS[buildingType]
|
||||
|
||||
// 检查队列中是否已存在该建筑的升级或拆除任务
|
||||
const existingQueueItem = planet.buildQueue.find(
|
||||
item => (item.type === 'building' || item.type === 'demolish') && item.itemType === buildingType
|
||||
)
|
||||
if (existingQueueItem) {
|
||||
return { valid: false, reason: 'errors.buildingAlreadyInQueue' }
|
||||
}
|
||||
|
||||
// 检查星球/月球限制
|
||||
if (buildingConfig.planetOnly && planet.isMoon) {
|
||||
return { valid: false, reason: 'errors.planetOnly' }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user