New rewrite with svelte and pocketbase
This commit is contained in:
parent
72bfc2ed89
commit
160617af60
95 changed files with 4402 additions and 0 deletions
34
frontend/src/app.css
Normal file
34
frontend/src/app.css
Normal file
|
@ -0,0 +1,34 @@
|
|||
@import 'tailwindcss';
|
||||
|
||||
@font-face {
|
||||
font-family: "Beleren";
|
||||
src: url("/fonts/Beleren2016-Bold.woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter-Tight-Normal";
|
||||
src: url("/fonts/inter-tight-latin-400-normal.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter-Tight-Italic";
|
||||
src: url("/fonts/inter-tight-latin-400-italic.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter-Tight-Bold";
|
||||
src: url("/fonts/inter-tight-latin-800-normal.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter-Tight-Bold-Italic";
|
||||
src: url("/fonts/inter-tight-latin-800-italic.ttf");
|
||||
}
|
||||
|
||||
.font-beleren {
|
||||
font-family: 'Beleren';
|
||||
}
|
||||
|
||||
.font-inter-tight {
|
||||
font-family: 'Inter-Tight-Normal';
|
||||
}
|
13
frontend/src/app.d.ts
vendored
Normal file
13
frontend/src/app.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
12
frontend/src/app.html
Normal file
12
frontend/src/app.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
17
frontend/src/lib/components/Card.svelte
Normal file
17
frontend/src/lib/components/Card.svelte
Normal file
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
let { name = "", url = "#", sanitizedName = "", smallImage = "", normalImage="", price = 0, cardmarketUri = "", numberOfDecks = 0, numberOfPossibleDecks = undefined, synergy = undefined } = $props()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full gap-0">
|
||||
<a class="w-full" href={url}><img src={normalImage} alt={"Scan de carte pour " + name} class="w-full rounded-md aspect-[488/680]" loading="lazy"/></a>
|
||||
<a href={cardmarketUri} target="_blank" class="text-xs w-full text-center mt-2">{price}€</a>
|
||||
<span class="w-full text-center text-xs">{name}</span>
|
||||
{#if numberOfPossibleDecks == undefined}
|
||||
<span class="w-full text-center">{numberOfDecks} Decks</span>
|
||||
{:else}
|
||||
<span class="w-full text-center">{numberOfDecks} Decks sur {numberOfPossibleDecks} ({numberOfPossibleDecks != 0 ? Math.round(100 * (numberOfDecks / numberOfPossibleDecks)) : 0}%)</span>
|
||||
{/if}
|
||||
{#if synergy != undefined}
|
||||
<span class="w-full text-center">Synergy {synergy > 0 ? "+" + Math.round(100*synergy).toString() : Math.round(100*synergy).toString()}%</span>
|
||||
{/if}
|
||||
</div>
|
7
frontend/src/lib/components/CardGrid.svelte
Normal file
7
frontend/src/lib/components/CardGrid.svelte
Normal file
|
@ -0,0 +1,7 @@
|
|||
<script>
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1 pl-4 pr-4 md:grid-cols-3 lg:grid-cols-5 gap-4 w-full max-w-6xl">
|
||||
{@render children()}
|
||||
</div>
|
38
frontend/src/lib/components/CardSearch.svelte
Normal file
38
frontend/src/lib/components/CardSearch.svelte
Normal file
|
@ -0,0 +1,38 @@
|
|||
<script>
|
||||
import Input from "./base/Input.svelte";
|
||||
|
||||
let { dialog = $bindable() } = $props()
|
||||
|
||||
let searchQuery = $state("")
|
||||
let searchResult = $state([])
|
||||
|
||||
async function search() {
|
||||
searchResult = []
|
||||
if(searchQuery.length > 3) {
|
||||
fetch("/api/search?q=" + searchQuery)
|
||||
.then( response => response.json() )
|
||||
.then( data => { searchResult = data; console.log(data) } )
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog class="max-w-96 w-full backdrop:backdrop-blur-sm backdrop:bg-black/30 p-4 rounded-md"
|
||||
style="margin: auto; width: calc(100% - 20px);"
|
||||
bind:this={dialog}
|
||||
onclick={(e) => { if (e.target === dialog) dialog.close(); }}>
|
||||
<div>
|
||||
<Input type="text" placeholder="Rechercher des cartes" bind:value={searchQuery} oninput={search} focus/>
|
||||
<div class="mt-4 flex flex-col gap-2 h-64 overflow-y-scroll items-center">
|
||||
{#if searchResult.length == 0}
|
||||
<span class="mt-8 text-xs">Pas de résultat...</span>
|
||||
{:else}
|
||||
{#each searchResult as result}
|
||||
<a class="flex flex-col gap-0 w-full" href={result.Url}>
|
||||
<span>{result.Name}</span>
|
||||
<span class="text-xs">{result.SetName}</span>
|
||||
</a>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
3
frontend/src/lib/components/Footer.svelte
Normal file
3
frontend/src/lib/components/Footer.svelte
Normal file
|
@ -0,0 +1,3 @@
|
|||
<div class="w-full flex flex-row justify-center text-xs text-center p-4">
|
||||
<span>Brawlset is unofficial Fan Content permitted under the Fan Content Policy. Not approved/endorsed by Wizards. Portions of the materials used are property of Wizards of the Coast. ©Wizards of the Coast LLC.</span>
|
||||
</div>
|
226
frontend/src/lib/components/Navbar.svelte
Normal file
226
frontend/src/lib/components/Navbar.svelte
Normal file
|
@ -0,0 +1,226 @@
|
|||
<script>
|
||||
import { getContext, onMount } from "svelte";
|
||||
import Blue from "./icons/Blue.svelte";
|
||||
import White from "./icons/White.svelte";
|
||||
import Black from "./icons/Black.svelte";
|
||||
import Colorless from "./icons/Colorless.svelte";
|
||||
import Red from "./icons/Red.svelte";
|
||||
import Green from "./icons/Green.svelte";
|
||||
import IconBase from "./icons/IconBase.svelte";
|
||||
import CardSearch from "./CardSearch.svelte";
|
||||
import SearchIcon from "./icons/SearchIcon.svelte";
|
||||
import DeckIcon from "./icons/DeckIcon.svelte";
|
||||
import MenuIcon from "./icons/MenuIcon.svelte";
|
||||
|
||||
let dialog
|
||||
|
||||
let username = $state("")
|
||||
|
||||
let brawlsets = $state([])
|
||||
let isLoggedIn = $state(false)
|
||||
|
||||
onMount( async () => {
|
||||
const storageData = window.localStorage.getItem("pocketbase_auth")
|
||||
if (storageData != null) {
|
||||
const jsonData = JSON.parse(storageData)
|
||||
username = jsonData.record.name
|
||||
isLoggedIn = true
|
||||
}
|
||||
|
||||
fetch("/json/misc/brawlsets")
|
||||
.then( response => response.json() )
|
||||
.then( data => { brawlsets = data; console.log(data) } )
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<div class="font-inter-tight flex flex-row justify-between w-screen p-4 h-16">
|
||||
<a class="flex flex-row gap-2 items-center" href="/">
|
||||
<img alt="brawlset website logo" src="/assets/logo.png" class="h-8" />
|
||||
<span class="font-beleren text-3xl mt-2 bg-gradient-to-r from-black to-orange-500 bg-clip-text text-transparent">BrawlSet</span>
|
||||
</a>
|
||||
<div class="hidden flex-row gap-4 items-center md:flex">
|
||||
<details class="dropdown w-0 md:w-fit">
|
||||
<summary role="button">
|
||||
<a class="cursor-pointer text-stone-500">Commandants</a>
|
||||
</summary>
|
||||
<ul>
|
||||
<li>Top commandants</li>
|
||||
<li>
|
||||
<div>Mono</div>
|
||||
<ul>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><White/><span>Blanc</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><Blue/><span>Bleu</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><Black/><span>Noir</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><Red/><span>Rouge</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><Green/><span>Vert</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><Colorless/><span>Incolor</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div>2 couleurs</div>
|
||||
<ul>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Blue/></div><span>Azorius</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Blue/><Black/></div><span>Dimir</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Black/><Red/></div><span>Rakdos</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Red/><Green/></div><span>Gruul</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Green/><White/></div><span>Selesnya</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Black/></div><span>Orzhov</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Blue/><Red/></div><span>Izzet</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Black/><Green/></div><span>Golgari</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Red/><White/></div><span>Boros</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Green/><Blue/></div><span>Simic</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div>3 couleurs</div>
|
||||
<ul>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Blue/><Black/></div><span>Esper</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Blue/><Black/><Red/></div><span>Grixis</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Black/><Red/><Green/></div><span>Jund</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Red/><Green/><White/></div><span>Naya</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Green/><White/><Blue/></div><span>Bant</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Black/><Green/></div><span>Abzan</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Blue/><Red/><White/></div><span>Jeskai</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Black/><Green/><Blue/></div><span>Sultai</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Red/><White/><Black/></div><span>Mardu</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Green/><Blue/><Red/></div><span>Temur</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<div>4+ couleurs</div>
|
||||
<ul>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Blue/><Black/><Red/></div><span>Yore-Tiller</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Blue/><Black/><Red/><Green/></div><span>Glint-Eye</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Black/><Red/><Green/><White/></div><span>Dune-Brood</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Red/><Green/><White/><Blue/></div><span>Ink-Treader</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><Green/><White/><Blue/><Black/></div><span>Witch-Maw</span></a></li>
|
||||
<li><a class="flex flex-row items-center gap-2" href="#"><div class="flex flex-row gap-0"><White/><Blue/><Black/><Red/><Green/></div><span>5 couleurs</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<details class="dropdown">
|
||||
<summary role="button">
|
||||
<a class="cursor-pointer text-stone-500">BSets</a>
|
||||
</summary>
|
||||
<ul>
|
||||
{#each brawlsets.slice(0,5) as brawlset}
|
||||
<li>
|
||||
<a class="flex flex-row gap-2 items-center" href={"/bset/" + brawlset.SanitizedName}>
|
||||
<div class="flex flex-row gap-1">
|
||||
{#each brawlset.IconsSvgUri as icon}
|
||||
<IconBase src={icon} alt="brawlset icon"/>
|
||||
{/each}
|
||||
</div>
|
||||
{brawlset.Name}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
<li><a href="/bset/all">Plus de BSets...</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a class="cursor-pointer text-stone-500" href="/regles">Règles</a>
|
||||
<a class="cursor-pointer text-stone-500" href="/faq">F.A.Q</a>
|
||||
</div>
|
||||
<div class="flex flex-row gap-4 items-center text-stone-500">
|
||||
<button class="flex flex-row items-center" onclick={() => dialog.showModal()}>
|
||||
<SearchIcon class="visible w-fit md:invisible md:w-0" />
|
||||
<span class="invisible w-0 md:w-fit md:visible">Rechercher</span>
|
||||
</button>
|
||||
{#if isLoggedIn}
|
||||
<a class="cursor-pointer flex flex-row items-center" href="/decks">
|
||||
<DeckIcon class="visible w-fit md:invisible md:w-0" />
|
||||
<span class="invisible w-0 md:w-fit md:visible">Decks</span>
|
||||
</a>
|
||||
<a class="cursor-pointer hidden md:inline" href="/profil">
|
||||
<span class="invisible w-0 md:w-fit md:visible">{username}</span>
|
||||
</a>
|
||||
{:else}
|
||||
<a class="cursor-pointer" href="/connexion">Connexion</a>
|
||||
<a class="cursor-pointer" href="#">Inscription</a>
|
||||
{/if}
|
||||
<details class="dropdown inline md:hidden">
|
||||
<summary role="button">
|
||||
<a class="cursor-pointer"><MenuIcon /></a>
|
||||
</summary>
|
||||
<ul>
|
||||
{#each brawlsets.slice(0,5) as brawlset}
|
||||
<li>
|
||||
<a class="flex flex-row gap-2 items-center" href={"/bset/" + brawlset.SanitizedName}>
|
||||
<div class="flex flex-row gap-1">
|
||||
{#each brawlset.IconsSvgUri as icon}
|
||||
<IconBase src={icon} alt="brawlset icon"/>
|
||||
{/each}
|
||||
</div>
|
||||
{brawlset.Name}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
<li><a href="/bset/all">Plus de BSets...</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CardSearch bind:dialog={dialog} />
|
||||
|
||||
<style>
|
||||
/* Close the dropdown with outside clicks */
|
||||
.dropdown > summary::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdown[open] > summary::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dropdown summary {
|
||||
list-style: none;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.dropdown ul {
|
||||
white-space: nowrap;
|
||||
position: absolute;
|
||||
margin-top: 5px;
|
||||
padding: 5px;
|
||||
box-sizing: border-box;
|
||||
z-index: 2;
|
||||
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #D3D3D3;
|
||||
box-shadow: 0px 4px 5px #D3D3D3;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.dropdown li {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown ul ul {
|
||||
display: none;
|
||||
left: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.dropdown li:hover {
|
||||
background-color: hsl(240 4.8% 95.9%);
|
||||
}
|
||||
|
||||
.dropdown li:hover > ul {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
8
frontend/src/lib/components/base/Button.svelte
Normal file
8
frontend/src/lib/components/base/Button.svelte
Normal file
|
@ -0,0 +1,8 @@
|
|||
<script>
|
||||
|
||||
let { children, ...restProps } = $props()
|
||||
</script>
|
||||
|
||||
<button class="bg-orange-500 cursor-pointer text-white rounded-md p-2" {...restProps}>
|
||||
{@render children()}
|
||||
</button>
|
5
frontend/src/lib/components/base/Input.svelte
Normal file
5
frontend/src/lib/components/base/Input.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
let { type, placeholder = "", name = "", value = $bindable(), ...restProps} = $props()
|
||||
</script>
|
||||
|
||||
<input type={type} placeholder={placeholder} name={name} class="flex h-auto min-h-9 w-full border-l-orange-500 border-l-4 bg-transparent px-3 py-1 text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50" bind:value {...restProps} />
|
5
frontend/src/lib/components/icons/Black.svelte
Normal file
5
frontend/src/lib/components/icons/Black.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/B.svg" alt="Black mana icon"/>
|
5
frontend/src/lib/components/icons/Blue.svelte
Normal file
5
frontend/src/lib/components/icons/Blue.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/U.svg" alt="Blue mana icon"/>
|
5
frontend/src/lib/components/icons/Colorless.svelte
Normal file
5
frontend/src/lib/components/icons/Colorless.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/C.svg" alt="Colorless mana icon"/>
|
6
frontend/src/lib/components/icons/DeckIcon.svelte
Normal file
6
frontend/src/lib/components/icons/DeckIcon.svelte
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" {...$$props}>
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<rect width="18" height="18" x="3" y="3" rx="2" />
|
||||
<path d="M3 9a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2M3 11h3c.8 0 1.6.3 2.1.9l1.1.9c1.6 1.6 4.1 1.6 5.7 0l1.1-.9c.5-.5 1.3-.9 2.1-.9H21" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 399 B |
5
frontend/src/lib/components/icons/Green.svelte
Normal file
5
frontend/src/lib/components/icons/Green.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/G.svg" alt="Green mana icon"/>
|
5
frontend/src/lib/components/icons/IconBase.svelte
Normal file
5
frontend/src/lib/components/icons/IconBase.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
let { src, alt } = $props();
|
||||
</script>
|
||||
|
||||
<img src={src} alt={alt} loading="lazy" class="min-h-4 min-w-4 size-4" />
|
3
frontend/src/lib/components/icons/MenuIcon.svelte
Normal file
3
frontend/src/lib/components/icons/MenuIcon.svelte
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" {...$$props}>
|
||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 12h16M4 6h16M4 18h16" />
|
||||
</svg>
|
After Width: | Height: | Size: 240 B |
5
frontend/src/lib/components/icons/Red.svelte
Normal file
5
frontend/src/lib/components/icons/Red.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/R.svg" alt="Red mana icon"/>
|
6
frontend/src/lib/components/icons/SearchIcon.svelte
Normal file
6
frontend/src/lib/components/icons/SearchIcon.svelte
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" {...$$props}>
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21l-4.3-4.3" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 279 B |
5
frontend/src/lib/components/icons/White.svelte
Normal file
5
frontend/src/lib/components/icons/White.svelte
Normal file
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import IconBase from "./IconBase.svelte";
|
||||
</script>
|
||||
|
||||
<IconBase src="https://svgs.scryfall.io/card-symbols/W.svg" alt="White mana icon"/>
|
1
frontend/src/lib/index.ts
Normal file
1
frontend/src/lib/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
// place files you want to import through the `$lib` alias in this folder.
|
15
frontend/src/routes/+layout.svelte
Normal file
15
frontend/src/routes/+layout.svelte
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import Navbar from '$lib/components/Navbar.svelte';
|
||||
import Footer from '$lib/components/Footer.svelte';
|
||||
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="w-screen min-h-screen flex flex-col gap-0">
|
||||
<Navbar />
|
||||
<div class="mt-8 font-inter-tight grow">
|
||||
{@render children()}
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
6
frontend/src/routes/+page.svelte
Normal file
6
frontend/src/routes/+page.svelte
Normal file
|
@ -0,0 +1,6 @@
|
|||
<div class="flex flex-col w-full items-center mt-8 md:mt-32 p-4 text-center">
|
||||
<p class="mb-12 text-3xl text-orange-500">CE SITE EST EN COURS DE DEVELOPPEMENT - NE SAUVEGARDEZ PAS VOS DONNÉES UNIQUEMENT SUR BRAWLSET</p>
|
||||
<h1 class="text-8xl font-beleren">The BrawlSet</h1>
|
||||
<p class="text-center text-stone-500 mt-12">Un système de règles MTG basé sur le mode de jeu commander et inventé à Rennes, pays de la galette saucisse.<br />
|
||||
Pour plus d'informations allez voir les <a href="/rules" class="text-orange-500">règles</a> ou la <a href="/faq" class="text-orange-500">FAQ</a>.</p>
|
||||
</div>
|
95
frontend/src/routes/bset/[slug]/+page.svelte
Normal file
95
frontend/src/routes/bset/[slug]/+page.svelte
Normal file
|
@ -0,0 +1,95 @@
|
|||
<script lang="ts">
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import CardGrid from '$lib/components/CardGrid.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import type { PageProps } from './$types';
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let commander = $state([])
|
||||
let creature = $state([])
|
||||
let instant = $state([])
|
||||
let planeswalker = $state([])
|
||||
let artifact = $state([])
|
||||
let sorcery = $state([])
|
||||
let enchantment = $state([])
|
||||
let land = $state([])
|
||||
let slug = $derived(data.slug)
|
||||
let test = $state("")
|
||||
|
||||
$effect(() => {
|
||||
commander = creature = instant = planeswalker = artifact = sorcery = enchantment = []
|
||||
|
||||
fetch("/json/brawlset/"+slug)
|
||||
.then( response => response.json() )
|
||||
.then( data => {
|
||||
commander = data.Cards.commander
|
||||
creature = data.Cards.creature
|
||||
sorcery = data.Cards.sorcery
|
||||
instant = data.Cards.instant
|
||||
land = data.Cards.land
|
||||
enchantment = data.Cards.enchantment
|
||||
artifact = data.Cards.artifact
|
||||
planeswalker = data.Cards.planeswalker
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full items-center">
|
||||
<h1>{slug}</h1>
|
||||
|
||||
<h2>Commandants</h2>
|
||||
<CardGrid>
|
||||
{#each commander as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks}/>
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Planeswalker</h2>
|
||||
<CardGrid>
|
||||
{#each planeswalker as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Creature</h2>
|
||||
<CardGrid>
|
||||
{#each creature as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Rituels</h2>
|
||||
<CardGrid>
|
||||
{#each sorcery as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Artefacts</h2>
|
||||
<CardGrid>
|
||||
{#each artifact as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Éphémères</h2>
|
||||
<CardGrid>
|
||||
{#each instant as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Enchantements</h2>
|
||||
<CardGrid>
|
||||
{#each enchantment as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Terrains</h2>
|
||||
<CardGrid>
|
||||
{#each land as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
</div>
|
6
frontend/src/routes/bset/[slug]/+page.ts
Normal file
6
frontend/src/routes/bset/[slug]/+page.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = ({ params }) => {
|
||||
console.log("test")
|
||||
return {slug: params.slug, test: "yeeeeheeee"}
|
||||
};
|
29
frontend/src/routes/bset/all/+page.svelte
Normal file
29
frontend/src/routes/bset/all/+page.svelte
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import Input from "$lib/components/base/Input.svelte";
|
||||
import IconBase from "$lib/components/icons/IconBase.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let brawlsets = $state([])
|
||||
onMount(async () => {
|
||||
fetch("/json/misc/brawlsets")
|
||||
.then( response => response.json() )
|
||||
.then( data => { brawlsets = data; console.log(data) } )
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="m-auto mt-16 max-w-4xl flex flex-col gap-4">
|
||||
<Input type="text" placeholder="Rechercher des brawlsets" />
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
{#each brawlsets as brawlset}
|
||||
<a class="flex flex-row gap-2 items-center" href={"/bset/" + brawlset.SanitizedName}>
|
||||
<div class="flex flex-row items-center">
|
||||
{#each brawlset.IconsSvgUri as icon}
|
||||
<IconBase src={icon} alt={"Set icon for " + brawlset.Name} />
|
||||
{/each}
|
||||
</div>
|
||||
<span>{brawlset.Name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
90
frontend/src/routes/commander/[slug]/+page.svelte
Normal file
90
frontend/src/routes/commander/[slug]/+page.svelte
Normal file
|
@ -0,0 +1,90 @@
|
|||
<script lang="ts">
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import CardGrid from '$lib/components/CardGrid.svelte';
|
||||
import type { PageProps } from './$types';
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let creature = $state([])
|
||||
let instant = $state([])
|
||||
let planeswalker = $state([])
|
||||
let artifact = $state([])
|
||||
let sorcery = $state([])
|
||||
let enchantment = $state([])
|
||||
let land = $state([])
|
||||
let slug = $derived(data.slug)
|
||||
|
||||
let mainCardData = $state({})
|
||||
|
||||
$effect(() => {
|
||||
creature = instant = planeswalker = artifact = sorcery = enchantment = []
|
||||
console.log(data)
|
||||
|
||||
fetch("/json/commander/"+slug)
|
||||
.then( response => response.json() )
|
||||
.then( data => {
|
||||
creature = data.Cards.creature
|
||||
sorcery = data.Cards.sorcery
|
||||
instant = data.Cards.instant
|
||||
land = data.Cards.land
|
||||
enchantment = data.Cards.enchantment
|
||||
artifact = data.Cards.artifact
|
||||
planeswalker = data.Cards.planeswalker
|
||||
mainCardData = data.MainCard
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full items-center">
|
||||
<div class="w-full max-w-64">
|
||||
<Card class="" normalImage={mainCardData.NormalImage} name={mainCardData.Name} price={mainCardData.Price} mainCardDatamarketUri={mainCardData.CardmarketUri} numberOfDecks={mainCardData.NumberOfDecks} />
|
||||
</div>
|
||||
|
||||
<h2>Planeswalker</h2>
|
||||
<CardGrid>
|
||||
{#each planeswalker as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Creature</h2>
|
||||
<CardGrid>
|
||||
{#each creature as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Rituels</h2>
|
||||
<CardGrid>
|
||||
{#each sorcery as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Artefacts</h2>
|
||||
<CardGrid>
|
||||
{#each artifact as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Éphémères</h2>
|
||||
<CardGrid>
|
||||
{#each instant as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Enchantements</h2>
|
||||
<CardGrid>
|
||||
{#each enchantment as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
|
||||
<h2>Terrains</h2>
|
||||
<CardGrid>
|
||||
{#each land as card}
|
||||
<Card normalImage={card.NormalImage} url={card.Url} name={card.Name} price={card.Price} cardmarketUri={card.CardmarketUri} numberOfDecks={card.NumberOfDecks} numberOfPossibleDecks={card.NumberOfPossibleDecks} synergy={card.Synergy} />
|
||||
{/each}
|
||||
</CardGrid>
|
||||
</div>
|
6
frontend/src/routes/commander/[slug]/+page.ts
Normal file
6
frontend/src/routes/commander/[slug]/+page.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = ({ params }) => {
|
||||
console.log("test")
|
||||
return {slug: params.slug, test: "yeeeeheeee"}
|
||||
};
|
34
frontend/src/routes/connexion/+page.svelte
Normal file
34
frontend/src/routes/connexion/+page.svelte
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import PocketBase from 'pocketbase';
|
||||
import Input from '$lib/components/base/Input.svelte';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
const pb = new PocketBase('http://localhost:8090');
|
||||
|
||||
async function login(form) {
|
||||
const formData = new FormData(form.target)
|
||||
const data = {};
|
||||
for (let field of formData) {
|
||||
const [key, value] = field;
|
||||
data[key] = value;
|
||||
}
|
||||
console.log(data)
|
||||
try {
|
||||
const authData = await pb.collection('users').authWithPassword(data.email, data.password);
|
||||
console.log(authData)
|
||||
console.log(pb.authStore.token);
|
||||
window.location.href = "/"
|
||||
} catch (err) {
|
||||
console.log("Failed to log");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center mt-32 h-full">
|
||||
<form class="flex flex-col gap-4 w-lg" on:submit|preventDefault={(e) => login(e)}>
|
||||
<Input name="email" placeholder="email" type="email" />
|
||||
<Input name="password" placeholder="password" type="password" />
|
||||
<button class="border rounded p-2 bg-gray-800 text-white hover:bg-gray-700">Connexion</button>
|
||||
</form>
|
||||
</div>
|
104
frontend/src/routes/decks/+page.svelte
Normal file
104
frontend/src/routes/decks/+page.svelte
Normal file
|
@ -0,0 +1,104 @@
|
|||
<script>
|
||||
import Button from "$lib/components/base/Button.svelte";
|
||||
import Input from "$lib/components/base/Input.svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let brawlsets = $state([])
|
||||
let deckImporter = $state("")
|
||||
let selectedBset = $state("")
|
||||
let deckName = $state("")
|
||||
let deckUrl = $state("")
|
||||
let commander = $state("")
|
||||
let token = $state("")
|
||||
|
||||
onMount( async () => {
|
||||
const storageData = window.localStorage.getItem("pocketbase_auth")
|
||||
if (storageData != null) {
|
||||
const jsonData = JSON.parse(storageData)
|
||||
token = jsonData.token
|
||||
}
|
||||
|
||||
fetch("/json/misc/brawlsets")
|
||||
.then( response => response.json() )
|
||||
.then( data => {
|
||||
let sortedData = data
|
||||
sortedData.sort((a,b) => a.Name.localeCompare(b.Name))
|
||||
brawlsets = sortedData
|
||||
})
|
||||
})
|
||||
|
||||
function setCommander(txt) {
|
||||
let lines = txt.split("\n")
|
||||
commander = lines[lines.length - 1].slice(1)
|
||||
}
|
||||
|
||||
function getDataFromLine(line){
|
||||
if(line != "") {
|
||||
const data = line.split(" ")
|
||||
const amount = parseInt(data[0])
|
||||
const name = data.slice(1).join(" ").split(" // ")[0].split("/")[0]
|
||||
return {"name":name, "amount":amount}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function importDeck(){
|
||||
const deckText = deckImporter
|
||||
let lines = deckText.split("\n")
|
||||
lines = lines.filter((line) => line.match(/[0-9]+\s[\w]+/) != undefined)
|
||||
const dataToSend = { name: deckName, url: deckUrl, selected_bset: selectedBset,commander_name: getDataFromLine(commander).name, cards: [] }
|
||||
lines.slice(0, lines.length - 1).forEach((line) => {
|
||||
const data = getDataFromLine(line)
|
||||
if(data != null) {
|
||||
dataToSend.cards.push(data)
|
||||
}
|
||||
});
|
||||
|
||||
console.log(dataToSend)
|
||||
|
||||
fetch('/api/deck/create', {
|
||||
method: "POST",
|
||||
headers: {Authorization: token, "Content-Type": "application/json"},
|
||||
body: JSON.stringify(dataToSend)
|
||||
}).then((res) => {
|
||||
if(res.status == 200) {
|
||||
res.json().then((apiData) => {
|
||||
deckImporter = ""
|
||||
selectedBset = ""
|
||||
deckName = ""
|
||||
deckUrl = ""
|
||||
commander = ""
|
||||
console.log(apiData)
|
||||
})
|
||||
} else if (res.status == 401) {
|
||||
res.json().then((apiData) => {
|
||||
console.log(apiData)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col w-full items-center p-4">
|
||||
<div class="flex flex-col-reverse md:flex-row max-w-4xl w-full gap-4">
|
||||
<div class="flex flex-col w-full">
|
||||
<textarea placeholder="Deck list dans le format MTGO" class="h-60" bind:value={deckImporter} oninput={(e) => setCommander(e.target.value)}></textarea>
|
||||
<span>Commandant : {commander}</span>
|
||||
</div>
|
||||
<div class="flex flex-col w-full gap-4">
|
||||
<Input type="text" placeholder="Nom du deck" bind:value={deckName} />
|
||||
<Input type="text" placeholder="Url du deck (facultatif)" bind:value={deckUrl} />
|
||||
<select bind:value={selectedBset}>
|
||||
<option value="0">Selectionnez un Brawlset...</option>
|
||||
{#each brawlsets as brawlset}
|
||||
<option value={brawlset.SanitizedName}>
|
||||
{brawlset.Name}
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Button onclick={importDeck}>Importer</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
4
frontend/src/routes/faq/+page.svelte
Normal file
4
frontend/src/routes/faq/+page.svelte
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div class="flex flex-col mt-16 w-full items-center">
|
||||
<h1>F.A.Q</h1>
|
||||
<p>Coming soon...</p>
|
||||
</div>
|
13
frontend/src/routes/profil/+page.svelte
Normal file
13
frontend/src/routes/profil/+page.svelte
Normal file
|
@ -0,0 +1,13 @@
|
|||
<script>
|
||||
import Button from "$lib/components/base/Button.svelte";
|
||||
|
||||
function removeAuth() {
|
||||
window.localStorage.removeItem("pocketbase_auth")
|
||||
window.location.href = "/"
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center mt-16">
|
||||
<Button onclick={removeAuth}>Deconnexion</Button>
|
||||
</div>
|
31
frontend/src/routes/regles/+page.svelte
Normal file
31
frontend/src/routes/regles/+page.svelte
Normal file
|
@ -0,0 +1,31 @@
|
|||
<div class="flex flex-col items-center">
|
||||
<div class="flex flex-col items-center w-full">
|
||||
<h1 class="text-3xl">BrawlSet Canon Règles Officielles</h1>
|
||||
<h2>BrawlSet Committee</h2>
|
||||
<h2>17 Octobre 2022</h2>
|
||||
</div>
|
||||
<div class="flex flex-col max-w-5xl gap-4 mt-8 mb-32">
|
||||
<h2 class="font-bold">903.BS. Le mode de jeu BrawlSet</h2>
|
||||
<ul class="flex flex-col gap-4">
|
||||
<li><strong>903.BS.1</strong> Le BrawlSet est une variante du Commander. Cette variante utilise les règles classiques du Commander avec les modifications suivantes.</li>
|
||||
<li><strong>903.BS.2</strong> La Règle d’Or du format est d’utiliser des cartes provenant du même set. Pour des raisons de niveau des cartes et/ou d’histoire, certains sets sont regroupés en « BSet »</li>
|
||||
<li><strong>903.BS.3</strong> La liste des BSets est disponible dans le document List_BSet_FR.pdf</li>
|
||||
<li><strong>903.BS.4</strong> Une carte fait partie d’un BSet si elle a été imprimée au moins une fois avec le symbole d’édition d’un des sets qui composent le BSet. La liste complète des symboles d’édition peut être trouvée dans la section Card Set Archive du site Magic de Wizard of the Coast (<a href="https://magic.wizards.com/en/products/card-set-archive">lien</a>)</li>
|
||||
<li>Exemple : <em>Cela prend en compte les cartes Timeshifted pour Time Spiral, les cartes de Brawl Decks, de Planeswalker Decks et de Theme Boosters, ainsi que les cartes de Buy a Box. Cela ne prend pas en compte les cartes des Masterpiece series (Explorer d’Ixalan, Expeditions de Zendikar, Inventions de Kaladesh, Invocations de Amonkhet, et Planeswalker Mythic Editions de Guilds of Ravnica, Ravnica Allegeance et War of the Spark).</em></li>
|
||||
<li><strong>903.BS.5</strong> Il n’y a pas de sideboard pour modifier son deck entre deux parties de BrawlSet. Cependant, comme certaines cartes permettent d’amener des cartes dans la partie depuis l’extérieur du jeu, les joueur·euses peuvent avoir un « wishboard ». Le nombre maximum de cartes d’un wishboard est 15, il n’y pas de nombre minimum de cartes.</li>
|
||||
<li><strong>903.BS.6</strong> Les cartes des wishboard sont hors de la partie, ainsi aucun effet de la partie ne peut permettre à un·e joueur·euse de regarder ou rechercher dans le wishboard d’un·e autre joueur·euse. Un·e joueur·euse ne peut amener une carte hors de la partie dans la partie sauf si cette carte vient de son wishboard ou d’une partie principale d’une sous-partie. Il n’y a pas de restriction sur l’identité colorée des cartes qu’un·e joueur·euse peut amener dans la partie depuis l’extérieur du jeu, ceci est une modification de la règle 903.11.</li>
|
||||
<li>Exemple : <em>Léon lance Emrakul, the Promised End pour prendre le contrôle de l’adversaire ciblé durant son prochain tour et prend le contrôle du prochain tour de Dylan. Léon peut faire en sorte que Dylan lance Granted, l’aventure de Fae of Wishes pendant qu’il contrôle le tour de Dylan. Léon ne pourra pas chercher ou trouver quoi que ce soit dans le wishboard de Dylan. Parce que l’effet demande de chercher dans le wishboard du joueur / de la joueuse lançant cette carte, Léon ne peut pas chercher dans son wishboard. Aucune carte ne sera amenée dans la partie.</em></li>
|
||||
<li><strong>903.BS.7</strong> Un·e joueur·euse désigne comme commandant un Planeswalker légendaire ou une créature légendaire. Ceci est une modification de la règle 903.3</li>
|
||||
<li><strong>903.BS.8</strong> Si un deck de BrawlSet utilise un·e compagnon·ne, le/la compagnon·ne doit respecter l’identité colorée du commandant.</li>
|
||||
<li><strong>903.BS.9</strong> La règle de construction en singleton du Commander s’applique à la combinaison du deck et du wishboard. Ceci est une modification de la règle 903.5b</li>
|
||||
<li><strong>903.BS.10</strong> Toutes les cartes d’un deck de BrawlSet doivent faire partie du même BSet. Cette règle s’applique à la combinaison du deck et du wishboard. Cette règle s’applique aux terrains de base. Ce BSet est le BSet du deck.</li>
|
||||
<li><strong>903.BS.11</strong> Une carte avec un type de terrain de base ne peut être inclue dans un deck que si elle ne produit que du mana de l’identité colorée du commandant. Si aucun terrain de base faisant partie du BSet de ce deck ne vérifie cette propriété, cette règle ne s’applique pas pour les terrains de base pour ce deck. Ceci est une modification de la règle 903.5d.</li>
|
||||
<li>Exemple : <em>Paul veut construire un deck de BrawlSet War of the Spark avec Karn, the Great Creator en commandant. Comme l’identité colorée de Karn est incolore et que les Wastes n’ont pas été éditées dans War of the Spark, Paul peut utiliser n’importe quel terrain de base édité dans War of the Spark pour construire son deck, par exemple une Forest, huit Islands, neuf Mountains, treize Plains et quinze Swamps.</em></li>
|
||||
<li><strong>903.BS.12</strong> La taille minimum d’un deck de BrawlSet est 60, il n’y a pas de taille maximum de deck. Ceci est une modification de la règle 903.5a.</li>
|
||||
<li><strong>903.BS.13</strong> Dans une partie de BrawlSet, le nombre de point de vie de départ est 20. Ceci est une modification de la règle 119.1c</li>
|
||||
<li><strong>903.BS.14</strong> Les parties de BrawlSet n’utilisent pas la règle des blessures de commandant, ainsi l’action décrite dans la règle 704.5v, qui fait perdre un·e joueur·euse qui a subi 21 blessures de combat ou plus d’un commandant ne s’applique pas. Ceci est une modification de la règle 704.5v.</li>
|
||||
<li><strong>903.BS.15</strong> Au début d’un match, chaque joueur·euse doit déclarer son BSet, il ne peut pas être changé entre les parties d’un même match.</li>
|
||||
<li><strong>903.BS.16</strong> Le format de BrawlSet utilise la règle du commander swap. Cela signifie que n’importe quel joueur·euse peut changer de commandant entre deux parties d’un match. Le nouveau commandant doit être une carte du deck, l’ancien commandant devient une carte normale du deck. Le commandant du deck reste secret jusqu’à ce qu’il soit révélé au début de la partie.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue