Kaynağa Gözat

version 0.1

Mistivia 1 ay önce
ebeveyn
işleme
5db7a8c78d

+ 2 - 0
.gitignore

@@ -23,3 +23,5 @@ dist-ssr
 *.sln
 *.sw?
 
+data/cards.json
+

+ 1 - 1
README.md

@@ -2,7 +2,7 @@
 
 游戏王卡组编辑器
 
-开发中,未完成
+Demo: [ygodeck.mistivia.com](https://ygodeck.mistivia.com)
 
 ## 截图
 

+ 31 - 0
data/build-card-info.py

@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+import json
+
+j = None
+with open('cards.json') as fp:
+    j = json.load(fp)
+
+outData = dict()
+
+def convert(card):
+    newCard = dict()
+    newCard['names'] = []
+    for namek in ['cn_name', 'sc_name', 'md_name', 'nwbbs_n', \
+                  'cnocg_n', 'jp_ruby', 'jp_name', 'en_name']:
+        if namek in card and len(card[namek]) > 0:
+            newCard['names'].append(card[namek])
+    newCard['isExtra'] = False
+    for t in ['超量', '链接', '同调', '融合']:
+        if t in card['text']['types']:
+            newCard['isExtra'] = True
+    newCard['cid'] = card['cid']
+    return newCard
+
+for k in j:
+    outData[j[k]['id']] = convert(j[k])
+
+outStr = 'export const cardDb = ' + json.dumps(outData) + ';'
+
+with open('../src/data/cardDb.js', 'w') as fp:
+    fp.write(outStr)

+ 5 - 0
data/build.sh

@@ -0,0 +1,5 @@
+curl https://ygocdb.com/api/v0/cards.zip -o cards.zip
+unzip cards.zip
+rm cards.zip
+python3 build-card-info.py
+

+ 13 - 7
src/components/CardThumb.svelte

@@ -1,21 +1,27 @@
-<script lang="ts">
+<script lang="js">
 
-import {setLeftPanelCard} from '../control/left_panel';
+    import {setLeftPanelCard} from '../control/left_panel';
 
-let {id} = $props();
+    let {id, area} = $props();
 
-function onhover() {
-    setLeftPanelCard(id); 
-}
+    function onhover() {
+        setLeftPanelCard(id); 
+    }
+
+    function onDragStart(e) {
+        e.dataTransfer.setData('text', JSON.stringify({id, area}))
+    }
 
 </script>
 
 {#if id}
     <img
+        draggable="true"
         onmouseover={onhover}
         onfocus={onhover}
+        ondragstart={onDragStart}
         height="100%"
-        src="https://cdn.233.momobako.com/ygopro/pics/{id}.jpg!half"
+        src="https://cdn.233.momobako.com/ygopro/pics/{id}.jpg"
         alt="yugioh card {id}"
     />
 {/if}

+ 50 - 8
src/components/MainPanel.svelte

@@ -1,6 +1,6 @@
 <script lang="js">
     import CardThumb from './CardThumb.svelte';
-    import { deck, setDeck } from '../control/deck';
+    import { deck, setDeck, deckOps } from '../control/deck';
     import {
         parseYdk,
         genYdk,
@@ -31,19 +31,60 @@
         downloadStringAsFile('mydeck.ydk', deckString)
     }
 
+    function copyDeck() {
+        let deckString = genYdk($deck);
+        navigator.clipboard.writeText(deckString)
+            .then(() => {
+              alert('YDK卡组码已复制到剪贴板');
+            })
+            .catch(err => {
+              alert("失败!");
+            });
+    }
+
     function shareDeck() {
         let url = window.location.href;
         url = url.split('#')[0]
         url = url + '#' + genYdke($deck);
         navigator.clipboard.writeText(url)
             .then(() => {
-              alert('分享链接已复制到剪贴板')
+              alert('分享链接已复制到剪贴板');
             })
             .catch(err => {
               alert("失败!");
             });
     }
 
+    function onDrop(to, event) {
+        event.preventDefault();
+        const data = JSON.parse(event.dataTransfer.getData('text'));
+        let from = data.area;
+        if (from === 'search') {
+            if (to === 'main') {
+                deckOps.add2main(data.id);
+            } else if (to === 'side') {
+                deckOps.add2side(data.id);
+            } else if (to === 'extra') {
+                deckOps.add2extra(data.id);
+            }
+        } else if (from === 'main') {
+            if (to == 'side') {
+                deckOps.main2side(data.id);
+            }
+        } else if (from === 'extra') {
+            if (to == 'side') {
+                deckOps.extra2side(data.id);
+            }
+        } else if (from === 'side') {
+            if (to == 'main') {
+                deckOps.side2main(data.id);
+            } else if (to == 'extra') {
+                deckOps.side2extra(data.id);
+            }
+        }
+    }
+
+
 </script>
 
 <input bind:this={fileInput} style="display:none;" onchange={loadDeck} type="file" class="file-input" accept=".ydk" />
@@ -52,35 +93,36 @@
     <div class="control-bar">
         <button class="btn" onclick={openDeck}>打开</button>
         <button class="btn" onclick={saveDeck}>保存</button>
+        <button class="btn" onclick={copyDeck}>复制到剪贴板</button>
         <button class="btn" onclick={shareDeck}>分享</button>
     </div>
     <div class="deck-section">
         <div class="deck-group">
             <h3>主卡组({$deck.main.length})</h3>
-            <div class="card-grid main-deck">
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("main", e)} class="card-grid main-deck">
                 {#each $deck.main as card}
                     <div class="card-grid-thumb">
-                        <CardThumb id={card} />
+                        <CardThumb id={card} area="main" />
                     </div>
                 {/each}
             </div>
         </div>
         <div class="deck-group">
             <h3>额外卡组({$deck.extra.length})</h3>
-            <div class="card-grid extra-deck">
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("extra", e)} class="card-grid extra-deck">
                 {#each $deck.extra as card}
                     <div class="card-grid-thumb">
-                        <CardThumb id={card} />
+                        <CardThumb id={card} area="extra" />
                     </div>
                 {/each}
             </div>
         </div>
         <div class="deck-group">
             <h3>副卡组({$deck.side.length})</h3>
-            <div class="card-grid side-deck">
+            <div role="region" ondragover={(e)=>e.preventDefault()} ondrop={(e)=>onDrop("side", e)} class="card-grid side-deck">
                 {#each $deck.side as card}
                     <div class="card-grid-thumb">
-                        <CardThumb id={card} />
+                        <CardThumb id={card} area="side" />
                     </div>
                 {/each}
             </div>

+ 20 - 17
src/components/RightPanel.svelte

@@ -1,31 +1,34 @@
 <script lang="js">
     import CardThumb from './CardThumb.svelte';
-    import { resultCards } from '../control/search'
-
-    function onPrevPage() {
+    import { changeInput, showingCards, onPrevPage, onNextPage } from '../control/search'
+    import { deckOps } from '../control/deck';
 
+    function onChange(event) {
+        changeInput(event.target.value);
     }
 
-    function onNextPage() {
-        
+    function onDrop(event) {
+        event.preventDefault();
+        const data = JSON.parse(event.dataTransfer.getData('text'));
+        if (data.area === 'main' || data.area === 'side' || data.area === 'extra') {
+            deckOps.deleteCard(data.area, data.id);
+        }
     }
 </script>
 
-<div class="right-panel">
+<div class="right-panel" role="region" ondragover={(e)=>e.preventDefault()} ondrop={onDrop}>
     <div class="search-bar">
-        <input type="text" placeholder="搜索卡牌...">
+        <input type="text" placeholder="搜索卡牌..." oninput={onChange}>
     </div>
     <div class="card-list">
-        {#if $resultCards.length > 0}
-            <div class="card-item">
-                {#each $resultCards as card}
-                    <div class="card-thumbnail">
-                        <CardThumb id= {card.id} />
-                    </div>
-                    <span>{card.name}</span>
-                {/each}
-            </div>
-        {/if}
+           {#each $showingCards as card}
+               <div class="card-item">
+                   <div class="card-thumbnail">
+                       <CardThumb id= {card.id} area="search" />
+                   </div>
+                   <span>{card.name}</span>
+               </div>
+           {/each}
     </div>
     <div class="pagination">
         <button class="page-btn" onclick={onPrevPage}>上一页</button>

+ 100 - 3
src/control/deck.js

@@ -1,16 +1,112 @@
 import { writable } from "svelte/store";
 import { parseYdke } from '../utils';
+import { cardDb } from '../data/cardDb';
 
 let deck = writable({main: [], extra: [], side: []});
+let deckState = {main: [], extra: [], side: []};
 
 function setDeck(d) {
-    d.main.sort();
-    d.extra.sort();
-    d.side.sort();
+    let sortFn = (a, b) => {return cardDb[a].cid - cardDb[b].cid;}
+    d.main.sort(sortFn);
+    d.extra.sort(sortFn);
+    d.side.sort(sortFn);
+    deckState = d;
     deck.set(d);
     localStorage.setItem('cachedDeck', JSON.stringify(d));
 };
 
+const OK = 1;
+const FAIL = 0;
+
+function canAdd(d, id) {
+    let count = 0;
+    for (let c of d.main) {
+        if (c === id) count += 1;
+    }
+    for (let c of d.side) {
+        if (c === id) count += 1;
+    }
+    for (let c of d.extra) {
+        if (c === id) count += 1;
+    }
+    if (count + 1 > 3) return false;
+    return true;
+}
+
+function delCard(deck, id) {
+    for (let i = 0; i < deck.length; i++) {
+        if (deck[i] == id) {
+            deck.splice(i, 1);
+            return true;
+        }
+    }
+    return false;
+}
+
+let deckOps = {
+    "deleteCard": (from, id) => {
+        if (from === 'main') {
+            delCard(deckState.main, id);
+            setDeck(deckState);
+        } else if (from === 'side') {
+            delCard(deckState.side, id);
+            setDeck(deckState);
+        } else if (from === 'extra') {
+            delCard(deckState.extra, id);
+            setDeck(deckState);
+        }
+    },
+    "main2side": (id) => {
+        if (delCard(deckState.main, id)) {
+            deckState.side.push(id);
+        }
+        setDeck(deckState);
+    },
+    "extra2side": (id) => {
+        if (delCard(deckState.extra, id)) {
+            deckState.side.push(id);
+        }
+        setDeck(deckState);
+    },
+    "side2main": (id) => {
+        if (cardDb[id].isExtra) return;
+        if (delCard(deckState.side, id)) {
+            deckState.main.push(id);
+        }
+        setDeck(deckState);
+    },
+    "side2extra": (id) => {
+        if (!cardDb[id].isExtra) return;
+        if (delCard(deckState.side, id)) {
+            deckState.extra.push(id);
+        }
+        setDeck(deckState);
+    },
+    "add2extra": (id) => {
+        if (!cardDb[id].isExtra) return;
+        let d = deckState;
+        if (canAdd(d, id)) {
+            d.extra.push(id);
+            setDeck(d);
+        }
+    },
+    "add2main": (id) => {
+        if (cardDb[id].isExtra) return;
+        let d = deckState;
+        if (canAdd(d, id)) {
+            d.main.push(id);
+            setDeck(d);
+        }
+    },
+    "add2side": (id) => {
+        let d = deckState;
+        if (canAdd(d, id)) {
+            d.side.push(id);
+            setDeck(d);
+        }
+    }
+};
+
 function initDeck() {
     let url = window.location.href.split('#');
     if (url.length === 2) {
@@ -34,5 +130,6 @@ initDeck();
 export {
     deck,
     setDeck,
+    deckOps,
 };
 

+ 67 - 2
src/control/search.js

@@ -1,7 +1,72 @@
 import { writable } from 'svelte/store';
+import { cardDb } from '../data/cardDb';
 
-let resultCards = writable([]);
+let showingCards = writable([]);
+let resultCards = [];
+
+let curVer = 0;
+let curPage = 0;
+
+function showCards() {
+    showingCards.set(resultCards.slice(curPage * 10, curPage * 10 + 10));
+}
+
+function changeInput(query) {
+    curVer += 1;
+    query = query.split(' ');
+    setTimeout(()=>{doSearch(curVer, query)}, 0);
+}
+
+function doSearch(ver, query) {
+    let result = [];
+    for (let key in cardDb) {
+        if (ver !== curVer) {
+            return;
+        }
+        let hit = true;
+        for (let word of query) {
+            let matched = false;
+            for (let name of cardDb[key].names) {
+                if (name.toLowerCase().includes(word.toLowerCase())) {
+                    matched = true;
+                    break;
+                }
+            }
+            if (!matched) {
+                hit = false;
+                break;
+            }
+        }
+        if (hit) {
+            result.push({"id": key, "name": cardDb[key].names[0]})
+        }
+    }
+    if (ver !== curVer) return;
+    result.sort((a, b) => {
+        return cardDb[a.id].cid - cardDb[b.id].cid;
+    });
+    resultCards = result;
+    curPage = 0;
+    showCards();
+}
+
+function onPrevPage() {
+    if (curPage > 0) {
+        curPage -= 1;
+        showCards();
+    }
+}
+
+function onNextPage() {
+    if (curPage < Math.floor(resultCards.length / 10) - 1) {
+        curPage += 1;
+        showCards();
+    }
+}
 
 export {
-    resultCards,
+    changeInput,
+    onPrevPage,
+    onNextPage,
+    showingCards,
 };

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/data/cardDb.js


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor