├── public ├── favicon.ico ├── favicon.png └── poe2 │ ├── filters.json │ └── static.json ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── src ├── app │ ├── app.routes.server.ts │ ├── app.service.spec.ts │ ├── core │ │ ├── config.service.spec.ts │ │ └── config.service.ts │ ├── app.config.server.ts │ ├── app.config.ts │ ├── app.routes.ts │ ├── about │ │ ├── about.component.scss │ │ ├── about.component.spec.ts │ │ ├── about.component.ts │ │ └── about.component.html │ ├── home │ │ ├── home.component.spec.ts │ │ ├── analyze │ │ │ ├── analyze.component.spec.ts │ │ │ ├── analyze.component.scss │ │ │ ├── analyze.component.html │ │ │ └── analyze.component.ts │ │ ├── home.component.scss │ │ ├── data.ts │ │ └── home.component.html │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.service.ts │ ├── app.component.ts │ └── app.component.scss ├── main.ts ├── styles.scss └── index.html ├── .editorconfig ├── preload.js ├── tsconfig.app.json ├── tsconfig.spec.json ├── .gitignore ├── tsconfig.json ├── forge.config.js ├── README.md ├── package.json ├── angular.json └── main.js /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seraveegd/poe2-trade-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seraveegd/poe2-trade-app/HEAD/public/favicon.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /src/app/app.routes.server.ts: -------------------------------------------------------------------------------- 1 | import { RenderMode, ServerRoute } from '@angular/ssr'; 2 | 3 | export const serverRoutes: ServerRoute[] = [ 4 | { 5 | path: '**', 6 | renderMode: RenderMode.Prerender 7 | } 8 | ]; 9 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { bootstrapApplication } from '@angular/platform-browser'; 4 | import { appConfig } from './app/app.config'; 5 | import { AppComponent } from './app/app.component'; 6 | 7 | bootstrapApplication(AppComponent, appConfig) 8 | .catch((err) => console.error(err)); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | ij_typescript_use_double_quotes = false 14 | 15 | [*.md] 16 | max_line_length = off 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppService', () => { 6 | let service: AppService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(AppService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/core/config.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ConfigService } from './config.service'; 4 | 5 | describe('ConfigService', () => { 6 | let service: ConfigService; 7 | 8 | beforeEach(() => { 9 | TestBed.configureTestingModule({}); 10 | service = TestBed.inject(ConfigService); 11 | }); 12 | 13 | it('should be created', () => { 14 | expect(service).toBeTruthy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | import { provideServerRendering, withRoutes } from '@angular/ssr'; 2 | import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; 3 | import { appConfig } from './app.config'; 4 | import { serverRoutes } from './app.routes.server'; 5 | 6 | const serverConfig: ApplicationConfig = { 7 | providers: [provideServerRendering(withRoutes(serverRoutes))] 8 | }; 9 | 10 | export const config = mergeApplicationConfig(appConfig, serverConfig); 11 | -------------------------------------------------------------------------------- /preload.js: -------------------------------------------------------------------------------- 1 | const ipcRenderer = require('electron').ipcRenderer; 2 | 3 | window.ipcRenderer = ipcRenderer; 4 | 5 | window.addEventListener('DOMContentLoaded', () => { 6 | const replaceText = (selector, text) => { 7 | const element = document.getElementById(selector) 8 | if (element) element.innerText = text 9 | } 10 | 11 | for (const dependency of ['chrome', 'node', 'electron']) { 12 | replaceText(`${dependency}-version`, process.versions[dependency]) 13 | } 14 | }) -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "./out-tsc/app", 7 | "types": [ 8 | "@angular/localize" 9 | ] 10 | }, 11 | "files": [ 12 | "src/main.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "./tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "./out-tsc/spec", 7 | "types": [ 8 | "jasmine", 9 | "@angular/localize" 10 | ] 11 | }, 12 | "include": [ 13 | "src/**/*.spec.ts", 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/app/core/config.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Optional } from '@angular/core'; 2 | 3 | export class Config { 4 | api_base_url = 'https://pathofexile.tw'; 5 | } 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class ConfigService { 11 | 12 | private _api_base_url = 'https://pathofexile.tw'; 13 | 14 | constructor(@Optional() config: Config) { 15 | if (config) { 16 | this._api_base_url = config.api_base_url; 17 | } 18 | } 19 | 20 | get api_base_url() { 21 | return this._api_base_url; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | /* Importing Bootstrap SCSS file. */ 4 | @import 'bootstrap/scss/bootstrap'; 5 | 6 | * { 7 | font-size: 14px; 8 | } 9 | 10 | :root { 11 | color-scheme: light dark; 12 | } 13 | 14 | @media (prefers-color-scheme: dark) { 15 | body { 16 | background: #333; 17 | color: white; 18 | } 19 | } 20 | 21 | @media (prefers-color-scheme: light) { 22 | body { 23 | background: #ddd; 24 | color: black; 25 | } 26 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | import { provideClientHydration, withEventReplay } from '@angular/platform-browser'; 6 | import { provideHttpClient } from '@angular/common/http'; 7 | 8 | export const appConfig: ApplicationConfig = { 9 | providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay()), provideHttpClient()] 10 | }; 11 | -------------------------------------------------------------------------------- /src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { HomeComponent } from './home/home.component'; 3 | import { AboutComponent } from './about/about.component'; 4 | 5 | export const routes: Routes = [ 6 | { 7 | path: 'home', 8 | component: HomeComponent 9 | }, 10 | { 11 | path: 'about', 12 | component: AboutComponent 13 | }, 14 | { 15 | path: '', 16 | redirectTo: 'home', 17 | pathMatch: 'full' 18 | }, 19 | { 20 | path: '**', 21 | component: HomeComponent 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /src/app/about/about.component.scss: -------------------------------------------------------------------------------- 1 | .card-body { 2 | padding: 10px !important; 3 | 4 | * { 5 | text-align: center; 6 | } 7 | 8 | p { 9 | margin-block-start: 0.5em; 10 | margin-block-end: 0em; 11 | } 12 | } 13 | 14 | .card-header{ 15 | text-align: center; 16 | } 17 | 18 | @media (prefers-color-scheme: dark) { 19 | .card { 20 | background-color: rgb(27 27 27 / 30%); 21 | color: #e5e4e4; 22 | border-color: #747474; 23 | 24 | .card-header{ 25 | background-color: rgb(26 24 24 / 50%); 26 | border-color: #747474; 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/app/home/home.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomeComponent } from './home.component'; 4 | 5 | describe('HomeComponent', () => { 6 | let component: HomeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [HomeComponent] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(HomeComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/app/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutComponent } from './about.component'; 4 | 5 | describe('AboutComponent', () => { 6 | let component: AboutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [AboutComponent] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(AboutComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/app/home/analyze/analyze.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AnalyzeComponent } from './analyze.component'; 4 | 5 | describe('AnalyzeComponent', () => { 6 | let component: AnalyzeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | imports: [AnalyzeComponent] 12 | }) 13 | .compileComponents(); 14 | 15 | fixture = TestBed.createComponent(AnalyzeComponent); 16 | component = fixture.componentInstance; 17 | fixture.detectChanges(); 18 | }); 19 | 20 | it('should create', () => { 21 | expect(component).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | /out 9 | 10 | # Node 11 | /node_modules 12 | npm-debug.log 13 | yarn-error.log 14 | 15 | # IDEs and editors 16 | .idea/ 17 | .project 18 | .classpath 19 | .c9/ 20 | *.launch 21 | .settings/ 22 | *.sublime-workspace 23 | 24 | # Visual Studio Code 25 | .vscode/* 26 | !.vscode/settings.json 27 | !.vscode/tasks.json 28 | !.vscode/launch.json 29 | !.vscode/extensions.json 30 | .history/* 31 | 32 | # Miscellaneous 33 | /.angular/cache 34 | .sass-cache/ 35 | /connect.lock 36 | /coverage 37 | /libpeerconnection.log 38 | testem.log 39 | /typings 40 | 41 | # System files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /src/app/home/analyze/analyze.component.scss: -------------------------------------------------------------------------------- 1 | #fetchResult { 2 | tbody { 3 | img { 4 | max-width: 36px; 5 | } 6 | } 7 | } 8 | 9 | @media (prefers-color-scheme: dark) { 10 | table { 11 | th { 12 | background-color: #333333; 13 | color: #e5e4e4; 14 | border-color: #6d6d6d; 15 | } 16 | 17 | tr:nth-child(even) { 18 | td { 19 | background: rgb(38 37 36 / 30%); 20 | color: #e5e4e4; 21 | border-color: #6d6d6d; 22 | } 23 | } 24 | 25 | tr:nth-child(odd) { 26 | td { 27 | background-color: rgb(38 38 37 / 70%) !important; 28 | color: #e5e4e4; 29 | border-color: #6d6d6d; 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/app/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; 3 | import { Shell } from 'electron'; 4 | 5 | @Component({ 6 | selector: 'app-about', 7 | imports: [NgbAccordionModule], 8 | templateUrl: './about.component.html', 9 | styleUrl: './about.component.scss' 10 | }) 11 | export class AboutComponent { 12 | public shell!: Shell; 13 | 14 | constructor() { 15 | if ((window).require) { 16 | try { 17 | this.shell = (window).require('electron').shell; 18 | } catch (e) { 19 | throw e; 20 | } 21 | } else { 22 | console.warn('App not running inside Electron!'); 23 | } 24 | } 25 | 26 | openGithubRelease(){ 27 | this.shell.openExternal("https://github.com/Seraveegd/poe2-trade-app/releases"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | @if(mode === 'overlay') { 3 |
4 | } 5 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "compileOnSave": false, 5 | "compilerOptions": { 6 | "outDir": "./dist/out-tsc", 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "isolatedModules": true, 14 | "esModuleInterop": true, 15 | "experimentalDecorators": true, 16 | "moduleResolution": "bundler", 17 | "importHelpers": true, 18 | "target": "ES2022", 19 | "module": "ES2022" 20 | }, 21 | "angularCompilerOptions": { 22 | "enableI18nLegacyMessageIdFormat": false, 23 | "strictInjectionParameters": true, 24 | "strictInputAccessModifiers": true, 25 | "strictTemplates": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'electron-app' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('electron-app'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('Hello, electron-app'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Poe2TradeApp 7 | 8 | 9 | 10 | 25 | 26 | 27 | 28 |
29 |
30 |
31 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /forge.config.js: -------------------------------------------------------------------------------- 1 | const { FusesPlugin } = require('@electron-forge/plugin-fuses'); 2 | const { FuseV1Options, FuseVersion } = require('@electron/fuses'); 3 | 4 | module.exports = { 5 | packagerConfig: { 6 | asar: true, 7 | icon: __dirname + '/public/favicon', 8 | extraResource: [ 9 | __dirname + '/resources/items.json', 10 | __dirname + '/resources/stats.json', 11 | ] 12 | }, 13 | rebuildConfig: {}, 14 | makers: [ 15 | { 16 | name: '@electron-forge/maker-squirrel', 17 | config: { 18 | setupIcon: __dirname + '/public/favicon.ico', 19 | iconUrl: __dirname + '/public/favicon.ico' 20 | }, 21 | }, 22 | { 23 | name: '@electron-forge/maker-zip', 24 | platforms: ['darwin', 'win32'], 25 | } 26 | ], 27 | plugins: [ 28 | { 29 | name: '@electron-forge/plugin-auto-unpack-natives', 30 | config: {}, 31 | }, 32 | // Fuses are used to enable/disable various Electron functionality 33 | // at package time, before code signing the application 34 | new FusesPlugin({ 35 | version: FuseVersion.V1, 36 | [FuseV1Options.RunAsNode]: false, 37 | [FuseV1Options.EnableCookieEncryption]: true, 38 | [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, 39 | [FuseV1Options.EnableNodeCliInspectArguments]: false, 40 | [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, 41 | [FuseV1Options.OnlyLoadAppFromAsar]: true, 42 | }), 43 | ], 44 | }; 45 | -------------------------------------------------------------------------------- /src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 3 | import { ConfigService } from './core/config.service'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class AppService { 9 | 10 | constructor(private http: HttpClient, private config: ConfigService) { } 11 | 12 | get_leagues(): any { 13 | return this.http.get(`${this.config.api_base_url}/api/trade2/data/leagues`, {}); 14 | } 15 | 16 | get_trade(league: any, query: any): any { 17 | let headers: HttpHeaders = new HttpHeaders(); 18 | headers.set('Accept', ' application/json'); 19 | 20 | league = encodeURI(league); 21 | 22 | return this.http.post(`${this.config.api_base_url}/api/trade2/search/poe2/${league}`, query, { headers: headers }); 23 | } 24 | 25 | get_trade_fetch(fetchstr: any, fetchQueryID: any): any { 26 | return this.http.get(`${this.config.api_base_url}/api/trade2/fetch/${fetchstr}?query=${fetchQueryID}&realm=poe2`, {}); 27 | } 28 | 29 | getOfficialItemData(): any { 30 | return this.http.get(`${this.config.api_base_url}/api/trade2/data/items`, {}); 31 | } 32 | 33 | getOfficialStatesData(): any { 34 | return this.http.get(`${this.config.api_base_url}/api/trade2/data/stats`, {}); 35 | } 36 | 37 | getItemData(): any { 38 | return this.http.get('poe2/items.json'); 39 | } 40 | 41 | getStatsData(): any { 42 | return this.http.get('poe2/stats.json'); 43 | } 44 | 45 | getStatsRangesData(): any { 46 | return this.http.get('poe2/ranges.json'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Poe2TradeApp 2 | 3 | This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.0.6. 4 | 5 | ## Development server 6 | 7 | To start a local development server, run: 8 | 9 | ```bash 10 | ng serve 11 | ``` 12 | 13 | Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. 14 | 15 | ## Code scaffolding 16 | 17 | Angular CLI includes powerful code scaffolding tools. To generate a new component, run: 18 | 19 | ```bash 20 | ng generate component component-name 21 | ``` 22 | 23 | For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: 24 | 25 | ```bash 26 | ng generate --help 27 | ``` 28 | 29 | ## Building 30 | 31 | To build the project run: 32 | 33 | ```bash 34 | ng build 35 | ``` 36 | 37 | This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. 38 | 39 | ## Running unit tests 40 | 41 | To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: 42 | 43 | ```bash 44 | ng test 45 | ``` 46 | 47 | ## Running end-to-end tests 48 | 49 | For end-to-end (e2e) testing, run: 50 | 51 | ```bash 52 | ng e2e 53 | ``` 54 | 55 | Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. 56 | 57 | ## Additional Resources 58 | 59 | For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. 60 | -------------------------------------------------------------------------------- /src/app/about/about.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | POE2 查價工具 v0.7.10 6 |
7 |
8 |

作者:Seraveegd

9 |

10 | 13 |

14 |
15 |
16 |
17 |
18 |
19 |
20 | 免責聲明 21 |
22 |
23 |
使用此程式者請自行承擔所有可能後果和風險
24 |

此程式並無修改任何記憶體

25 |

運作原理為抓取 POE2 遊戲內對物品複製後的文字進行分析,以利快速查價

26 |

串接的 API 皆為官方公開資源,請勿於短時間內快速搜尋造成伺服器負擔

27 |

28 | This product isn't affiliated with 29 | or 30 | endorsed by Grinding Gear Games and Garena in any way. 31 |

32 |
33 |
34 |
35 |
36 |
37 |
38 | 這是免費程式 39 |
40 |
41 |

此程式已在 GitHub 上開源,歡迎分享給親朋好友使用

42 |
43 |
44 |
45 |
46 |
47 |
48 | 使用說明 49 |
50 |
51 |

1. 將滑鼠停在物品上,按下 Ctrl+C (確認在首頁視窗)

52 |

2. 程式將自動判斷此次搜尋物品

53 |
54 |
55 |
56 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poe2-trade-app", 3 | "version": "0.7.10", 4 | "main": "main.js", 5 | "author": "Seraveegd", 6 | "description": "poe2-trade-app", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Seraveegd/poe2-trade-app.git", 10 | "directory": "Seraveegd/poe2-trade-app" 11 | }, 12 | "scripts": { 13 | "ng": "ng", 14 | "start": "electron-forge start", 15 | "build": "ng build", 16 | "watch": "ng build --watch --configuration development", 17 | "test": "ng test", 18 | "electron": "ng build && electron .", 19 | "package": "electron-forge package", 20 | "make": "electron-forge make" 21 | }, 22 | "private": true, 23 | "dependencies": { 24 | "@angular/animations": "^19.0.0", 25 | "@angular/common": "^19.0.0", 26 | "@angular/compiler": "^19.0.0", 27 | "@angular/core": "^19.0.0", 28 | "@angular/forms": "^19.0.0", 29 | "@angular/platform-browser": "^19.0.0", 30 | "@angular/platform-browser-dynamic": "^19.0.0", 31 | "@angular/router": "^19.0.0", 32 | "@ng-bootstrap/ng-bootstrap": "^18.0.0", 33 | "@popperjs/core": "^2.11.8", 34 | "bootstrap": "^5.3.3", 35 | "electron-log": "^5.4.3", 36 | "electron-overlay-window": "^3.3.0", 37 | "electron-squirrel-startup": "^1.0.1", 38 | "electron-store": "^10.0.1", 39 | "rxjs": "~7.8.0", 40 | "tslib": "^2.3.0", 41 | "update-electron-app": "^3.1.1", 42 | "zone.js": "~0.15.0" 43 | }, 44 | "devDependencies": { 45 | "@angular-devkit/build-angular": "^19.0.6", 46 | "@angular/cli": "^19.0.6", 47 | "@angular/compiler-cli": "^19.0.0", 48 | "@angular/localize": "^19.0.0", 49 | "@electron-forge/cli": "^7.8.0", 50 | "@electron-forge/maker-deb": "^7.8.0", 51 | "@electron-forge/maker-rpm": "^7.8.0", 52 | "@electron-forge/maker-squirrel": "^7.8.0", 53 | "@electron-forge/maker-zip": "^7.8.0", 54 | "@electron-forge/plugin-auto-unpack-natives": "^7.8.0", 55 | "@electron-forge/plugin-fuses": "^7.8.0", 56 | "@electron/fuses": "^1.8.0", 57 | "@types/jasmine": "~5.1.0", 58 | "electron": "^35.1.2", 59 | "jasmine-core": "~5.4.0", 60 | "karma": "~6.4.0", 61 | "karma-chrome-launcher": "~3.2.0", 62 | "karma-coverage": "~2.2.0", 63 | "karma-jasmine": "~5.1.0", 64 | "karma-jasmine-html-reporter": "~2.1.0", 65 | "typescript": "~5.6.2" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { RouterLink, RouterOutlet } from '@angular/router'; 3 | import { CommonModule } from '@angular/common'; 4 | import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | imports: [ 9 | RouterOutlet, 10 | RouterLink, 11 | CommonModule, 12 | NgbTooltipModule 13 | ], 14 | providers: [], 15 | templateUrl: './app.component.html', 16 | styleUrl: './app.component.scss' 17 | }) 18 | 19 | export class AppComponent implements OnInit { 20 | 21 | public colorScheme = 'dark'; 22 | public mode: any; 23 | 24 | constructor() { } 25 | 26 | ngOnInit(): void { 27 | (window).ipcRenderer.on('reply-mode', (event: any, arg: any) => { 28 | this.mode = arg; 29 | }); 30 | (window).ipcRenderer.send('get-mode'); 31 | 32 | const localStorageColorScheme = localStorage.getItem('prefers-color'); 33 | // Check if any prefers-color-scheme is stored in localStorage 34 | if (localStorageColorScheme) { 35 | this.colorScheme = localStorageColorScheme; 36 | // Save prefers-color-scheme from localStorage 37 | (window).ipcRenderer.send('toggle-theme', localStorageColorScheme); 38 | } 39 | 40 | (window).ipcRenderer.on('visibility-change', (e: any, state: any) => { 41 | this.isDisplay(state); 42 | }); 43 | } 44 | 45 | changeTheme() { 46 | if (this.colorScheme === 'dark') { 47 | this.colorScheme = 'light'; 48 | } else { 49 | this.colorScheme = 'dark'; 50 | } 51 | 52 | localStorage.setItem('prefers-color', this.colorScheme); 53 | 54 | (window).ipcRenderer.send('toggle-theme', this.colorScheme); 55 | } 56 | 57 | isDisplay(state: any) { 58 | console.log(state, document.body.style.display); 59 | if (state === true) { 60 | document.body.style.display = 'block'; 61 | // (window).ipcRenderer.send('overlay'); 62 | } else if (state === false) { 63 | document.body.style.display = 'none'; 64 | } else if (state == 'blur') { 65 | (window).ipcRenderer.send('blur'); 66 | } else if (typeof state === 'undefined') { 67 | if (document.body.style.display === 'none') { 68 | console.log('+'); 69 | document.body.style.display = 'block'; 70 | } else { 71 | console.log('-'); 72 | document.body.style.display = 'none'; 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/app/home/home.component.scss: -------------------------------------------------------------------------------- 1 | .row { 2 | padding: 4px; 3 | 4 | &:first-child { 5 | padding: 0 4px 4px 4px; 6 | } 7 | 8 | &:last-child { 9 | padding: 4px 4px 0 4px 10 | } 11 | 12 | input[type="text"] { 13 | font-size: 12px; 14 | } 15 | 16 | div { 17 | align-content: center; 18 | } 19 | } 20 | 21 | #search { 22 | padding: 6px; 23 | } 24 | 25 | #stats { 26 | margin: 0; 27 | user-select: none; 28 | 29 | tr { 30 | td { 31 | position: relative; 32 | vertical-align: middle; 33 | padding: 2px !important; 34 | 35 | input { 36 | border: 1px solid #ccc; 37 | } 38 | } 39 | } 40 | } 41 | 42 | select { 43 | cursor: pointer; 44 | } 45 | 46 | input[type=range]::-webkit-slider-thumb { 47 | background: #ccc; 48 | } 49 | 50 | .form-range::-webkit-slider-runnable-track { 51 | background-color: rgba(79, 76, 76, 0.877); 52 | } 53 | 54 | @media (prefers-color-scheme: dark) { 55 | #stats { 56 | border: 1px solid #525252; 57 | box-shadow: 0px 0px 30px rgb(0 0 0 / 50%); 58 | } 59 | 60 | #itemName { 61 | color: white; 62 | } 63 | 64 | #result_status { 65 | color: white; 66 | } 67 | 68 | .card { 69 | background-color: rgb(32 29 29 / 70%); 70 | border: 1px solid rgb(204 204 204 / 30%); 71 | 72 | .form-floating { 73 | label { 74 | color: #e5e4e4; 75 | } 76 | 77 | label::after { 78 | background: none; 79 | } 80 | } 81 | 82 | select, 83 | input.form-control { 84 | background: #292828; 85 | color: #959595; 86 | border: 1px solid #626262 !important; 87 | 88 | &:disabled { 89 | background-color: #5b5b5b; 90 | } 91 | } 92 | 93 | label { 94 | color: #e5e4e4; 95 | } 96 | 97 | table { 98 | tr { 99 | th { 100 | background-color: #1b1a1b; 101 | color: #d0cdcd; 102 | border-color: #6d6d6d; 103 | } 104 | 105 | td { 106 | background-color: #29292a; 107 | color: #b6b6b6; 108 | font-weight: bold; 109 | text-shadow: 1px 1px 2px #211d1d; 110 | border-color: #6d6d6d; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | 117 | @media (prefers-color-scheme: light) { 118 | #stats { 119 | border: 1px solid #e8e0e0; 120 | box-shadow: 0 0 30px #c4c4c480; 121 | } 122 | 123 | .card { 124 | background-color: #ffffff82; 125 | border: 1px solid #cccccc7a; 126 | 127 | select, 128 | input.form-control { 129 | background: #ffffff82; 130 | color: #131313; 131 | } 132 | } 133 | 134 | #itemName { 135 | color: #131313; 136 | } 137 | 138 | #result_status { 139 | color: #131313; 140 | } 141 | 142 | table { 143 | tr { 144 | td { 145 | font-weight: bold; 146 | text-shadow: 1px 1px 2px #ddd2d2; 147 | 148 | &:nth-child(3) { 149 | color: #313131; 150 | } 151 | } 152 | } 153 | } 154 | 155 | input[type=range]::-webkit-slider-thumb { 156 | background: rgb(53, 51, 51) 157 | } 158 | 159 | .form-range::-webkit-slider-runnable-track { 160 | background-color: #d2ddfdb8; 161 | } 162 | } -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "poe2-trade-app": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular/build:application", 19 | "options": { 20 | "outputPath": "dist/poe2-trade-app", 21 | "index": "src/index.html", 22 | "browser": "src/main.ts", 23 | "polyfills": [ 24 | "zone.js", 25 | "@angular/localize/init" 26 | ], 27 | "tsConfig": "tsconfig.app.json", 28 | "inlineStyleLanguage": "scss", 29 | "assets": [ 30 | { 31 | "glob": "**/*", 32 | "input": "public" 33 | } 34 | ], 35 | "styles": [ 36 | "src/styles.scss" 37 | ], 38 | "scripts": [] 39 | }, 40 | "configurations": { 41 | "production": { 42 | "budgets": [ 43 | { 44 | "type": "initial", 45 | "maximumWarning": "500kB", 46 | "maximumError": "1MB" 47 | }, 48 | { 49 | "type": "anyComponentStyle", 50 | "maximumWarning": "4kB", 51 | "maximumError": "8kB" 52 | } 53 | ], 54 | "outputHashing": "all" 55 | }, 56 | "development": { 57 | "optimization": false, 58 | "extractLicenses": false, 59 | "sourceMap": true 60 | } 61 | }, 62 | "defaultConfiguration": "production" 63 | }, 64 | "serve": { 65 | "builder": "@angular/build:dev-server", 66 | "configurations": { 67 | "production": { 68 | "buildTarget": "poe2-trade-app:build:production" 69 | }, 70 | "development": { 71 | "buildTarget": "poe2-trade-app:build:development" 72 | } 73 | }, 74 | "defaultConfiguration": "development" 75 | }, 76 | "extract-i18n": { 77 | "builder": "@angular/build:extract-i18n" 78 | }, 79 | "test": { 80 | "builder": "@angular/build:karma", 81 | "options": { 82 | "polyfills": [ 83 | "zone.js", 84 | "zone.js/testing", 85 | "@angular/localize/init" 86 | ], 87 | "tsConfig": "tsconfig.spec.json", 88 | "inlineStyleLanguage": "scss", 89 | "assets": [ 90 | { 91 | "glob": "**/*", 92 | "input": "public" 93 | } 94 | ], 95 | "styles": [ 96 | "src/styles.scss" 97 | ], 98 | "scripts": [] 99 | } 100 | } 101 | } 102 | } 103 | }, 104 | "cli": { 105 | "analytics": "d3cbf89a-1550-415a-ba23-ee9df1513bda" 106 | }, 107 | "schematics": { 108 | "@schematics/angular:component": { 109 | "type": "component" 110 | }, 111 | "@schematics/angular:directive": { 112 | "type": "directive" 113 | }, 114 | "@schematics/angular:service": { 115 | "type": "service" 116 | }, 117 | "@schematics/angular:guard": { 118 | "typeSeparator": "." 119 | }, 120 | "@schematics/angular:interceptor": { 121 | "typeSeparator": "." 122 | }, 123 | "@schematics/angular:module": { 124 | "typeSeparator": "." 125 | }, 126 | "@schematics/angular:pipe": { 127 | "typeSeparator": "." 128 | }, 129 | "@schematics/angular:resolver": { 130 | "typeSeparator": "." 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/app/home/analyze/analyze.component.html: -------------------------------------------------------------------------------- 1 | @if (searchResult.fetchQueryID !== '') { 2 |
3 |
4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 32 | 33 | 34 | 35 | @for (currencys of computed; track $index) { 36 | @for (currency of currencys[1]; track $index;) { 37 | 38 | 45 | 46 | } 47 | } 48 | 60 | 61 | 68 |
12 | 前 {{ fetchResult.length }} 筆價格分析 13 | @if (corruptedCount > 0) { 14 |
15 | {{ corruptedCount }}筆已汙染 16 |
17 | } 18 | @if (searchResult.extraFilterStr !== ''){ 19 |
群組內過濾:{{ searchResult.extraFilterStr }} 20 |
剩餘{{ extraFilterCount }}筆 21 | } 22 | 24 | 27 | 28 | 31 |
報價:{{ currency[0] }} x / {{ 40 | currency[1][0] }}筆 41 | @if (currency[1][1] > 0) { 42 | ({{currency[1][1]}}筆已汙染) 43 | } 44 |
69 |
70 |
71 | } -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: Avenir, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | border-bottom: 1px solid rgb(75 75 75 / 50%); 6 | padding: 3px; 7 | box-shadow: 1px 1px 5px #1c1e1d; 8 | margin-bottom: 4px; 9 | position: relative; 10 | 11 | #hide { 12 | position: fixed; 13 | top: 34px; 14 | z-index: 999; 15 | line-height: 32px; 16 | left: 50%; 17 | transform: translate(-50%, -50%); 18 | margin-left: 270px; 19 | 20 | button { 21 | border: 1px solid rgba(97, 97, 97, 0.5); 22 | } 23 | } 24 | 25 | #nav { 26 | display: flex; 27 | } 28 | 29 | a { 30 | padding: 4px; 31 | text-decoration: none; 32 | flex: auto; 33 | text-align: center; 34 | 35 | &:hover { 36 | background-color: rgb(34 32 32 / 50%); 37 | } 38 | } 39 | 40 | button { 41 | margin-left: auto; 42 | } 43 | } 44 | 45 | @media (prefers-color-scheme: dark) { 46 | #app { 47 | background-color: rgba(19, 19, 19, 0.8); 48 | 49 | a { 50 | color: white; 51 | } 52 | } 53 | 54 | #hide { 55 | button { 56 | background: rgba(56, 56, 56, 0.5); 57 | } 58 | } 59 | } 60 | 61 | @media (prefers-color-scheme: light) { 62 | #app { 63 | box-shadow: 1px 1px 5px rgba(255, 255, 255, 0.5); 64 | background-color: #ffffff82; 65 | border: 1px solid #cccccc7a; 66 | 67 | a { 68 | color: #0067ff; 69 | 70 | &:hover { 71 | background: rgb(190 186 186 / 50%); 72 | } 73 | 74 | &:first-child { 75 | border-right: 1px solid #c0c0c0 !important; 76 | } 77 | } 78 | } 79 | 80 | #hide { 81 | button { 82 | background-color: #d2ddfdb8; 83 | } 84 | } 85 | } 86 | 87 | #toggle_checkbox { 88 | display: none; 89 | } 90 | 91 | label { 92 | display: block; 93 | width: 116px; 94 | height: 28px; 95 | margin: 0 auto; 96 | background-color: #77b5fe; 97 | border-radius: 56px; 98 | transform: translateY(-50%); 99 | cursor: pointer; 100 | transition: 0.3s ease background-color; 101 | overflow: hidden; 102 | position: absolute; 103 | top: 15px; 104 | } 105 | 106 | #star { 107 | position: absolute; 108 | top: 6px; 109 | left: 14px; 110 | width: 15px; 111 | height: 10px; 112 | background-color: #fafd0f; 113 | transform: scale(1); 114 | border-radius: 50%; 115 | transition: 0.3s ease top, 0.3s ease left, 0.3s ease transform, 0.3s ease background-color; 116 | z-index: 1; 117 | } 118 | 119 | #star-1 { 120 | position: relative; 121 | } 122 | 123 | #star-2 { 124 | position: absolute; 125 | transform: rotateZ(36deg); 126 | } 127 | 128 | .star { 129 | top: 3px; 130 | left: -5px; 131 | font-size: 31px; 132 | line-height: 8px; 133 | color: #fafd0f; 134 | transition: 0.3s ease color; 135 | } 136 | 137 | #moon { 138 | position: absolute; 139 | bottom: -52px; 140 | right: 28px; 141 | width: 27px; 142 | height: 27px; 143 | background-color: #fff; 144 | border-radius: 50%; 145 | transition: .3s ease bottom; 146 | } 147 | 148 | #moon:before { 149 | content: ""; 150 | position: absolute; 151 | top: -12px; 152 | left: -17px; 153 | width: 40px; 154 | height: 40px; 155 | background-color: #03a9f4; 156 | border-radius: 50%; 157 | transition: 0.3s ease background-color; 158 | } 159 | 160 | #toggle_checkbox:checked+label { 161 | background-color: #000; 162 | } 163 | 164 | #toggle_checkbox:checked+label #star { 165 | top: 3px; 166 | left: 64px; 167 | transform: scale(0.3); 168 | background-color: yellow; 169 | } 170 | 171 | #toggle_checkbox:checked+label .star { 172 | color: yellow; 173 | } 174 | 175 | #toggle_checkbox:checked+label #moon { 176 | bottom: 2px; 177 | } 178 | 179 | #toggle_checkbox:checked+label #moon:before { 180 | background-color: #000; 181 | } -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow, ipcMain, nativeTheme, globalShortcut, nativeImage, Tray, Menu, Notification } = require('electron'); 2 | const { OverlayController, OVERLAY_WINDOW_OPTS } = require('electron-overlay-window'); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const { exec } = require('child_process'); 6 | const { updateElectronApp, UpdateSourceType } = require('update-electron-app') 7 | 8 | app.disableHardwareAcceleration(); 9 | 10 | let store = null; 11 | 12 | (async () => { 13 | const Store = (await import('electron-store')).default; 14 | store = new Store(); 15 | 16 | if (typeof store.get('mode') == 'undefined') { 17 | store.set('mode', 'overlay'); 18 | } 19 | 20 | const mode = store.get('mode'); 21 | 22 | if (require('electron-squirrel-startup')) app.quit(); 23 | 24 | let win = null; 25 | let tray = null; 26 | 27 | const toggleMouseKey = 'CmdOrCtrl + J'; 28 | const toggleShowKey = 'CmdOrCtrl + K'; 29 | 30 | //視窗模式 31 | function createWindow() { 32 | win = new BrowserWindow({ 33 | width: 600, 34 | height: 800, 35 | icon: `dist/poe2-trade-app/browser/favicon.ico`, 36 | backgroundColor: '#000000cc', 37 | webPreferences: { 38 | defaultFontFamily: { 39 | standard: "Microsoft YaHei" 40 | }, 41 | defaultFontSize: 14, 42 | preload: path.join(__dirname, 'preload.js'), 43 | nodeIntegration: true, 44 | contextIsolation: false 45 | }, 46 | autoHideMenuBar: true 47 | }); 48 | 49 | win.once('ready-to-show', () => { 50 | win.show(); 51 | }); 52 | 53 | win.loadURL(path.join(__dirname, `dist/poe2-trade-app/browser/index.html`)); 54 | 55 | // Open the DevTools. 56 | // win.webContents.openDevTools({ mode: 'detach', activate: false }); 57 | 58 | nativeTheme.themeSource = 'dark'; 59 | 60 | ipcMain.on('analyze-item', (msg) => { 61 | //nothing 62 | }); 63 | } 64 | 65 | //覆蓋模式 66 | function createOverlayWindow() { 67 | win = new BrowserWindow({ 68 | width: 600, 69 | height: 800, 70 | icon: `dist/poe2-trade-app/browser/favicon.ico`, 71 | webPreferences: { 72 | defaultFontFamily: { 73 | standard: "Microsoft YaHei" 74 | }, 75 | defaultFontSize: 14, 76 | preload: path.join(__dirname, 'preload.js'), 77 | nodeIntegration: true, 78 | contextIsolation: false 79 | }, 80 | ...OVERLAY_WINDOW_OPTS 81 | }); 82 | 83 | win.once('ready-to-show', () => { 84 | win.show(); 85 | }); 86 | 87 | win.loadURL(path.join(__dirname, `dist/poe2-trade-app/browser/index.html`)); 88 | 89 | // Open the DevTools. 90 | // win.webContents.openDevTools({ mode: 'detach', activate: false }); 91 | 92 | makeInteractive(); 93 | 94 | OverlayController.attachByTitle( 95 | win, 96 | 'Path of Exile 2', 97 | { hasTitleBarOnMac: true } 98 | ) 99 | 100 | OverlayController.events.on('attach', () => { 101 | console.log('OC: attach'); 102 | 103 | new Notification({ 104 | title: 'POE2 查價通知', 105 | body: '檢測到POE2視窗。', 106 | timeoutType: '2000', 107 | icon: `dist/poe2-trade-app/browser/favicon.ico` 108 | }).show(); 109 | }); 110 | 111 | OverlayController.events.on('detach', () => { 112 | console.log('OC: detach'); 113 | 114 | new Notification({ 115 | title: 'POE2 查價通知', 116 | body: '未檢測到POE2視窗。', 117 | timeoutType: '2000', 118 | icon: `dist/poe2-trade-app/browser/favicon.ico` 119 | }).show(); 120 | }); 121 | 122 | nativeTheme.themeSource = 'dark'; 123 | 124 | function makeInteractive() { 125 | let isInteractable = false; 126 | 127 | function toggleOverlayState() { 128 | if (isInteractable) { 129 | isInteractable = false; 130 | OverlayController.focusTarget(); 131 | } else { 132 | isInteractable = true; 133 | OverlayController.activateOverlay(); 134 | } 135 | } 136 | 137 | win.on('blur', () => { 138 | console.log('blur'); 139 | isInteractable = false; 140 | OverlayController.focusTarget(); 141 | 142 | win.webContents.send('visibility-change', false); 143 | }) 144 | 145 | ipcMain.on('analyze-item', (msg) => { 146 | console.log('analyze-item'); 147 | 148 | isInteractable = true; 149 | OverlayController.activateOverlay(); 150 | 151 | win.webContents.send('visibility-change', true); 152 | }); 153 | 154 | ipcMain.on('blur', (msg) => { 155 | win.blur(); 156 | }); 157 | 158 | globalShortcut.register(toggleMouseKey, toggleOverlayState); 159 | 160 | globalShortcut.register(toggleShowKey, () => { 161 | win.webContents.send('visibility-change'); 162 | 163 | isInteractable = true; 164 | OverlayController.activateOverlay(); 165 | }) 166 | } 167 | } 168 | 169 | app.whenReady().then(() => { 170 | //自動檢查更新 171 | updateElectronApp(); 172 | 173 | const icon = nativeImage.createFromPath(path.join(__dirname, 'dist/poe2-trade-app/browser/favicon.ico')); 174 | tray = new Tray(icon); 175 | 176 | const contextMenu = Menu.buildFromTemplate([ 177 | { 178 | label: '視窗模式', 179 | type: 'radio', 180 | checked: mode == 'window', 181 | click: () => { 182 | if (mode == 'overlay') { 183 | store.set('mode', 'window'); 184 | 185 | new Notification({ 186 | title: 'POE2 查價重新啟動通知', 187 | body: '將重新啟動切換APP為視窗模式。', 188 | timeoutType: '2000', 189 | icon: `dist/poe2-trade-app/browser/favicon.ico` 190 | }).show(); 191 | 192 | app.relaunch(); 193 | app.quit(); 194 | } 195 | } 196 | }, 197 | { 198 | label: '覆蓋模式', 199 | type: 'radio', 200 | checked: mode == 'overlay', 201 | click: () => { 202 | if (mode == 'window') { 203 | store.set('mode', 'overlay'); 204 | 205 | new Notification({ 206 | title: 'POE2 查價重新啟動通知', 207 | body: '將重新啟動切換APP為覆蓋模式。', 208 | timeoutType: '2000', 209 | icon: `dist/poe2-trade-app/browser/favicon.ico` 210 | }).show(); 211 | 212 | app.relaunch(); 213 | app.quit(); 214 | } 215 | } 216 | }, 217 | { 218 | label: '離開', 219 | click: () => { 220 | app.quit(); 221 | } 222 | } 223 | ]) 224 | 225 | tray.setToolTip('POE2 查價工具 v0.7.10'); 226 | tray.setContextMenu(contextMenu); 227 | 228 | setTimeout( 229 | mode == 'overlay' ? createOverlayWindow : createWindow, 230 | process.platform === 'linux' ? 1000 : 0 // https://github.com/electron/electron/issues/16809 231 | ) 232 | 233 | app.on('activate', () => { 234 | if (BrowserWindow.getAllWindows().length === 0) createWindow() 235 | }) 236 | }) 237 | 238 | app.on('window-all-closed', () => { 239 | if (process.platform !== 'darwin') { 240 | app.quit(); 241 | } 242 | }) 243 | 244 | //更換佈景主題 245 | ipcMain.on('toggle-theme', (event, msg) => { 246 | if (msg === 'dark') { 247 | nativeTheme.themeSource = 'light'; 248 | if (store.get('mode') == 'window') { 249 | win.setBackgroundColor('#ffffffcc'); 250 | } 251 | } else { 252 | nativeTheme.themeSource = 'dark'; 253 | if (store.get('mode') == 'window') { 254 | win.setBackgroundColor('#000000cc'); 255 | } 256 | } 257 | }); 258 | 259 | //取得本地物品資料 260 | ipcMain.on('get-local-items', (event, msg) => { 261 | items = fs.readFileSync(path.join(process.cwd(), '/resources/items.json'), 'utf-8'); 262 | 263 | event.sender.send('reply-local-items', JSON.parse(items)); 264 | }); 265 | 266 | //取得本地詞綴資料 267 | ipcMain.on('get-local-stats', (event, msg) => { 268 | stats = fs.readFileSync(path.join(process.cwd(), '/resources/stats.json'), 'utf-8'); 269 | 270 | event.sender.send('reply-local-stats', JSON.parse(stats)); 271 | }); 272 | 273 | //更新本地物品資料 274 | ipcMain.on('update-local-items', (event, msg) => { 275 | fs.writeFileSync(path.join(process.cwd(), '/resources/items.json'), JSON.stringify(msg)); 276 | }); 277 | 278 | //更新本地詞綴資料 279 | ipcMain.on('update-local-stats', (event, msg) => { 280 | fs.writeFileSync(path.join(process.cwd(), '/resources/stats.json'), JSON.stringify(msg)); 281 | }); 282 | 283 | //取得運作模式 284 | ipcMain.on('get-mode', (event, msg) => { 285 | event.sender.send('reply-mode', store.get('mode')); 286 | }); 287 | 288 | })(); -------------------------------------------------------------------------------- /src/app/home/analyze/analyze.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; 2 | import { AppService } from '../../app.service'; 3 | import { forkJoin } from 'rxjs'; 4 | import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; 5 | 6 | @Component({ 7 | selector: 'app-analyze', 8 | imports: [NgbTooltipModule], 9 | templateUrl: './analyze.component.html', 10 | styleUrl: './analyze.component.scss', 11 | }) 12 | export class AnalyzeComponent implements OnInit, OnChanges { 13 | @Output() isCounting = new EventEmitter(); 14 | @Input({ required: true }) searchResult: any = []; 15 | 16 | // public isLoading = false; 17 | public fetchResult: any = []; //回傳結果 18 | public computed: any = new Map(); //價格統計 19 | // public fetchIndex = 0; 20 | public maxRead = 40; //每次讀取 21 | public itemImage = ''; //物品圖示 22 | public observ: any = []; //紀錄序列 23 | public corruptedCount: number = 0; //汙染統計 24 | public extraFilterCount: number = 0; //額外過濾統計 25 | 26 | public currencysList: any = new Map([ 27 | ["transmute", { 28 | text: "蛻變石", 29 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9NYWdpYyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/2f8e1ff9f8/CurrencyUpgradeToMagic.png" 30 | }], 31 | ["aug", { 32 | text: "增幅石", 33 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRNb2RUb01hZ2ljIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/c8ad0ddc84/CurrencyAddModToMagic.png" 34 | }], 35 | ["regal", { 36 | text: "富豪石", 37 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlTWFnaWNUb1JhcmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/e8fb148e80/CurrencyUpgradeMagicToRare.png" 38 | }], 39 | ["annul", { 40 | text: "無效石", 41 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQW5udWxsT3JiIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/2daba8ccca/AnnullOrb.png" 42 | }], 43 | ["exalted", { 44 | text: "崇高石", 45 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRNb2RUb1JhcmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/ad7c366789/CurrencyAddModToRare.png" 46 | }], 47 | ["chaos", { 48 | text: "混沌石", 49 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lSZXJvbGxSYXJlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/c0ca392a78/CurrencyRerollRare.png" 50 | }], 51 | ["vaal", { 52 | text: "瓦爾寶珠", 53 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lWYWFsIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/72bc84396c/CurrencyVaal.png" 54 | }], 55 | ["alch", { 56 | text: "點金石", 57 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9SYXJlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/9b80b44821/CurrencyUpgradeToRare.png" 58 | }], 59 | ["divine", { 60 | text: "神聖石", 61 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lNb2RWYWx1ZXMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/2986e220b3/CurrencyModValues.png" 62 | }], 63 | ["mirror", { 64 | text: "卡蘭德魔鏡", 65 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lEdXBsaWNhdGUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/26bc31680e/CurrencyDuplicate.png" 66 | }], 67 | ["chance", { 68 | text: "機會石", 69 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9VbmlxdWUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/93c6cc8d5b/CurrencyUpgradeToUnique.png" 70 | }], 71 | ["artificers", { 72 | text: "工匠石", 73 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRFcXVpcG1lbnRTb2NrZXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/5131fd4774/CurrencyAddEquipmentSocket.png" 74 | }], 75 | ["wisdom", { 76 | text: "知識卷軸", 77 | image: "https://webtw.poecdn.com/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lJZGVudGlmaWNhdGlvbiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/884f7bc58b/CurrencyIdentification.png" 78 | }]]); 79 | 80 | constructor(private poe_service: AppService) { 81 | } 82 | 83 | ngOnChanges(changes: SimpleChanges): void { 84 | console.log(changes); 85 | } 86 | 87 | ngOnInit(): void { 88 | this.analyze(); 89 | } 90 | 91 | analyze() { 92 | console.log(this.searchResult); 93 | if (this.searchResult.searchTotal > 0) { 94 | this.fetchResult.length = 0; 95 | this.computed = new Map(); 96 | // let vm = this; 97 | // this.isLoading = true; 98 | 99 | for (let i = 0; i < this.searchResult.fetchID.length && i < this.maxRead; i += 10) { 100 | // this.fetchIndex = i; 101 | const fetchIDs = this.searchResult.fetchID.slice(i, i + 10); 102 | 103 | this.observ.push(this.poe_service.get_trade_fetch(fetchIDs.join(','), this.searchResult.fetchQueryID)); 104 | } 105 | 106 | this.fetchResultPrice(); 107 | // 之後檢查 108 | // let newFilterResult = this.searchResult.fetchID.filter(function (item: any, index: any, array: any) { 109 | // return indexLength > 4 ? index >= 4 && index < 8 && item : index < indexLength; 110 | // }) 111 | // this.poe_service.get_trade_fetch(this.searchResult.fetchID, this.searchResult.fetchQueryID).subscribe((res: any) => { 112 | // if (res) { 113 | // this.fetchResult = this.fetchResult.concat(res.result); 114 | 115 | // console.log(this.fetchResult); 116 | 117 | // this.fetchResultPrice(); 118 | 119 | // this.isLoading = false; 120 | // // this.$emit('scroll') 121 | // } 122 | // }); 123 | } 124 | // this.axios.all(newFilterResult.map((element: any, index: any) => { 125 | // let params = { 126 | // baseUrl: this.baseUrl, 127 | // element, 128 | // fetchQueryID: this.fetchQueryID 129 | // } 130 | // return this.axios.post(`http://localhost:3031/trade_fetch`, params) 131 | // // return this.axios.get(`${this.baseUrl}/api/trade/fetch/${element}?query=${this.fetchQueryID}`) 132 | // })) 133 | // .then(this.axios.spread((...res) => { 134 | // let limitStringArray = [] 135 | // res.forEach((element, index) => { 136 | // this.fetchResult[indexLength > 4 ? index + 4 : index].push(element.data.result) 137 | // let limitString = (element.headers["x-rate-limit-ip-state"]).split(",") 138 | // let limitState = limitString[1].substring(0, limitString[1].indexOf(':')) 139 | // limitStringArray.push(parseInt(limitState, 10)) 140 | // }); 141 | // this.switchLimitState(Math.max(...limitStringArray)) 142 | // if (this.fetchResult[0].length !== 0 && !this.itemImage) { 143 | // this.itemImage = this.fetchResult[0][0][0].item.icon 144 | // } 145 | // this.isLoading = false; 146 | // this.$emit('scroll') 147 | // })) 148 | // .catch(function (error) { 149 | // console.log(error) 150 | // vm.isLoading = false; 151 | // if (error.response.headers) { 152 | // let limitString = (error.response.headers["x-rate-limit-ip-state"]).split(",") 153 | // let limitState = limitString[1].slice(limitString[1].lastIndexOf(":") + 1) 154 | // limitState = parseInt(limitState, 10) 155 | // vm.$message({ 156 | // type: 'error', 157 | // message: `被 Server 限制發送需求了,請等待 ${limitState} 秒後再重試` 158 | // }); 159 | // } 160 | // }) 161 | } 162 | 163 | // switchLimitState(limitState: any) { 164 | // // console.log('PriceAnalysis', limitState) 165 | // switch (limitState) { 166 | // case 12: 167 | // this.$emit('countdown', 4 / 1.33) 168 | // break; 169 | // case 13: 170 | // case 14: 171 | // case 15: 172 | // case 16: 173 | // this.$emit('countdown', 6 / 1.33) 174 | // break; 175 | // default: 176 | // break; 177 | // } 178 | // } 179 | 180 | fetchResultPrice() { 181 | forkJoin([...this.observ]).subscribe((res: any) => { 182 | this.fetchResult = [].concat(...res.map((e: any) => { return e.result })); 183 | 184 | this.itemImage = this.fetchResult[0].item.icon; 185 | 186 | this.fetchResult.forEach((item: any) => { 187 | if (this.searchResult.extraFilterStr !== '' && item.item.explicitMods[0] !== this.searchResult.extraFilterStr) { 188 | return; 189 | } 190 | 191 | if (item.listing.price) { 192 | if (this.computed.has(item.listing.price.currency)) { 193 | if (!this.computed.get(item.listing.price.currency).has(item.listing.price.amount)) { 194 | this.computed.get(item.listing.price.currency).set(item.listing.price.amount, [1, 0]); 195 | } else { 196 | this.computed.get(item.listing.price.currency).set(item.listing.price.amount, [this.computed.get(item.listing.price.currency).get(item.listing.price.amount)[0] + 1, this.computed.get(item.listing.price.currency).get(item.listing.price.amount)[1]]); 197 | } 198 | } else { 199 | this.computed.set(item.listing.price.currency, new Map([ 200 | [item.listing.price.amount, [1, 0]] 201 | ])); 202 | } 203 | } 204 | 205 | if (item.item.corrupted) { 206 | this.computed.get(item.listing.price.currency).set(item.listing.price.amount, [this.computed.get(item.listing.price.currency).get(item.listing.price.amount)[0], this.computed.get(item.listing.price.currency).get(item.listing.price.amount)[1] + 1]); 207 | this.corruptedCount += 1; 208 | } 209 | 210 | this.extraFilterCount += 1; 211 | }); 212 | 213 | console.log(this.computed); 214 | }, (error: any) => { 215 | console.error(error.error.message); 216 | }) 217 | // this.fetchResult.forEach((item: any) => { 218 | // if (item.listing.price) { 219 | // if (this.computed.has(item.listing.price.currency)) { 220 | // if (!this.computed.get(item.listing.price.currency).has(item.listing.price.amount)) { 221 | // this.computed.get(item.listing.price.currency).set(item.listing.price.amount, 1); 222 | // } else { 223 | // console.log(2); 224 | // this.computed.get(item.listing.price.currency).set(item.listing.price.amount, this.computed.get(item.listing.price.currency).get(item.listing.price.amount) + 1); 225 | // } 226 | // } else { 227 | // this.computed.set(item.listing.price.currency, new Map([ 228 | // [item.listing.price.amount, 1] 229 | // ])); 230 | // } 231 | // } 232 | // }); 233 | 234 | // return this.fetchResult.flat(Infinity).map((item: any) => { 235 | // if (!item.gone) { 236 | // return Object.values(item)[1]; 237 | // } 238 | 239 | // return false; 240 | // }).filter(function (item: any, index: any, array: any) { 241 | // return item; // 排除包含 "gone": true 的物品(物品不存在) 242 | // }).map((item: any) => { 243 | // if (item.price) { 244 | // item.price.accountName = new Array 245 | // item.price.accountName[0] = item.account.name // 增加該帳號到陣列中 246 | // } 247 | // return item.price 248 | // }).filter(function (item: any, index: any, array: any) { 249 | // return item; // 排除介面已選擇有標價但 API 還是回傳尚未標價的物品 (未標價 => null) 250 | // }); 251 | 252 | this.isCounting.emit(false); 253 | } 254 | 255 | // fetchResultLength() { 256 | // return Math.ceil(this.fetchResultPrice.length / 10) 257 | // } 258 | 259 | // calResultLength() { // 官方回傳的總數扣除目前搜尋且已整理的數量 260 | // return this.resultLength - this.fetchResultPrice.length 261 | // } 262 | 263 | // collectionRepeat() { 264 | // if (!this.isPriced || !this.fetchResultPrice[0]) { 265 | // return 0 266 | // } 267 | // const result = [...this.fetchResultPrice.reduce((r, e) => { // 計算相同 amount & currency 重複的次數 268 | // let k = `${e.amount}|${e.currency}`; 269 | // if (!r.has(k)) { 270 | // r.set(k, { 271 | // ...e, 272 | // count: 1 273 | // }) 274 | // } else { 275 | // r.get(k).count++ 276 | // if (r.get(k).accountName.indexOf(e.accountName[0]) === -1) { 277 | // r.get(k).accountName.push(e.accountName[0]) // 每筆價格整理內皆包含不重複的使用者帳號名稱 278 | // } 279 | // } 280 | // return r; 281 | // }, new Map).values()] 282 | 283 | // return result 284 | // // return [...new Set(this.fetchResultPrice.map(item => JSON.stringify(item)))].map(item => JSON.parse(item)); // 去除相同 obj 285 | // } 286 | } 287 | -------------------------------------------------------------------------------- /src/app/home/data.ts: -------------------------------------------------------------------------------- 1 | import { lastValueFrom } from "rxjs"; 2 | import { AppService } from "../app.service"; 3 | import { HttpErrorResponse } from "@angular/common/http"; 4 | import { inject } from "@angular/core"; 5 | 6 | export class Data { 7 | private poe_service = inject(AppService); 8 | //原始資料 9 | public datas: any = { 10 | items: [], // 交易網物品 API 資料 11 | stats: [], // 交易網詞綴 API 資料 12 | ranges: [] //詞綴範圍資料 13 | }; 14 | //物品資料 15 | public basics: any = { 16 | categorizedItems: [], // 有分類的物品資料 17 | map: { // 地圖基底 18 | option: [], 19 | chosenM: '無', 20 | isSearch: false, 21 | }, 22 | gem: { // 技能寶石基底 23 | option: [], 24 | chosenG: '無', 25 | isSearch: false, 26 | } 27 | }; 28 | //詞綴資料 29 | public stats: any = { 30 | implicit: [], // 固定屬性 31 | explicit: [], // 隨機屬性 32 | wrap: [], //拆行詞綴 33 | enchant: [], // 附魔詞綴 34 | rune: [], // 增幅詞綴 35 | skill: [], //技能詞綴 36 | allocates: [], // 項鍊塗油配置附魔詞綴 37 | sanctum: [], //聖所詞綴 38 | desecrated: [], //褻瀆詞綴 39 | fractured: [] //破裂詞綴 40 | } 41 | 42 | constructor() { 43 | this.loadData(); 44 | } 45 | 46 | public async loadData() { 47 | let loadlocal = false; 48 | 49 | (window).ipcRenderer.on('reply-local-items', (event: any, arg: any) => { 50 | this.datas.items = arg; 51 | }); 52 | (window).ipcRenderer.on('reply-local-stats', (event: any, arg: any) => { 53 | this.datas.stats = arg; 54 | }); 55 | 56 | const allItems = this.poe_service.getOfficialItemData(); 57 | const allStats = this.poe_service.getOfficialStatesData(); 58 | const allStatsRanges = this.poe_service.getStatsRangesData(); 59 | 60 | this.datas.items = await lastValueFrom(allItems).catch((error: HttpErrorResponse) => { 61 | console.log("error: ", error); 62 | loadlocal = true; 63 | (window).ipcRenderer.send('get-local-items'); 64 | }); 65 | this.datas.stats = await lastValueFrom(allStats).catch((error: HttpErrorResponse) => { 66 | console.log("error: ", error); 67 | loadlocal = true; 68 | (window).ipcRenderer.send('get-local-stats'); 69 | }); 70 | this.datas.ranges = await lastValueFrom(allStatsRanges).then((ranges: any) => ranges.ranges); 71 | 72 | //更新本地資料 73 | if (!loadlocal) { 74 | (window).ipcRenderer.send('update-local-items', this.datas.items); 75 | (window).ipcRenderer.send('update-local-stats', this.datas.stats); 76 | } 77 | 78 | this.dealWithitemsData(); 79 | this.dealWithstatsData(); 80 | } 81 | 82 | //物品格式化 83 | dealWithitemsData() { // 物品 API 84 | let accessoryIndex = 0; //飾品 85 | let armourIndex = 0; //護甲 86 | let flasksIndex = 0; //藥水 87 | let jewelIndex = 0; //珠寶 88 | let weaponIndex = 0; //武器 89 | let mapIndex = 0; //地圖 90 | let sanctumIndex = 0; //聖所 91 | this.basics.categorizedItems.length = 0; 92 | this.basics.map.option.length = 0; 93 | this.basics.gem.option.length = 0; 94 | 95 | let result = this.datas.items.result; 96 | //"id": "accessory", "label": "飾品" 97 | result[result.findIndex((e: any) => e.id === "accessory")].entries.forEach((element: any) => { 98 | const basetype = ["赤紅項鍊", "生皮腰帶", "鍛鐵戒指"]; 99 | 100 | if (basetype.includes(element.type) && !('flags' in element)) { 101 | accessoryIndex += 1; 102 | } 103 | 104 | switch (accessoryIndex) { 105 | case 1: // 項鍊起始點 { "type": "赤紅項鍊", "text": "赤紅項鍊" } 106 | element.name = "項鍊"; 107 | element.option = "accessory.amulet"; 108 | this.basics.categorizedItems.push(element); 109 | break; 110 | case 2: // 腰帶起始點 { "type": "生皮腰帶", "text": "生皮腰帶" } 111 | element.name = "腰帶"; 112 | element.option = "accessory.belt"; 113 | this.basics.categorizedItems.push(element); 114 | break; 115 | case 3: // 戒指起始點 { "type": "鍛鐵戒指", "text": "鍛鐵戒指" } 116 | element.name = "戒指"; 117 | element.option = "accessory.ring"; 118 | this.basics.categorizedItems.push(element); 119 | break; 120 | default: 121 | break; 122 | } 123 | }); 124 | //"id": "armour", "label": "護甲" 125 | result[result.findIndex((e: any) => e.id === "armour")].entries.forEach((element: any) => { 126 | const basetype = ["皮革背心", "生皮長靴", "枝條法器", "麂皮護腕", "黃金面紗", "皮革輕盾", "朽木塔盾", "寬頭箭袋"]; 127 | 128 | if (basetype.includes(element.type) && !('flags' in element)) { 129 | armourIndex += 1; 130 | } 131 | 132 | switch (armourIndex) { 133 | case 1: // 胸甲起始點 { "type": "皮革背心", "text": "皮革背心" } 134 | element.name = "胸甲"; 135 | element.option = "armour.chest"; 136 | this.basics.categorizedItems.push(element); 137 | break; 138 | case 2: // 鞋子起始點 { "type": "生皮長靴", "text": "生皮長靴" } 139 | element.name = "鞋子"; 140 | element.option = "armour.boots"; 141 | this.basics.categorizedItems.push(element); 142 | break; 143 | case 3: // 法器起始點 { "type": "枝條法器", "text": "枝條法器" } 144 | element.name = "法器"; 145 | element.option = "armour.focus"; 146 | this.basics.categorizedItems.push(element); 147 | break; 148 | case 4: // 手套起始點 { "type": "麂皮護腕", "text": "麂皮護腕" } 149 | element.name = "手套"; 150 | element.option = "armour.gloves"; 151 | this.basics.categorizedItems.push(element); 152 | break; 153 | case 5: // 頭盔起始點 { "type": "黃金面紗", "text": "黃金面紗" } 154 | element.name = "頭盔"; 155 | element.option = "armour.helmet"; 156 | this.basics.categorizedItems.push(element); 157 | break; 158 | case 6: // 輕盾起始點 { "type": "皮革輕盾", "text": "皮革輕盾" } 159 | element.name = "輕盾"; 160 | element.option = "armour.buckler"; 161 | this.basics.categorizedItems.push(element); 162 | break; 163 | case 7: // 盾牌起始點 { "type": "朽木塔盾", "text": "朽木塔盾" } 164 | element.name = "盾"; 165 | element.option = "armour.shield"; 166 | this.basics.categorizedItems.push(element); 167 | break; 168 | case 8: // 箭袋起始點 { "type": "寬頭箭袋", "text": "寬頭箭袋" } 169 | element.name = "箭袋"; 170 | element.option = "armour.quiver"; 171 | this.basics.categorizedItems.push(element); 172 | break; 173 | default: 174 | break; 175 | } 176 | }); 177 | //"id": "flask", "label": "藥劑" 178 | result[result.findIndex((e: any) => e.id === "flask")].entries.forEach((element: any) => { 179 | const basetype = ["解凍護符", "低階生命藥劑"]; 180 | 181 | if (basetype.includes(element.type) && !('flags' in element)) { 182 | flasksIndex += 1; 183 | } 184 | 185 | switch (flasksIndex) { 186 | case 1: // 護符起始點 { "type": "解凍護符", "text": "解凍護符" } 187 | element.name = "護符"; 188 | element.option = "flask"; //之後檢查 189 | this.basics.categorizedItems.push(element); 190 | break; 191 | case 2: // 藥劑起始點 { "type": "低階生命藥劑", "text": "低階生命藥劑" } 192 | element.name = "藥劑"; 193 | element.option = "flask"; 194 | this.basics.categorizedItems.push(element); 195 | break; 196 | default: 197 | break; 198 | } 199 | }); 200 | //"id": "jewel", "label": "珠寶" 201 | result[result.findIndex((e: any) => e.id === "jewel")].entries.forEach((element: any) => { 202 | const basetype = ["翠綠碧雲"]; 203 | 204 | if (basetype.includes(element.type) && !('flags' in element)) { 205 | jewelIndex += 1; 206 | } 207 | 208 | switch (jewelIndex) { 209 | case 1: // 珠寶起始點 { "type": "翠綠碧雲", "text": "翠綠碧雲" } 210 | element.name = "珠寶"; 211 | element.option = "jewel"; 212 | this.basics.categorizedItems.push(element); 213 | break; 214 | default: 215 | break; 216 | } 217 | }); 218 | //"id": "weapon", "label": "武器" 219 | result[result.findIndex((e: any) => e.id === "weapon")].entries.forEach((element: any) => { 220 | const basetype = ["分裂鏈錘", "鈍斧", "木製棍棒", "硬木長矛", "闊劍", "雜響權杖", "凋零法杖", "粗製弓", "臨時十字弓", "纏繞細杖", "灰燼長杖", "變形魔符", "分裂巨斧", "墮落巨棍棒", "鍛鐵巨劍"] 221 | 222 | if (basetype.includes(element.type) && !('flags' in element)) { 223 | weaponIndex += 1; 224 | } 225 | 226 | switch (weaponIndex) { 227 | case 1: // 鏈錘起始點 { "type": "分裂鏈錘", "text": "分裂鏈錘" } 228 | element.name = "鏈錘"; 229 | element.option = "weapon.flail"; 230 | element.weapon = "weapon.onemelee" // "weapon.one" 單手武器 231 | this.basics.categorizedItems.push(element); 232 | break; 233 | case 2: // 單手斧起始點 { "type": "鈍斧", "text": "鈍斧" } 234 | element.name = "單手斧"; 235 | element.option = "weapon.oneaxe"; 236 | element.weapon = "weapon.onemelee" // "weapon.one" 單手武器 237 | this.basics.categorizedItems.push(element); 238 | break; 239 | case 3: // 單手錘起始點 { "type": "木製棍棒", "text": "木製棍棒" } 240 | element.name = "單手錘"; 241 | element.option = "weapon.onemace"; 242 | element.weapon = "weapon.onemelee" // "weapon.one" 單手武器 243 | this.basics.categorizedItems.push(element); 244 | break; 245 | case 4: // 長矛起始點 { "type": "硬木長矛", "text": "硬木長矛" } 246 | element.name = "長矛"; 247 | element.option = "weapon.spear"; 248 | element.weapon = "weapon.onemelee" // "weapon.one" 單手武器 249 | this.basics.categorizedItems.push(element); 250 | break; 251 | case 5: // 單手劍起始點 { "type": "闊劍", "text": "闊劍" } 252 | element.name = "單手劍"; 253 | element.option = "weapon.onesword"; 254 | element.weapon = "weapon.onemelee" // "weapon.one" 單手武器 255 | this.basics.categorizedItems.push(element); 256 | break; 257 | case 6: // 權杖起始點 { "type": "雜響權杖", "text": "雜響權杖" } 258 | element.name = "權杖"; 259 | element.option = "weapon.sceptre"; 260 | element.weapon = "weapon.caster"; 261 | this.basics.categorizedItems.push(element); 262 | break; 263 | case 7: // 法杖起始點 { "type": "凋零法杖", "text": "凋零法杖" } 264 | element.name = "法杖"; 265 | element.option = "weapon.wand"; 266 | element.weapon = "weapon.caster"; 267 | this.basics.categorizedItems.push(element); 268 | break; 269 | case 8: // 弓起始點 { "type": "粗製弓", "text": "粗製弓" } 270 | element.name = "弓"; 271 | element.option = "weapon.bow"; 272 | element.weapon = "weapon.ranged"; 273 | // element.weapon = "weapon.one" 274 | this.basics.categorizedItems.push(element); 275 | break; 276 | case 9: // 十字弓起始點 { "type": "鏽劍", "text": "鏽劍" } 277 | element.name = "十字弓"; 278 | element.option = "weapon.crossbow"; 279 | element.weapon = "weapon.ranged"; 280 | // element.weapon = "weapon.one" 281 | this.basics.categorizedItems.push(element); 282 | break; 283 | case 10: // 細杖起始點 { "type": "纏繞細杖", "text": "纏繞細杖" } 284 | element.name = "細杖"; 285 | element.option = "weapon.warstaff"; 286 | element.weapon = "weapon.twomelee"; 287 | this.basics.categorizedItems.push(element); 288 | break; 289 | case 11: // 長杖起始點{ "type": "灰燼長杖", "text": "灰燼長杖" } 290 | element.name = "長杖"; 291 | element.option = "weapon.staff"; 292 | element.weapon = "weapon.caster"; 293 | this.basics.categorizedItems.push(element); 294 | break; 295 | case 12: // 魔符起始點{ "type": "變形魔符", "text": "變形魔符" } 296 | element.name = "魔符"; 297 | element.option = "weapon.talisman"; 298 | element.weapon = "weapon.twomelee"; 299 | this.basics.categorizedItems.push(element); 300 | break; 301 | case 13: // 雙手斧起始點 { "type": "分裂巨斧", "text": "分裂巨斧" } 302 | element.name = "雙手斧"; 303 | element.option = "weapon.twoaxe"; 304 | element.weapon = "weapon.twomelee"; 305 | this.basics.categorizedItems.push(element); 306 | break; 307 | case 14: // 雙手錘起始點 { "type": "墮落巨棍棒", "text": "墮落巨棍棒" } 308 | element.name = "雙手錘"; 309 | element.option = "weapon.twomace"; 310 | element.weapon = "weapon.twomelee"; 311 | this.basics.categorizedItems.push(element); 312 | break; 313 | case 15: // 雙手劍起始點 { "type": "鍛鐵巨劍", "text": "鍛鐵巨劍" } 314 | element.name = "雙手劍"; 315 | element.option = "weapon.twosword"; 316 | element.weapon = "weapon.twomelee"; 317 | this.basics.categorizedItems.push(element); 318 | break; 319 | default: 320 | break; 321 | } 322 | }); 323 | //"id": "maps", "label": "地圖" 324 | result[result.findIndex((e: any) => e.id === "map")].entries.forEach((element: any) => { 325 | const basetype = ["探險日誌", "幻像異界", "地圖鑰匙(階級 1)", "遠古危機碎片", "意志的測試代幣", "巨靈之幣", "裂痕碑牌", "最後通牒雕刻", "怯懦之運"] // 地圖起始點 { "type": "探險日誌", "text": "探險日誌" } 326 | 327 | if (basetype.includes(element.type) && !('flags' in element)) { 328 | mapIndex += 1; 329 | } 330 | 331 | switch (mapIndex) { 332 | case 1: // 日誌起始點 { "type": "探險日誌", "text": "探險日誌" } 333 | element.name = "日誌"; 334 | element.option = "map.logbook"; 335 | this.basics.categorizedItems.push(element); 336 | break; 337 | case 2: // 終局物品起始點 { "type": "幻像異界", "text": "幻像異界" } 338 | element.name = "終局物品"; 339 | element.option = "map"; 340 | this.basics.categorizedItems.push(element); 341 | break; 342 | case 3: // 地圖起始點 { "type": "地圖鑰匙(階級 1)", "text": "地圖鑰匙(階級 1)" } 343 | this.basics.map.option.push(element.type); 344 | break; 345 | case 4: // 地圖碎片起始點 { "type": "遠古危機碎片", "text": "遠古危機碎片" } 346 | // element.name = "地圖碎片"; 347 | // element.option = "map.fragment"; 348 | // this.basics.categorizedItems.push(element); 349 | this.basics.map.option.push(element.type); 350 | break; 351 | case 5: // 巔峰鑰匙起始點 { "type": "意志的測試代幣", "text": "意志的測試代幣" } //之後檢查 352 | element.name = "巔峰鑰匙"; 353 | element.option = "map.bosskey"; 354 | this.basics.categorizedItems.push(element); 355 | break; 356 | case 6: // 巨靈之幣起始點 { "type": "巨靈之幣", "text": "巨靈之幣" } 357 | // element.name = "巨靈之幣"; 358 | // element.option = "map.barya"; 359 | // this.basics.categorizedItems.push(element); 360 | this.basics.map.option.push(element.type); 361 | break; 362 | case 7: // 碑牌日誌起始點 { "type": "裂痕碑牌", "text": "裂痕碑牌" } 363 | element.name = "碑牌"; 364 | element.option = "map.tablet"; 365 | this.basics.categorizedItems.push(element); 366 | break; 367 | case 8: // 通牒鑰匙起始點 { "type": "最後通牒雕刻", "text": "最後通牒雕刻" } 368 | // element.name = "通牒鑰匙"; 369 | // element.option = "map.ultimatum"; 370 | // this.basics.categorizedItems.push(element); 371 | this.basics.map.option.push(element.type); 372 | break; 373 | case 9: // 地圖碎片起始點 { "type": "怯懦之運", "text": "怯懦之運" } 374 | element.name = "地圖碎片"; 375 | element.option = "map.fragment"; 376 | this.basics.categorizedItems.push(element); 377 | break; 378 | } 379 | 380 | // if (element.type.type.indexOf("探險日誌") > -1 || element.type.type.indexOf("地圖")) { 381 | // this.basics.map.option.push(element.type); 382 | // } else { 383 | // this.basics.categorizedItems.push(element); 384 | // } 385 | }); 386 | //"id": "gems", "label": "技能寶石" 387 | result[result.findIndex((e: any) => e.id === "gem")].entries.forEach((element: any) => { 388 | this.basics.gem.option.push(element.text); 389 | }); 390 | //"id": "sanctum" 391 | result[result.findIndex((e: any) => e.id === "sanctum")].entries.forEach((element: any) => { 392 | const basetype = ["古甕聖物"]; 393 | 394 | if (basetype.includes(element.type) && !('flags' in element)) { 395 | sanctumIndex += 1; 396 | } 397 | 398 | switch (sanctumIndex) { 399 | case 1: // 聖物起始點 { "type": "古甕聖物", "text": "古甕聖物" } 400 | element.name = "聖物"; 401 | element.option = "sanctum.relic"; 402 | this.basics.categorizedItems.push(element); 403 | break; 404 | // case 2: // 聖域研究起始點 { "type": "聖域寶庫研究", "text": "聖域寶庫研究" } 405 | // element.name = "聖域研究"; 406 | // element.option = "sanctum.research"; 407 | // this.options.mapBasic.option.push(element.type); 408 | // break; 409 | default: 410 | break; 411 | } 412 | }); 413 | 414 | //清除資料 415 | this.datas.items = []; 416 | } 417 | 418 | //詞綴格式化 419 | dealWithstatsData() { 420 | let result = this.datas.stats.result; 421 | //隨機屬性 422 | result[result.findIndex((e: any) => e.id === "explicit")].entries.forEach((element: any, index: any) => { 423 | let text = element.text; 424 | let count = (text.match(/\|/g) || []).length; 425 | //處理說明字串 426 | if (count > 0) { 427 | text = this.replaceIllustrate(text, count); 428 | } 429 | //處理折行詞綴 430 | if (text.includes('\n')) { 431 | this.stats.wrap.push(text); 432 | } 433 | 434 | this.stats.explicit.push(text, element.id); 435 | }) 436 | //固定屬性 437 | result[result.findIndex((e: any) => e.id === "implicit")].entries.forEach((element: any, index: any) => { 438 | let text = element.text; 439 | let count = (text.match(/\|/g) || []).length; 440 | //處理說明字串 441 | if (count > 0) { 442 | text = this.replaceIllustrate(text, count); 443 | } 444 | //處理折行詞綴 445 | if (text.includes('\n')) { 446 | this.stats.wrap.push(text); 447 | } 448 | 449 | this.stats.implicit.push(text, element.id) 450 | }) 451 | //附魔詞綴 452 | result[result.findIndex((e: any) => e.id === "enchant")].entries.forEach((element: any, index: any) => { 453 | let text = element.text; 454 | // if (element.id === "enchant.stat_2954116742") { // 項鍊塗油配置附魔詞綴 455 | // element.option.options.forEach((element, index) => { 456 | // this.allocatesStats.push(element.text, (element.id).toString()) 457 | // }) 458 | // } 459 | let count = (text.match(/\|/g) || []).length; 460 | //處理說明字串 461 | if (count > 0) { 462 | text = this.replaceIllustrate(text, count); 463 | } 464 | //處理折行詞綴 465 | if (text.includes('\n')) { 466 | this.stats.wrap.push(text); 467 | } 468 | 469 | this.stats.enchant.push(text, element.id); 470 | }) 471 | //增幅詞綴 472 | result[result.findIndex((e: any) => e.id === "rune")].entries.forEach((element: any, index: any) => { 473 | let text = element.text; 474 | let count = (text.match(/\|/g) || []).length; 475 | //處理說明字串 476 | if (count > 0) { 477 | text = this.replaceIllustrate(text, count); 478 | } 479 | //處理折行詞綴 480 | if (text.includes('\n')) { 481 | this.stats.wrap.push(text); 482 | } 483 | 484 | this.stats.rune.push(text, element.id); 485 | }) 486 | //聖域 487 | result[result.findIndex((e: any) => e.id === "sanctum")].entries.forEach((element: any, index: any) => { 488 | let text = element.text; 489 | let count = (text.match(/\|/g) || []).length; 490 | //處理說明字串 491 | if (count > 0) { 492 | text = this.replaceIllustrate(text, count); 493 | } 494 | 495 | this.stats.sanctum.push(text, element.id); 496 | }) 497 | //技能詞綴 498 | result[result.findIndex((e: any) => e.id === "skill")].entries.forEach((element: any, index: any) => { 499 | let text = element.text; 500 | //處理折行詞綴 501 | if (text.includes('\n')) { 502 | this.stats.wrap.push(text); 503 | } 504 | 505 | this.stats.skill.push(text, element.id); 506 | }) 507 | //褻瀆詞綴 508 | result[result.findIndex((e: any) => e.id === "desecrated")].entries.forEach((element: any, index: any) => { 509 | let text = element.text; 510 | //處理折行詞綴 511 | if (text.includes('\n')) { 512 | this.stats.wrap.push(text); 513 | } 514 | 515 | this.stats.desecrated.push(text, element.id); 516 | }) 517 | //破裂詞綴 518 | result[result.findIndex((e: any) => e.id === "fractured")].entries.forEach((element: any, index: any) => { 519 | let text = element.text; 520 | //處理折行詞綴 521 | if (text.includes('\n')) { 522 | this.stats.wrap.push(text); 523 | } 524 | 525 | this.stats.fractured.push(text, element.id); 526 | }) 527 | 528 | //清除資料 529 | this.datas.stats = []; 530 | } 531 | 532 | //取代說明字樣 533 | replaceIllustrate(text: any, count: any) { 534 | for (let i = 0; i < count; i++) { 535 | let poS = text.indexOf("["); 536 | let poM = text.indexOf("|"); 537 | let poE = text.indexOf("]"); 538 | 539 | if (poM !== -1 && poE > poM && (poM - poS < 40)) { 540 | let fullText = text.substring(poS, poE + 1); 541 | let replace = text.substring(poM + 1, poE); 542 | 543 | // console.log(poS, poM, poE, fullText, replace); 544 | 545 | text = text.replace(fullText, replace); 546 | } 547 | } 548 | 549 | return text; 550 | } 551 | } -------------------------------------------------------------------------------- /src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
7 |
8 |
9 | 14 |
15 |
16 | 22 |
23 |
24 |
25 |
26 |
27 | 34 | 35 |
36 |
37 |
38 |
39 | 55 |
56 |
57 | 64 | 65 |
66 |
67 |
68 |
69 | 76 | 77 |
78 |
79 |
80 |
81 |
82 |
83 | 84 |
85 |
86 |
87 |
88 |
89 |
90 | 93 | 94 |
95 |
96 |
97 | 100 |
101 |
102 | 105 |
106 |
107 |
108 | 111 | 112 |
113 |
114 |
115 | 123 |
124 |
125 |
126 |
127 |
128 | 132 | 134 |
135 |
136 |
137 | 145 |
146 |
147 |
148 | 151 | 152 |
153 |
154 |
155 | 157 |
158 |
159 |
160 |
161 |
162 | 163 |
164 |
165 |
166 |
167 |
168 |
169 | 172 | 173 |
174 |
175 |
176 | 179 |
180 |
181 | 184 |
185 |
186 |
187 | 191 | 192 |
193 |
194 |
195 | 203 |
204 |
205 |
206 |
207 |
208 | 209 |
210 |
211 |
212 |
213 |
214 |
215 | 218 | 219 |
220 |
221 |
222 | 225 |
226 |
227 | 230 |
231 |
232 |
233 | 237 | 238 |
239 |
240 |
241 | 244 |
245 |
246 | 249 |
250 |
251 |
252 |
253 |
254 | 257 | 258 |
259 |
260 |
261 | 264 |
265 |
266 | 269 |
270 |
271 |
272 |
273 |
274 | 275 | @if(item.name !== ''){ 276 |
277 |
280 |
281 | } 282 | @if(item.searchStats.length > 0 || item.searchDefences.length){ 283 |
284 |
285 |
286 |
287 |
288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | @for (defence of item.searchDefences; track $index) { 301 | 302 | 315 | 320 | 326 | 333 | 340 | 341 | } 342 | 343 | @if( searchOptions.itemSocket.min > 0) { 344 | 345 | 358 | 361 | 365 | 372 | 379 | 380 | } 381 | 382 | @for (stat of item.searchStats; track $index) { 383 | 384 | 402 | 407 | 419 | 426 | 433 | 434 | } 435 |
查詢種類詞綴內容最小值最大值
304 |
305 |
306 | 309 | 312 |
313 |
314 |
317 | {{ 318 | defence.type }} 319 | 323 | {{ 324 | defence.text }} 325 | 328 |
329 | 331 |
332 |
335 |
336 | 338 |
339 |
347 |
348 |
349 | 353 | 355 |
356 |
357 |
360 | 插槽 364 | 增幅插槽數量 367 |
368 | 370 |
371 |
374 |
375 | 377 |
378 |
386 | @if (stat.id !== '') { 387 |
388 |
389 | 392 | 395 |
396 |
397 | } 398 | @else { 399 | ? 400 | } 401 |
404 | {{ 405 | stat.type }} 406 | 410 |
{{ stat.text }}
411 | @if (stat.rangeMin != null && item.category !== 'unique'){ 412 | 417 | } 418 |
421 |
422 | 424 |
425 |
428 |
429 | 431 |
432 |
436 |
437 |
438 |
439 |
440 |
441 | } 442 |
443 | 444 | @if (item.category) { 445 | 448 | } 449 | 450 |
451 |
452 | @if (searchResult.status !== ''){ 453 |
454 | } 455 | 456 | @if (app.apiErrorStr !== '' && app.isApiError){ 457 | 458 | API 錯誤:{{ app.apiErrorStr }} 459 | 460 | } 461 | 464 | @if (searchResult.status !== '') { 465 |
467 |
468 |
470 | @if (searchResult.searchTotal > 0){ 471 | 472 | } 473 | } -------------------------------------------------------------------------------- /public/poe2/filters.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": [ 3 | { 4 | "id": "status_filters", 5 | "filters": [ 6 | { 7 | "id": "status", 8 | "option": { 9 | "options": [ 10 | { 11 | "id": "available", 12 | "text": "即刻購買以及面對面交易" 13 | }, 14 | { 15 | "id": "securable", 16 | "text": "即刻購買" 17 | }, 18 | { 19 | "id": "onlineleague", 20 | "text": "面對面交易(聯盟在線)" 21 | }, 22 | { 23 | "id": "online", 24 | "text": "面對面交易(在線)" 25 | }, 26 | { 27 | "id": "any", 28 | "text": "任何" 29 | } 30 | ] 31 | } 32 | } 33 | ] 34 | }, 35 | { 36 | "id": "type_filters", 37 | "title": "類別過濾", 38 | "filters": [ 39 | { 40 | "id": "category", 41 | "text": "道具分類", 42 | "fullSpan": true, 43 | "option": { 44 | "options": [ 45 | { 46 | "id": null, 47 | "text": "任何" 48 | }, 49 | { 50 | "id": "weapon", 51 | "text": "武器" 52 | }, 53 | { 54 | "id": "weapon.onemelee", 55 | "text": "任何單手近戰武器" 56 | }, 57 | { 58 | "id": "weapon.unarmed", 59 | "text": "空手" 60 | }, 61 | { 62 | "id": "weapon.claw", 63 | "text": "爪" 64 | }, 65 | { 66 | "id": "weapon.dagger", 67 | "text": "匕首" 68 | }, 69 | { 70 | "id": "weapon.onesword", 71 | "text": "單手劍" 72 | }, 73 | { 74 | "id": "weapon.oneaxe", 75 | "text": "單手斧" 76 | }, 77 | { 78 | "id": "weapon.onemace", 79 | "text": "單手錘" 80 | }, 81 | { 82 | "id": "weapon.spear", 83 | "text": "長鋒" 84 | }, 85 | { 86 | "id": "weapon.flail", 87 | "text": "鏈錘" 88 | }, 89 | { 90 | "id": "weapon.twomelee", 91 | "text": "任何雙手近戰武器" 92 | }, 93 | { 94 | "id": "weapon.twosword", 95 | "text": "雙手劍" 96 | }, 97 | { 98 | "id": "weapon.twoaxe", 99 | "text": "雙手斧" 100 | }, 101 | { 102 | "id": "weapon.twomace", 103 | "text": "雙手錘" 104 | }, 105 | { 106 | "id": "weapon.warstaff", 107 | "text": "細杖" 108 | }, 109 | { 110 | "id": "weapon.talisman", 111 | "text": "魔符聯盟(標準)" 112 | }, 113 | { 114 | "id": "weapon.ranged", 115 | "text": "任何遠程武器" 116 | }, 117 | { 118 | "id": "weapon.bow", 119 | "text": "弓" 120 | }, 121 | { 122 | "id": "weapon.crossbow", 123 | "text": "十字弓" 124 | }, 125 | { 126 | "id": "weapon.caster", 127 | "text": "任何法術武器" 128 | }, 129 | { 130 | "id": "weapon.wand", 131 | "text": "法杖" 132 | }, 133 | { 134 | "id": "weapon.sceptre", 135 | "text": "權杖" 136 | }, 137 | { 138 | "id": "weapon.staff", 139 | "text": "長杖" 140 | }, 141 | { 142 | "id": "weapon.rod", 143 | "text": "釣竿" 144 | }, 145 | { 146 | "id": "armour", 147 | "text": "任意護甲" 148 | }, 149 | { 150 | "id": "armour.helmet", 151 | "text": "頭盔" 152 | }, 153 | { 154 | "id": "armour.chest", 155 | "text": "胸甲" 156 | }, 157 | { 158 | "id": "armour.gloves", 159 | "text": "手套" 160 | }, 161 | { 162 | "id": "armour.boots", 163 | "text": "鞋子" 164 | }, 165 | { 166 | "id": "armour.quiver", 167 | "text": "箭袋" 168 | }, 169 | { 170 | "id": "armour.shield", 171 | "text": "盾" 172 | }, 173 | { 174 | "id": "armour.focus", 175 | "text": "法器" 176 | }, 177 | { 178 | "id": "armour.buckler", 179 | "text": "輕盾" 180 | }, 181 | { 182 | "id": "accessory", 183 | "text": "配件" 184 | }, 185 | { 186 | "id": "accessory.amulet", 187 | "text": "項鍊" 188 | }, 189 | { 190 | "id": "accessory.belt", 191 | "text": "腰帶" 192 | }, 193 | { 194 | "id": "accessory.ring", 195 | "text": "戒指" 196 | }, 197 | { 198 | "id": "gem", 199 | "text": "任何寶石" 200 | }, 201 | { 202 | "id": "gem.activegem", 203 | "text": "技能寶石" 204 | }, 205 | { 206 | "id": "gem.supportgem", 207 | "text": "輔助寶石" 208 | }, 209 | { 210 | "id": "gem.metagem", 211 | "text": "主要寶石" 212 | }, 213 | { 214 | "id": "jewel", 215 | "text": "任何珠寶" 216 | }, 217 | { 218 | "id": "flask", 219 | "text": "任何藥劑" 220 | }, 221 | { 222 | "id": "flask.life", 223 | "text": "生命藥劑" 224 | }, 225 | { 226 | "id": "flask.mana", 227 | "text": "魔力藥劑" 228 | }, 229 | { 230 | "id": "map", 231 | "text": "任何終局物品" 232 | }, 233 | { 234 | "id": "map.waystone", 235 | "text": "換界石" 236 | }, 237 | { 238 | "id": "map.fragment", 239 | "text": "地圖碎片" 240 | }, 241 | { 242 | "id": "map.logbook", 243 | "text": "日誌" 244 | }, 245 | { 246 | "id": "map.breachstone", 247 | "text": "裂痕石" 248 | }, 249 | { 250 | "id": "map.barya", 251 | "text": "巨靈之幣" 252 | }, 253 | { 254 | "id": "map.bosskey", 255 | "text": "巔峰鑰匙" 256 | }, 257 | { 258 | "id": "map.ultimatum", 259 | "text": "通牒鑰匙" 260 | }, 261 | { 262 | "id": "map.tablet", 263 | "text": "碑牌" 264 | }, 265 | { 266 | "id": "card", 267 | "text": "命運卡" 268 | }, 269 | { 270 | "id": "sanctum.relic", 271 | "text": "古典" 272 | }, 273 | { 274 | "id": "currency", 275 | "text": "任何通貨" 276 | }, 277 | { 278 | "id": "currency.omen", 279 | "text": "預兆" 280 | }, 281 | { 282 | "id": "currency.socketable", 283 | "text": "任何增幅" 284 | }, 285 | { 286 | "id": "currency.rune", 287 | "text": "符文" 288 | }, 289 | { 290 | "id": "currency.soulcore", 291 | "text": "靈魂核心" 292 | }, 293 | { 294 | "id": "currency.idol", 295 | "text": "魔偶" 296 | } 297 | ] 298 | } 299 | }, 300 | { 301 | "id": "rarity", 302 | "text": "物品稀有度", 303 | "fullSpan": true, 304 | "option": { 305 | "options": [ 306 | { 307 | "id": null, 308 | "text": "任何" 309 | }, 310 | { 311 | "id": "normal", 312 | "text": "一般" 313 | }, 314 | { 315 | "id": "magic", 316 | "text": "魔法" 317 | }, 318 | { 319 | "id": "rare", 320 | "text": "稀有" 321 | }, 322 | { 323 | "id": "unique", 324 | "text": "傳奇" 325 | }, 326 | { 327 | "id": "uniquefoil", 328 | "text": "傳奇 (貼模)" 329 | }, 330 | { 331 | "id": "nonunique", 332 | "text": "非傳奇道具" 333 | } 334 | ] 335 | } 336 | }, 337 | { 338 | "id": "ilvl", 339 | "text": "物品等級", 340 | "minMax": true 341 | }, 342 | { 343 | "id": "quality", 344 | "text": "道具品質", 345 | "minMax": true 346 | } 347 | ] 348 | }, 349 | { 350 | "id": "equipment_filters", 351 | "title": "裝備篩選器", 352 | "hidden": true, 353 | "filters": [ 354 | { 355 | "id": "damage", 356 | "text": "傷害", 357 | "minMax": true 358 | }, 359 | { 360 | "id": "aps", 361 | "text": "每秒攻擊速度", 362 | "minMax": true 363 | }, 364 | { 365 | "id": "crit", 366 | "text": "暴擊機率", 367 | "minMax": true 368 | }, 369 | { 370 | "id": "dps", 371 | "text": "每秒傷害", 372 | "minMax": true 373 | }, 374 | { 375 | "id": "pdps", 376 | "text": "物理傷害", 377 | "minMax": true 378 | }, 379 | { 380 | "id": "edps", 381 | "text": "元素傷害", 382 | "minMax": true 383 | }, 384 | { 385 | "id": "reload_time", 386 | "text": "重新載入時間", 387 | "minMax": true, 388 | "halfSpan": true 389 | }, 390 | { 391 | "id": "ar", 392 | "text": "護甲", 393 | "tip": "包含基礎值、區域詞綴和最大品質", 394 | "minMax": true 395 | }, 396 | { 397 | "id": "ev", 398 | "text": "閃避", 399 | "tip": "包含基礎值、區域詞綴和最大品質", 400 | "minMax": true 401 | }, 402 | { 403 | "id": "es", 404 | "text": "能量護盾", 405 | "tip": "包含基礎值、區域詞綴和最大品質", 406 | "minMax": true 407 | }, 408 | { 409 | "id": "block", 410 | "text": "格擋", 411 | "tip": "包含基礎值和區域詞綴", 412 | "minMax": true 413 | }, 414 | { 415 | "id": "spirit", 416 | "text": "精魂", 417 | "tip": "包含基礎值、區域詞綴和最大品質", 418 | "minMax": true 419 | }, 420 | { 421 | "id": "rune_sockets", 422 | "text": "可增幅插槽", 423 | "minMax": true 424 | } 425 | ] 426 | }, 427 | { 428 | "id": "req_filters", 429 | "title": "物品需求", 430 | "hidden": true, 431 | "filters": [ 432 | { 433 | "id": "lvl", 434 | "text": "等級", 435 | "minMax": true 436 | }, 437 | { 438 | "id": "str", 439 | "text": "力量", 440 | "minMax": true 441 | }, 442 | { 443 | "id": "dex", 444 | "text": "敏捷", 445 | "minMax": true 446 | }, 447 | { 448 | "id": "int", 449 | "text": "智慧", 450 | "minMax": true 451 | } 452 | ] 453 | }, 454 | { 455 | "id": "map_filters", 456 | "title": "終局篩選器", 457 | "hidden": true, 458 | "filters": [ 459 | { 460 | "id": "map_tier", 461 | "text": "換界石階級", 462 | "minMax": true 463 | }, 464 | { 465 | "id": "map_packsize", 466 | "text": "換界石怪物群大小", 467 | "minMax": true 468 | }, 469 | { 470 | "id": "map_iiq", 471 | "text": "換界石物品數量", 472 | "tip": "物品數量增加", 473 | "minMax": true 474 | }, 475 | { 476 | "id": "map_iir", 477 | "text": "換界石物品稀有度", 478 | "tip": "物品稀有度增加", 479 | "minMax": true 480 | }, 481 | { 482 | "id": "map_revives", 483 | "text": "換界石復活次數", 484 | "minMax": true 485 | }, 486 | { 487 | "id": "map_bonus", 488 | "text": "換界石掉落機率", 489 | "minMax": true 490 | }, 491 | { 492 | "id": "map_gold", 493 | "text": "換界石金幣數量", 494 | "minMax": true 495 | }, 496 | { 497 | "id": "map_experience", 498 | "text": "換界石經驗值獲取", 499 | "minMax": true 500 | }, 501 | { 502 | "id": "map_magic_monsters", 503 | "text": "換界石魔法怪物數量", 504 | "minMax": true 505 | }, 506 | { 507 | "id": "map_rare_monsters", 508 | "text": "換界石稀有怪物數量", 509 | "minMax": true 510 | }, 511 | { 512 | "id": "ultimatum_hint", 513 | "text": "最後通牒試煉提示", 514 | "option": { 515 | "options": [ 516 | { 517 | "id": null, 518 | "text": "任何" 519 | }, 520 | { 521 | "id": "Victorious", 522 | "text": "勝利" 523 | }, 524 | { 525 | "id": "Cowardly", 526 | "text": "怯懦" 527 | }, 528 | { 529 | "id": "Deadly", 530 | "text": "致命" 531 | } 532 | ] 533 | } 534 | } 535 | ] 536 | }, 537 | { 538 | "id": "misc_filters", 539 | "title": "其它", 540 | "hidden": true, 541 | "filters": [ 542 | { 543 | "id": "gem_level", 544 | "text": "寶石等級", 545 | "minMax": true 546 | }, 547 | { 548 | "id": "gem_sockets", 549 | "text": "寶石插槽", 550 | "minMax": true 551 | }, 552 | { 553 | "id": "area_level", 554 | "text": "區域等級", 555 | "minMax": true 556 | }, 557 | { 558 | "id": "stack_size", 559 | "text": "堆疊數量", 560 | "minMax": true 561 | }, 562 | { 563 | "id": "identified", 564 | "text": "已鑑定", 565 | "option": { 566 | "options": [ 567 | { 568 | "id": null, 569 | "text": "任何" 570 | }, 571 | { 572 | "id": "true", 573 | "text": "是" 574 | }, 575 | { 576 | "id": "false", 577 | "text": "否" 578 | } 579 | ] 580 | } 581 | }, 582 | { 583 | "id": "fractured_item", 584 | "text": "破裂", 585 | "option": { 586 | "options": [ 587 | { 588 | "id": null, 589 | "text": "任何" 590 | }, 591 | { 592 | "id": "true", 593 | "text": "是" 594 | }, 595 | { 596 | "id": "false", 597 | "text": "否" 598 | } 599 | ] 600 | } 601 | }, 602 | { 603 | "id": "corrupted", 604 | "text": "已汙染", 605 | "option": { 606 | "options": [ 607 | { 608 | "id": null, 609 | "text": "任何" 610 | }, 611 | { 612 | "id": "true", 613 | "text": "是" 614 | }, 615 | { 616 | "id": "false", 617 | "text": "否" 618 | } 619 | ] 620 | } 621 | }, 622 | { 623 | "id": "sanctified", 624 | "text": "聖化", 625 | "option": { 626 | "options": [ 627 | { 628 | "id": null, 629 | "text": "任何" 630 | }, 631 | { 632 | "id": "true", 633 | "text": "是" 634 | }, 635 | { 636 | "id": "false", 637 | "text": "否" 638 | } 639 | ] 640 | } 641 | }, 642 | { 643 | "id": "twice_corrupted", 644 | "text": "雙重腐化", 645 | "option": { 646 | "options": [ 647 | { 648 | "id": null, 649 | "text": "任何" 650 | }, 651 | { 652 | "id": "true", 653 | "text": "是" 654 | }, 655 | { 656 | "id": "false", 657 | "text": "否" 658 | } 659 | ] 660 | } 661 | }, 662 | { 663 | "id": "mutated", 664 | "text": "已培育的瓦爾傳奇", 665 | "option": { 666 | "options": [ 667 | { 668 | "id": null, 669 | "text": "任何" 670 | }, 671 | { 672 | "id": "true", 673 | "text": "是" 674 | }, 675 | { 676 | "id": "false", 677 | "text": "否" 678 | } 679 | ] 680 | } 681 | }, 682 | { 683 | "id": "veiled", 684 | "text": "未揭露", 685 | "option": { 686 | "options": [ 687 | { 688 | "id": null, 689 | "text": "任何" 690 | }, 691 | { 692 | "id": "true", 693 | "text": "是" 694 | }, 695 | { 696 | "id": "false", 697 | "text": "否" 698 | } 699 | ] 700 | } 701 | }, 702 | { 703 | "id": "desecrated", 704 | "text": "褻瀆", 705 | "option": { 706 | "options": [ 707 | { 708 | "id": null, 709 | "text": "任何" 710 | }, 711 | { 712 | "id": "true", 713 | "text": "是" 714 | }, 715 | { 716 | "id": "false", 717 | "text": "否" 718 | } 719 | ] 720 | } 721 | }, 722 | { 723 | "id": "foreseeing", 724 | "text": "預見", 725 | "option": { 726 | "options": [ 727 | { 728 | "id": null, 729 | "text": "任何" 730 | }, 731 | { 732 | "id": "true", 733 | "text": "是" 734 | }, 735 | { 736 | "id": "false", 737 | "text": "否" 738 | } 739 | ] 740 | } 741 | }, 742 | { 743 | "id": "mirrored", 744 | "text": "已複製", 745 | "option": { 746 | "options": [ 747 | { 748 | "id": null, 749 | "text": "任何" 750 | }, 751 | { 752 | "id": "true", 753 | "text": "是" 754 | }, 755 | { 756 | "id": "false", 757 | "text": "否" 758 | } 759 | ] 760 | } 761 | }, 762 | { 763 | "id": "sanctum_gold", 764 | "text": "巨靈之幣神聖之水", 765 | "minMax": true 766 | }, 767 | { 768 | "id": "unidentified_tier", 769 | "text": "未鑑定階級", 770 | "minMax": true 771 | } 772 | ] 773 | }, 774 | { 775 | "id": "trade_filters", 776 | "title": "交易過濾", 777 | "hidden": true, 778 | "filters": [ 779 | { 780 | "id": "account", 781 | "text": "賣家帳號", 782 | "fullSpan": true, 783 | "input": { 784 | "placeholder": "輸入帳號名稱..." 785 | } 786 | }, 787 | { 788 | "id": "collapse", 789 | "text": "透過帳號摺疊名單", 790 | "fullSpan": true, 791 | "option": { 792 | "options": [ 793 | { 794 | "id": null, 795 | "text": "否" 796 | }, 797 | { 798 | "id": "true", 799 | "text": "是" 800 | } 801 | ] 802 | } 803 | }, 804 | { 805 | "id": "indexed", 806 | "text": "標示", 807 | "fullSpan": true, 808 | "option": { 809 | "options": [ 810 | { 811 | "id": null, 812 | "text": "任何時間" 813 | }, 814 | { 815 | "id": "1hour", 816 | "text": "1 小時前" 817 | }, 818 | { 819 | "id": "3hours", 820 | "text": "3 小時前" 821 | }, 822 | { 823 | "id": "12hours", 824 | "text": "12 小時前" 825 | }, 826 | { 827 | "id": "1day", 828 | "text": "至多 1 天前" 829 | }, 830 | { 831 | "id": "3days", 832 | "text": "至多 3 天前" 833 | }, 834 | { 835 | "id": "1week", 836 | "text": "至多 1 個禮拜前" 837 | }, 838 | { 839 | "id": "2weeks", 840 | "text": "至多 2 個禮拜前" 841 | }, 842 | { 843 | "id": "1month", 844 | "text": "至多 1 個月前" 845 | }, 846 | { 847 | "id": "2months", 848 | "text": "至多 2 個月前" 849 | } 850 | ] 851 | } 852 | }, 853 | { 854 | "id": "sale_type", 855 | "text": "販售型式", 856 | "fullSpan": true, 857 | "option": { 858 | "options": [ 859 | { 860 | "id": "any", 861 | "text": "任何" 862 | }, 863 | { 864 | "id": null, 865 | "text": "直購價或定價" 866 | }, 867 | { 868 | "id": "priced_with_info", 869 | "text": "標價:" 870 | }, 871 | { 872 | "id": "unpriced", 873 | "text": "無定價" 874 | } 875 | ] 876 | } 877 | }, 878 | { 879 | "id": "fee", 880 | "text": "金幣手續費", 881 | "fullSpan": true, 882 | "minMax": true 883 | }, 884 | { 885 | "id": "price", 886 | "text": "直購價", 887 | "fullSpan": true, 888 | "option": { 889 | "options": [ 890 | { 891 | "id": null, 892 | "text": "等同崇高石" 893 | }, 894 | { 895 | "id": "exalted_divine", 896 | "text": "崇高石或神聖石" 897 | }, 898 | { 899 | "id": "aug", 900 | "text": "增幅石" 901 | }, 902 | { 903 | "id": "transmute", 904 | "text": "蛻變石" 905 | }, 906 | { 907 | "id": "exalted", 908 | "text": "崇高石" 909 | }, 910 | { 911 | "id": "regal", 912 | "text": "富豪石" 913 | }, 914 | { 915 | "id": "chaos", 916 | "text": "混沌石" 917 | }, 918 | { 919 | "id": "vaal", 920 | "text": "瓦爾寶珠" 921 | }, 922 | { 923 | "id": "alch", 924 | "text": "點金石" 925 | }, 926 | { 927 | "id": "divine", 928 | "text": "神聖石" 929 | }, 930 | { 931 | "id": "annul", 932 | "text": "無效石" 933 | }, 934 | { 935 | "id": "mirror", 936 | "text": "卡蘭德魔鏡" 937 | } 938 | ] 939 | }, 940 | "minMax": true 941 | } 942 | ] 943 | } 944 | ] 945 | } -------------------------------------------------------------------------------- /public/poe2/static.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": [ 3 | { 4 | "id": "Currency", 5 | "label": "通貨", 6 | "entries": [ 7 | { 8 | "id": "transmutation-shard", 9 | "text": "蛻變石碎片", 10 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9NYWdpY1NoYXJkIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/e67bca229b/CurrencyUpgradeToMagicShard.png" 11 | }, 12 | { 13 | "id": "chance-shard", 14 | "text": "機率碎片", 15 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9VbmlxdWVTaGFyZCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/9828a70270/CurrencyUpgradeToUniqueShard.png" 16 | }, 17 | { 18 | "id": "regal-shard", 19 | "text": "富豪石碎片", 20 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlTWFnaWNUb1JhcmVTaGFyZCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/e4128abe92/CurrencyUpgradeMagicToRareShard.png" 21 | }, 22 | { 23 | "id": "artificers-shard", 24 | "text": "工匠碎片", 25 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRFcXVpcG1lbnRTb2NrZXRTaGFyZCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/cdcdd8bf3f/CurrencyAddEquipmentSocketShard.png" 26 | }, 27 | { 28 | "id": "wisdom", 29 | "text": "知識卷軸", 30 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lJZGVudGlmaWNhdGlvbiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/884f7bc58b/CurrencyIdentification.png" 31 | }, 32 | { 33 | "id": "transmute", 34 | "text": "蛻變石", 35 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9NYWdpYyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/2f8e1ff9f8/CurrencyUpgradeToMagic.png" 36 | }, 37 | { 38 | "id": "aug", 39 | "text": "增幅石", 40 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRNb2RUb01hZ2ljIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/c8ad0ddc84/CurrencyAddModToMagic.png" 41 | }, 42 | { 43 | "id": "chance", 44 | "text": "機會石", 45 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9VbmlxdWUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/93c6cc8d5b/CurrencyUpgradeToUnique.png" 46 | }, 47 | { 48 | "id": "alch", 49 | "text": "點金石", 50 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlVG9SYXJlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/9b80b44821/CurrencyUpgradeToRare.png" 51 | }, 52 | { 53 | "id": "chaos", 54 | "text": "混沌石", 55 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lSZXJvbGxSYXJlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/c0ca392a78/CurrencyRerollRare.png" 56 | }, 57 | { 58 | "id": "vaal", 59 | "text": "瓦爾寶珠", 60 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lWYWFsIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/72bc84396c/CurrencyVaal.png" 61 | }, 62 | { 63 | "id": "regal", 64 | "text": "富豪石", 65 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lVcGdyYWRlTWFnaWNUb1JhcmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/e8fb148e80/CurrencyUpgradeMagicToRare.png" 66 | }, 67 | { 68 | "id": "exalted", 69 | "text": "崇高石", 70 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRNb2RUb1JhcmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/ad7c366789/CurrencyAddModToRare.png" 71 | }, 72 | { 73 | "id": "divine", 74 | "text": "神聖石", 75 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lNb2RWYWx1ZXMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/2986e220b3/CurrencyModValues.png" 76 | }, 77 | { 78 | "id": "annul", 79 | "text": "無效石", 80 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQW5udWxsT3JiIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/2daba8ccca/AnnullOrb.png" 81 | }, 82 | { 83 | "id": "artificers", 84 | "text": "工匠石", 85 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBZGRFcXVpcG1lbnRTb2NrZXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/5131fd4774/CurrencyAddEquipmentSocket.png" 86 | }, 87 | { 88 | "id": "mirror", 89 | "text": "卡蘭德的魔鏡", 90 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lEdXBsaWNhdGUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/26bc31680e/CurrencyDuplicate.png" 91 | }, 92 | { 93 | "id": "scrap", 94 | "text": "護甲片", 95 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lBcm1vdXJRdWFsaXR5Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/d5868f596d/CurrencyArmourQuality.png" 96 | }, 97 | { 98 | "id": "whetstone", 99 | "text": "磨刀石", 100 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lXZWFwb25RdWFsaXR5Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/18715ea7be/CurrencyWeaponQuality.png" 101 | }, 102 | { 103 | "id": "etcher", 104 | "text": "奧術蝕刻師", 105 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lXZWFwb25NYWdpY1F1YWxpdHkiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/516e8f1131/CurrencyWeaponMagicQuality.png" 106 | }, 107 | { 108 | "id": "bauble", 109 | "text": "玻璃彈珠", 110 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lGbGFza1F1YWxpdHkiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/1cec279e01/CurrencyFlaskQuality.png" 111 | }, 112 | { 113 | "id": "gcp", 114 | "text": "寶石匠的稜鏡", 115 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQ3VycmVuY3lHZW1RdWFsaXR5Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/f4bace18d7/CurrencyGemQuality.png" 116 | } 117 | ] 118 | }, 119 | { 120 | "id": "Fragments", 121 | "label": "碎片", 122 | "entries": [ 123 | { 124 | "id": "breach-splinter", 125 | "text": "裂痕裂片", 126 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaHN0b25lU3BsaW50ZXIiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/4abb17ea8e/BreachstoneSplinter.png" 127 | }, 128 | { 129 | "id": "breachstone", 130 | "text": "裂痕石", 131 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaHN0b25lIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/d60587d724/Breachstone.png" 132 | }, 133 | { 134 | "id": "simulacrum-splinter", 135 | "text": "幻像斷片", 136 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9EZWxpcml1bVNwbGludGVyIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/296e6ffc57/DeliriumSplinter.png" 137 | }, 138 | { 139 | "id": "simulacrum", 140 | "text": "幻像異界", 141 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9EZWxpcml1bUZyYWdtZW50Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/cce2c7e1b3/DeliriumFragment.png" 142 | }, 143 | { 144 | "id": "an-audience-with-the-king", 145 | "text": "晉見帝王", 146 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUml0dWFsL1Zvb2Rvb0tpbmdFZmZpZ3kiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/0b02c113cc/VoodooKingEffigy.png" 147 | }, 148 | { 149 | "id": "cowardly-fate", 150 | "text": "怯懦之運", 151 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL1RyaWFsbWFzdGVyS2V5MSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/661ab60074/TrialmasterKey1.png" 152 | }, 153 | { 154 | "id": "deadly-fate", 155 | "text": "致命之運", 156 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL1RyaWFsbWFzdGVyS2V5MiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/cccae44832/TrialmasterKey2.png" 157 | }, 158 | { 159 | "id": "victorious-fate", 160 | "text": "勝利之運", 161 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL1RyaWFsbWFzdGVyS2V5MyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/0270951a73/TrialmasterKey3.png" 162 | }, 163 | { 164 | "id": "ancient-crisis-fragment", 165 | "text": "遠古危機碎片", 166 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvUXVlc3RJdGVtcy9QaW5uYWNsZUtleTEiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/ef329c74d1/PinnacleKey1.png" 167 | }, 168 | { 169 | "id": "faded-crisis-fragment", 170 | "text": "褪色危機碎片", 171 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvUXVlc3RJdGVtcy9QaW5uYWNsZUtleTIiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/5709c39ee7/PinnacleKey2.png" 172 | }, 173 | { 174 | "id": "weathered-crisis-fragment", 175 | "text": "風化危機碎片", 176 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvUXVlc3RJdGVtcy9QaW5uYWNsZUtleTMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/32e6544250/PinnacleKey3.png" 177 | } 178 | ] 179 | }, 180 | { 181 | "id": "Runes", 182 | "label": "符文", 183 | "entries": [ 184 | { 185 | "id": "desert-rune", 186 | "text": "沙漠符文", 187 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvRmlyZVJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/eeabf0e7a0/FireRune.png" 188 | }, 189 | { 190 | "id": "glacial-rune", 191 | "text": "冰川符文", 192 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvQ29sZFJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/499bfe179a/ColdRune.png" 193 | }, 194 | { 195 | "id": "storm-rune", 196 | "text": "暴風符文", 197 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvTGlnaHRuaW5nUnVuZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/98319b3998/LightningRune.png" 198 | }, 199 | { 200 | "id": "iron-rune", 201 | "text": "鍛鐵符文", 202 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvRW5oYW5jZVJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/cfeb426ee0/EnhanceRune.png" 203 | }, 204 | { 205 | "id": "body-rune", 206 | "text": "肉體符文", 207 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvTGlmZVJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/a09dcb651b/LifeRune.png" 208 | }, 209 | { 210 | "id": "mind-rune", 211 | "text": "心靈符文", 212 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvTWFuYVJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/4be6331480/ManaRune.png" 213 | }, 214 | { 215 | "id": "rebirth-rune", 216 | "text": "重生符文", 217 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvTGlmZVJlY292ZXJ5UnVuZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/bbbb060773/LifeRecoveryRune.png" 218 | }, 219 | { 220 | "id": "inspiration-rune", 221 | "text": "啟發符文", 222 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvTWFuYVJlY292ZXJ5UnVuZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/5154db8cf8/ManaRecoveryRune.png" 223 | }, 224 | { 225 | "id": "stone-rune", 226 | "text": "岩石符文", 227 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvU3R1blJ1bmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/f7830fb594/StunRune.png" 228 | }, 229 | { 230 | "id": "vision-rune", 231 | "text": "遠見符文", 232 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUnVuZXMvQWNjdXJhY3lSdW5lIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/8e7841f0ef/AccuracyRune.png" 233 | } 234 | ] 235 | }, 236 | { 237 | "id": "Essences", 238 | "label": "精髓", 239 | "entries": [ 240 | { 241 | "id": "essence-of-the-body", 242 | "text": "肉體精髓", 243 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9MaWZlRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/638d6c4cbe/LifeEssence.png" 244 | }, 245 | { 246 | "id": "essence-of-the-mind", 247 | "text": "心智精髓", 248 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9NYW5hRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/c881e6273c/ManaEssence.png" 249 | }, 250 | { 251 | "id": "essence-of-enhancement", 252 | "text": "強化精髓", 253 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9EZWZlbmNlc0Vzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/e1e5c1ccb8/DefencesEssence.png" 254 | }, 255 | { 256 | "id": "essence-of-torment", 257 | "text": "折磨精髓", 258 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9QaHlzaWNhbEVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/e52cd05eeb/PhysicalEssence.png" 259 | }, 260 | { 261 | "id": "essence-of-flames", 262 | "text": "烈焰精髓", 263 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9GaXJlRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/add6c12f64/FireEssence.png" 264 | }, 265 | { 266 | "id": "essence-of-ice", 267 | "text": "寒冰精髓", 268 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9Db2xkRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/73d9fd8619/ColdEssence.png" 269 | }, 270 | { 271 | "id": "essence-of-electricity", 272 | "text": "電能精髓", 273 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9MaWdodG5pbmdFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/2c3725fdad/LightningEssence.png" 274 | }, 275 | { 276 | "id": "essence-of-ruin", 277 | "text": "毀滅精髓", 278 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9DaGFvc0Vzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/5f0d3c7014/ChaosEssence.png" 279 | }, 280 | { 281 | "id": "essence-of-battle", 282 | "text": "戰鬥精髓", 283 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9BdHRhY2tFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/3cece29e19/AttackEssence.png" 284 | }, 285 | { 286 | "id": "essence-of-sorcery", 287 | "text": "巫術精髓", 288 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9DYXN0ZXJFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/6bb099115a/CasterEssence.png" 289 | }, 290 | { 291 | "id": "essence-of-haste", 292 | "text": "迅捷精髓", 293 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9TcGVlZEVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/affda36f07/SpeedEssence.png" 294 | }, 295 | { 296 | "id": "essence-of-the-infinite", 297 | "text": "無限精髓", 298 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9BdHRyaWJ1dGVFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/58f9516082/AttributeEssence.png" 299 | }, 300 | { 301 | "id": "sep", 302 | "text": "" 303 | }, 304 | { 305 | "id": "greater-essence-of-the-body", 306 | "text": "高階肉體精髓", 307 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyTGlmZUVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/8906b401fe/GreaterLifeEssence.png" 308 | }, 309 | { 310 | "id": "greater-essence-of-the-mind", 311 | "text": "高階心智精髓", 312 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyTWFuYUVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/f9e17e6d47/GreaterManaEssence.png" 313 | }, 314 | { 315 | "id": "greater-essence-of-enhancement", 316 | "text": "高階強化精髓", 317 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyRGVmZW5jZXNFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/a0ed2df82c/GreaterDefencesEssence.png" 318 | }, 319 | { 320 | "id": "greater-essence-of-torment", 321 | "text": "高階折磨精髓", 322 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyUGh5c2ljYWxFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/a28c932def/GreaterPhysicalEssence.png" 323 | }, 324 | { 325 | "id": "greater-essence-of-flames", 326 | "text": "高階烈焰精髓", 327 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyRmlyZUVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/9b8d6e7b4e/GreaterFireEssence.png" 328 | }, 329 | { 330 | "id": "greater-essence-of-ice", 331 | "text": "高階寒冰精髓", 332 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyQ29sZEVzc2VuY2UiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/e5224bace0/GreaterColdEssence.png" 333 | }, 334 | { 335 | "id": "greater-essence-of-electricity", 336 | "text": "高階電能精髓", 337 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyTGlnaHRuaW5nRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/c6df464bb9/GreaterLightningEssence.png" 338 | }, 339 | { 340 | "id": "greater-essence-of-ruin", 341 | "text": "高階毀滅精髓", 342 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyQ2hhb3NFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/5c71ec7418/GreaterChaosEssence.png" 343 | }, 344 | { 345 | "id": "greater-essence-of-battle", 346 | "text": "高階戰鬥精髓", 347 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyQXR0YWNrRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/e2bb5332b8/GreaterAttackEssence.png" 348 | }, 349 | { 350 | "id": "greater-essence-of-sorcery", 351 | "text": "高階巫術精髓", 352 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyQ2FzdGVyRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/14b7e4c8ef/GreaterCasterEssence.png" 353 | }, 354 | { 355 | "id": "greater-essence-of-haste", 356 | "text": "高階迅捷精髓", 357 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyU3BlZWRFc3NlbmNlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/5a72f95c24/GreaterSpeedEssence.png" 358 | }, 359 | { 360 | "id": "greater-essence-of-the-infinite", 361 | "text": "高階無限精髓", 362 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXNzZW5jZS9HcmVhdGVyQXR0cmlidXRlRXNzZW5jZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/8a8cb823af/GreaterAttributeEssence.png" 363 | } 364 | ] 365 | }, 366 | { 367 | "id": "Relics", 368 | "label": "聖物", 369 | "entries": [] 370 | }, 371 | { 372 | "id": "Ultimatum", 373 | "label": "靈魂核心", 374 | "entries": [ 375 | { 376 | "id": "soul-core-of-tacati", 377 | "text": "特卡蒂靈魂核心", 378 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNoYW9zIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/cd9c4fb0a7/GreaterSoulCoreChaos.png" 379 | }, 380 | { 381 | "id": "soul-core-of-opiloti", 382 | "text": "歐派理堤靈魂核心", 383 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZVBoeXNpY2FsIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/366f87b8f2/GreaterSoulCorePhysical.png" 384 | }, 385 | { 386 | "id": "soul-core-of-jiquani", 387 | "text": "吉卡尼靈魂核心", 388 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUxpZmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/bb83a9f337/GreaterSoulCoreLife.png" 389 | }, 390 | { 391 | "id": "soul-core-of-zalatl", 392 | "text": "札拉提靈魂核心", 393 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZU1hbmEiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/1437190de2/GreaterSoulCoreMana.png" 394 | }, 395 | { 396 | "id": "soul-core-of-citaqualotl", 397 | "text": "希特克拉多靈魂核心", 398 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL1NvdWxDb3JlUGh5c2ljYWwiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/f48f9f0c62/SoulCorePhysical.png" 399 | }, 400 | { 401 | "id": "soul-core-of-puhuarte", 402 | "text": "普希瓦爾靈魂核心", 403 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUZpcmUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/6390ebeaa6/GreaterSoulCoreFire.png" 404 | }, 405 | { 406 | "id": "soul-core-of-tzamoto", 407 | "text": "薩摩特靈魂核心", 408 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNvbGQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/d437c04d5e/GreaterSoulCoreCold.png" 409 | }, 410 | { 411 | "id": "soul-core-of-xopec", 412 | "text": "柔派克靈魂核心", 413 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUxpZ2h0bmluZyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/3dffadfa9a/GreaterSoulCoreLightning.png" 414 | }, 415 | { 416 | "id": "soul-core-of-azcapa", 417 | "text": "艾斯卡巴靈魂核心", 418 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZVdlYWx0aFJhcml0eSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/6a69033a04/GreaterSoulCoreWealthRarity.png" 419 | }, 420 | { 421 | "id": "soul-core-of-topotante", 422 | "text": "塔普塔特靈魂核心", 423 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZVRyaUVsZW1lbnQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/ae7a949b3a/GreaterSoulCoreTriElement.png" 424 | }, 425 | { 426 | "id": "soul-core-of-quipolatl", 427 | "text": "克特帕托靈魂核心", 428 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZVNwZWVkIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/d4967ad0f1/GreaterSoulCoreSpeed.png" 429 | }, 430 | { 431 | "id": "soul-core-of-ticaba", 432 | "text": "堤卡巴靈魂核心", 433 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNyaXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/b453416064/GreaterSoulCoreCrit.png" 434 | }, 435 | { 436 | "id": "soul-core-of-atmohua", 437 | "text": "力量靈魂核心", 438 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNyaXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/b453416064/GreaterSoulCoreCrit.png" 439 | }, 440 | { 441 | "id": "soul-core-of-cholotl", 442 | "text": "敏捷靈魂核心", 443 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNyaXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/b453416064/GreaterSoulCoreCrit.png" 444 | }, 445 | { 446 | "id": "soul-core-of-zantipi", 447 | "text": "智慧靈魂核心", 448 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvU291bENvcmVzL0dyZWF0ZXJTb3VsQ29yZUNyaXQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/b453416064/GreaterSoulCoreCrit.png" 449 | } 450 | ] 451 | }, 452 | { 453 | "id": "BreachCatalyst", 454 | "label": "裂痕催化劑", 455 | "entries": [ 456 | { 457 | "id": "flesh-catalyst", 458 | "text": "生命催化劑", 459 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0TGlmZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/06ec489870/BreachCatalystLife.png" 460 | }, 461 | { 462 | "id": "neural-catalyst", 463 | "text": "魔力催化劑", 464 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0TWFuYSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/61d3a7a832/BreachCatalystMana.png" 465 | }, 466 | { 467 | "id": "carapace-catalyst", 468 | "text": "冶鍊的催化劑", 469 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0RGVmZW5jZXMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/568ca70b04/BreachCatalystDefences.png" 470 | }, 471 | { 472 | "id": "uul-netols-catalyst", 473 | "text": "物理催化劑", 474 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0UGh5c2ljYWwiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/bc9406c106/BreachCatalystPhysical.png" 475 | }, 476 | { 477 | "id": "xophs-catalyst", 478 | "text": "火焰催化劑", 479 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0RmlyZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/05b1999374/BreachCatalystFire.png" 480 | }, 481 | { 482 | "id": "tuls-catalyst", 483 | "text": "冰冷催化劑", 484 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0Q29sZCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/c3bc9abf43/BreachCatalystCold.png" 485 | }, 486 | { 487 | "id": "eshs-catalyst", 488 | "text": "閃電催化劑", 489 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0TGlnaHRuaW5nIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/299e62fc33/BreachCatalystLightning.png" 490 | }, 491 | { 492 | "id": "chayulas-catalyst", 493 | "text": "混沌催化劑", 494 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0Q2hhb3MiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/c80d8d7b1b/BreachCatalystChaos.png" 495 | }, 496 | { 497 | "id": "reaver-catalyst", 498 | "text": "研磨的催化劑", 499 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0QXR0YWNrIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/ccc363f502/BreachCatalystAttack.png" 500 | }, 501 | { 502 | "id": "sibilant-catalyst", 503 | "text": "充能的催化劑", 504 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0Q2FzdGVyIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/51dc6bc317/BreachCatalystCaster.png" 505 | }, 506 | { 507 | "id": "skittering-catalyst", 508 | "text": "飛掠的催化劑", 509 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0U3BlZWQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/14b5db3c3c/BreachCatalystSpeed.png" 510 | }, 511 | { 512 | "id": "adaptive-catalyst", 513 | "text": "本質的催化劑", 514 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvQnJlYWNoL0JyZWFjaENhdGFseXN0QXR0cmlidXRlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/015f6c9ac8/BreachCatalystAttribute.png" 515 | } 516 | ] 517 | }, 518 | { 519 | "id": "Expedition", 520 | "label": "探險貨幣與文物", 521 | "entries": [ 522 | { 523 | "id": "exotic-coinage", 524 | "text": "異域幣鑄", 525 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXhwZWRpdGlvbi9CYXJ0ZXJSZWZyZXNoQ3VycmVuY3kiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/8a4fe1f468/BarterRefreshCurrency.png" 526 | }, 527 | { 528 | "id": "broken-circle-artifact", 529 | "text": "破碎之環文物", 530 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXhwZWRpdGlvbi9SdW5lMTYiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/46b487f53f/Rune16.png" 531 | }, 532 | { 533 | "id": "black-scythe-artifact", 534 | "text": "黑暗血鐮文物", 535 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXhwZWRpdGlvbi9SdW5lNiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/2260f34070/Rune6.png" 536 | }, 537 | { 538 | "id": "order-artifact", 539 | "text": "秩序文物", 540 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXhwZWRpdGlvbi9SdW5lMTMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/47a41c9782/Rune13.png" 541 | }, 542 | { 543 | "id": "sun-artifact", 544 | "text": "豔陽文物", 545 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRXhwZWRpdGlvbi9SdW5lMTciLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/ca2335fdef/Rune17.png" 546 | } 547 | ] 548 | }, 549 | { 550 | "id": "Ritual", 551 | "label": "祭祀徵兆", 552 | "entries": [ 553 | { 554 | "id": "omen-of-refreshment", 555 | "text": "刷新之兆", 556 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxQmx1ZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/213474d1d5/VoodooOmens1Blue.png" 557 | }, 558 | { 559 | "id": "omen-of-resurgence", 560 | "text": "復興之兆", 561 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyQmx1ZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/044d5086da/VoodooOmens2Blue.png" 562 | }, 563 | { 564 | "id": "omen-of-amelioration", 565 | "text": "改善之兆", 566 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMzQmx1ZSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/b5ee965352/VoodooOmens3Blue.png" 567 | }, 568 | { 569 | "id": "omen-of-whittling", 570 | "text": "削切之兆", 571 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxRGFyayIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/2dea0999d5/VoodooOmens1Dark.png" 572 | }, 573 | { 574 | "id": "omen-of-sinistral-erasure", 575 | "text": "左旋抺除之兆", 576 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyRGFyayIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/0cfa6959be/VoodooOmens2Dark.png" 577 | }, 578 | { 579 | "id": "omen-of-dextral-erasure", 580 | "text": "右旋抺除之兆", 581 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMzRGFyayIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/6e2bb52963/VoodooOmens3Dark.png" 582 | }, 583 | { 584 | "id": "omen-of-sinistral-alchemy", 585 | "text": "左旋煉金之兆", 586 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxR3JlZW4iLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/11c49d2322/VoodooOmens1Green.png" 587 | }, 588 | { 589 | "id": "omen-of-dextral-alchemy", 590 | "text": "右旋煉金之兆", 591 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyR3JlZW4iLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/0ff0cfbb93/VoodooOmens2Green.png" 592 | }, 593 | { 594 | "id": "omen-of-sinistral-coronation", 595 | "text": "左旋加冕之兆", 596 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxUmVkIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/b8e2b8ed3f/VoodooOmens1Red.png" 597 | }, 598 | { 599 | "id": "omen-of-dextral-coronation", 600 | "text": "右旋加冕之兆", 601 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyUmVkIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/f1bbb78614/VoodooOmens2Red.png" 602 | }, 603 | { 604 | "id": "omen-of-corruption", 605 | "text": "腐化之兆", 606 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMzUmVkIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/9cfdcc9e1a/VoodooOmens3Red.png" 607 | }, 608 | { 609 | "id": "omen-of-greater-exaltation", 610 | "text": "大幅提升之兆", 611 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxWWVsbG93Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/22d9b6478d/VoodooOmens1Yellow.png" 612 | }, 613 | { 614 | "id": "omen-of-sinistral-exaltation", 615 | "text": "左旋提升之兆", 616 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyWWVsbG93Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/6d316b47ee/VoodooOmens2Yellow.png" 617 | }, 618 | { 619 | "id": "omen-of-dextral-exaltation", 620 | "text": "右旋提升之兆", 621 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMzWWVsbG93Iiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/ed7cf06fa4/VoodooOmens3Yellow.png" 622 | }, 623 | { 624 | "id": "omen-of-greater-annulment", 625 | "text": "大幅廢止之兆", 626 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMxUHVycGxlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/a6848dff1c/VoodooOmens1Purple.png" 627 | }, 628 | { 629 | "id": "omen-of-sinistral-annulment", 630 | "text": "左旋廢止之兆", 631 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMyUHVycGxlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/b9eaf38592/VoodooOmens2Purple.png" 632 | }, 633 | { 634 | "id": "omen-of-dextral-annulment", 635 | "text": "右旋廢止之兆", 636 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvT21lbnMvVm9vZG9vT21lbnMzUHVycGxlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/d901658529/VoodooOmens3Purple.png" 637 | } 638 | ] 639 | }, 640 | { 641 | "id": "DeliriumInstill", 642 | "label": "譫妄精練物", 643 | "entries": [ 644 | { 645 | "id": "distilled-ire", 646 | "text": "精煉的憤怒", 647 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkSXJlIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/cbd99f0eff/DistilledIre.png" 648 | }, 649 | { 650 | "id": "distilled-guilt", 651 | "text": "精煉的罪孽", 652 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkR3VpbHQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/21cd0fd17b/DistilledGuilt.png" 653 | }, 654 | { 655 | "id": "distilled-greed", 656 | "text": "精煉的貪婪", 657 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkR3JlZWQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/5888a4ae61/DistilledGreed.png" 658 | }, 659 | { 660 | "id": "distilled-paranoia", 661 | "text": "精煉的偏執", 662 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkUGFyYW5vaWEiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/279e807e8f/DistilledParanoia.png" 663 | }, 664 | { 665 | "id": "distilled-envy", 666 | "text": "精煉的忌妒", 667 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkRW52eSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/abf219916e/DistilledEnvy.png" 668 | }, 669 | { 670 | "id": "distilled-disgust", 671 | "text": "精煉的厭惡", 672 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkRGlzZ3VzdCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/f462426de0/DistilledDisgust.png" 673 | }, 674 | { 675 | "id": "distilled-despair", 676 | "text": "精煉的絕望", 677 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkRGVzcGFpciIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/c676bf7dae/DistilledDespair.png" 678 | }, 679 | { 680 | "id": "distilled-fear", 681 | "text": "精煉的恐懼", 682 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkRmVhciIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/8cffdae445/DistilledFear.png" 683 | }, 684 | { 685 | "id": "distilled-suffering", 686 | "text": "精煉的苦難", 687 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkU3VmZmVyaW5nIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/801a53662a/DistilledSuffering.png" 688 | }, 689 | { 690 | "id": "distilled-isolation", 691 | "text": "精煉的孤立", 692 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvRGlzdGlsbGVkRW1vdGlvbnMvRGlzdGlsbGVkSXNvbGF0aW9uIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/997a6a442e/DistilledIsolation.png" 693 | } 694 | ] 695 | }, 696 | { 697 | "id": "Waystones", 698 | "label": "換界石與碑牌", 699 | "entries": [ 700 | { 701 | "id": "waystone-1", 702 | "text": "地圖鑰匙(階級 1)", 703 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/bb4c55989d/EndgameMap1.png" 704 | }, 705 | { 706 | "id": "waystone-2", 707 | "text": "地圖鑰匙(階級 2)", 708 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/b4ffda8e35/EndgameMap2.png" 709 | }, 710 | { 711 | "id": "waystone-3", 712 | "text": "地圖鑰匙(階級 3)", 713 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/7940d0ad1a/EndgameMap3.png" 714 | }, 715 | { 716 | "id": "waystone-4", 717 | "text": "地圖鑰匙(階級 4)", 718 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwNCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/e405b6783b/EndgameMap4.png" 719 | }, 720 | { 721 | "id": "waystone-5", 722 | "text": "地圖鑰匙(階級 5)", 723 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwNSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/22247ac187/EndgameMap5.png" 724 | }, 725 | { 726 | "id": "waystone-6", 727 | "text": "地圖鑰匙(階級 6)", 728 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwNiIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/f64d3b6889/EndgameMap6.png" 729 | }, 730 | { 731 | "id": "waystone-7", 732 | "text": "地圖鑰匙(階級 7)", 733 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwNyIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/7f32b3664e/EndgameMap7.png" 734 | }, 735 | { 736 | "id": "waystone-8", 737 | "text": "地圖鑰匙(階級 8)", 738 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwOCIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/fb8a8651b5/EndgameMap8.png" 739 | }, 740 | { 741 | "id": "waystone-9", 742 | "text": "地圖鑰匙(階級 9)", 743 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwOSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/420f1c6412/EndgameMap9.png" 744 | }, 745 | { 746 | "id": "waystone-10", 747 | "text": "地圖鑰匙(階級 10)", 748 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTAiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/4a1266867a/EndgameMap10.png" 749 | }, 750 | { 751 | "id": "waystone-11", 752 | "text": "地圖鑰匙(階級 11)", 753 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTEiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/77da168a75/EndgameMap11.png" 754 | }, 755 | { 756 | "id": "waystone-12", 757 | "text": "地圖鑰匙(階級 12)", 758 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTIiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/1b37ec5864/EndgameMap12.png" 759 | }, 760 | { 761 | "id": "waystone-13", 762 | "text": "地圖鑰匙(階級 13)", 763 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTMiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/4877b17436/EndgameMap13.png" 764 | }, 765 | { 766 | "id": "waystone-14", 767 | "text": "地圖鑰匙(階級 14)", 768 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTQiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/9149454787/EndgameMap14.png" 769 | }, 770 | { 771 | "id": "waystone-15", 772 | "text": "地圖鑰匙(階級 15)", 773 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTUiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/cea6af97d7/EndgameMap15.png" 774 | }, 775 | { 776 | "id": "waystone-16", 777 | "text": "地圖鑰匙(階級 16)", 778 | "image": "/gen/image/WzI4LDE0LHsiZiI6IjJESXRlbXMvTWFwcy9FbmRnYW1lTWFwcy9FbmRnYW1lTWFwMTYiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/098fff71c2/EndgameMap16.png" 779 | }, 780 | { 781 | "id": "sep", 782 | "text": "" 783 | }, 784 | { 785 | "id": "precursor-tablet", 786 | "text": "先行者碑牌", 787 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUHJlY3Vyc29yVGFibGV0cy9QcmVjdXJzb3JUYWJsZXRHZW5lcmljIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/5658ce7f2d/PrecursorTabletGeneric.png" 788 | }, 789 | { 790 | "id": "breach-precursor-tablet", 791 | "text": "裂痕碑牌", 792 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUHJlY3Vyc29yVGFibGV0cy9QcmVjdXJzb3JUYWJsZXRCcmVhY2giLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/295c0839d7/PrecursorTabletBreach.png" 793 | }, 794 | { 795 | "id": "expedition-precursor-tablet", 796 | "text": "探險碑牌", 797 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUHJlY3Vyc29yVGFibGV0cy9QcmVjdXJzb3JUYWJsZXRFeHBlZGl0aW9uIiwic2NhbGUiOjEsInJlYWxtIjoicG9lMiJ9XQ/4450dac451/PrecursorTabletExpedition.png" 798 | }, 799 | { 800 | "id": "delirium-precursor-tablet", 801 | "text": "譫妄碑牌", 802 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUHJlY3Vyc29yVGFibGV0cy9QcmVjdXJzb3JUYWJsZXREZWxpcml1bSIsInNjYWxlIjoxLCJyZWFsbSI6InBvZTIifV0/81acb86b9f/PrecursorTabletDelirium.png" 803 | }, 804 | { 805 | "id": "ritual-precursor-tablet", 806 | "text": "祭祀碑牌", 807 | "image": "/gen/image/WzI1LDE0LHsiZiI6IjJESXRlbXMvQ3VycmVuY3kvUHJlY3Vyc29yVGFibGV0cy9QcmVjdXJzb3JUYWJsZXRSaXR1YWwiLCJzY2FsZSI6MSwicmVhbG0iOiJwb2UyIn1d/7c49d00f07/PrecursorTabletRitual.png" 808 | } 809 | ] 810 | }, 811 | { 812 | "id": "Misc", 813 | "label": null, 814 | "entries": [] 815 | } 816 | ] 817 | } --------------------------------------------------------------------------------