545 lines
16 KiB
Svelte
545 lines
16 KiB
Svelte
<script>
|
|
import { onMount } from 'svelte';
|
|
|
|
// A fake card for display purposes
|
|
const exampleCard = {
|
|
name: "Harald Bluetooth",
|
|
image_link: "https://upload.wikimedia.org/wikipedia/commons/thumb/c/ce/1200_Harald_Bl%C3%A5tand_anagoria.jpg/330px-1200_Harald_Bl%C3%A5tand_anagoria.jpg",
|
|
card_rarity: "rare",
|
|
card_type: "person",
|
|
wikidata_instance: "Q5",
|
|
text: "Harald \"Bluetooth\" Gormsson was a king of Denmark and Norway.",
|
|
attack: 351,
|
|
defense: 222,
|
|
cost: 5,
|
|
created_at: new Date().toISOString(),
|
|
reported: false,
|
|
};
|
|
|
|
const annotations = [
|
|
{ number: 1, label: "Name", description: "The name of the Wikipedia article this card was generated from." },
|
|
{ number: 2, label: "Type", description: "The category of the subject — Person, Location, Artwork, etc." },
|
|
{ number: 3, label: "Rarity badge", description: "Rarity is determined by the article's WikiRank quality score. From lowest to highest: Common, Uncommon, Rare, Super Rare, Epic, and Legendary." },
|
|
{ number: 4, label: "Wikipedia link", description: "Opens the Wikipedia article this card was generated from." },
|
|
{ number: 5, label: "Cost bubbles", description: "How much energy it costs to play this card. Derived from the card's attack and defense stats." },
|
|
{ number: 6, label: "Article text", description: "The opening paragraph of the Wikipedia article." },
|
|
{ number: 7, label: "Attack", description: "Determines how much damage this card deals when it attacks. Based on how many Wikipedia language editions the article appears in." },
|
|
{ number: 8, label: "Defense", description: "How much damage this card can absorb before being destroyed. Based on the article's monthly page views." },
|
|
];
|
|
|
|
// Annotation positions as percentage of card width/height
|
|
const markerPositions = [
|
|
{ number: 1, x: 15, y: 3 }, // name — top center
|
|
{ number: 2, x: 75, y: 3 }, // type badge — top right
|
|
{ number: 3, x: 14, y: 20 }, // rarity badge — top left of image
|
|
{ number: 4, x: 85, y: 20 }, // wiki link — top right of image
|
|
{ number: 5, x: 15, y: 53 }, // cost bubbles — bottom left of image
|
|
{ number: 6, x: 50, y: 73 }, // text — middle
|
|
{ number: 7, x: 15, y: 88 }, // attack — bottom left
|
|
{ number: 8, x: 85, y: 88 }, // defense — bottom right
|
|
];
|
|
</script>
|
|
|
|
<main>
|
|
<div class="content">
|
|
<h1 class="page-title">How to Play</h1>
|
|
<section class="section">
|
|
<h2 class="section-title">Understanding Your Cards</h2>
|
|
<div class="card-explainer">
|
|
<div class="card-annotated">
|
|
<div class="card-display">
|
|
<!-- Inline card rendering matching Card.svelte visuals -->
|
|
<div class="demo-card">
|
|
<div class="demo-inner" style="--bg: #f0e0c8; --header: #b87830">
|
|
<div class="demo-header">
|
|
<span class="demo-name">{exampleCard.name}</span>
|
|
<span class="demo-type-badge">Person</span>
|
|
</div>
|
|
<div class="demo-image-wrap">
|
|
<img src={exampleCard.image_link} alt={exampleCard.name} class="demo-image" />
|
|
<div class="demo-rarity" style="background: #2a5a9b; color: #fff">R</div>
|
|
<a href="https://en.wikipedia.org/wiki/Harald_Bluetooth" target="_blank" rel="noopener" class="demo-wiki">
|
|
<svg viewBox="0 0 50 50" width="14" height="14"><circle cx="25" cy="25" r="24" fill="white" stroke="#888" stroke-width="1"/><text x="25" y="33" text-anchor="middle" font-family="serif" font-size="28" font-weight="bold" fill="#000">W</text></svg>
|
|
</a>
|
|
<div class="demo-cost-bubbles">
|
|
{#each { length: exampleCard.cost } as _}
|
|
<div class="demo-cost-bubble">✦</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
<div class="demo-divider"></div>
|
|
<div class="demo-text">{exampleCard.text}</div>
|
|
<div class="demo-footer" style="background: #e8d8b8">
|
|
<span class="demo-stat">ATK <strong>{exampleCard.attack}</strong></span>
|
|
<span class="demo-date">{new Date(exampleCard.created_at).toLocaleDateString()}</span>
|
|
<span class="demo-stat">DEF <strong>{exampleCard.defense}</strong></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Annotation markers -->
|
|
<div class="markers">
|
|
{#each markerPositions as pos}
|
|
<div class="marker" style="left: {pos.x}%; top: {pos.y}%">
|
|
<div class="marker-bubble">{pos.number}</div>
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
<ol class="annotation-list">
|
|
{#each annotations as a}
|
|
<li>
|
|
<span class="annotation-number">{a.number}</span>
|
|
<div class="annotation-content">
|
|
<span class="annotation-label">{a.label}</span>
|
|
<span class="annotation-desc">{a.description}</span>
|
|
</div>
|
|
</li>
|
|
{/each}
|
|
</ol>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2 class="section-title">Taking a Turn</h2>
|
|
<div class="rules-grid">
|
|
<div class="rule-card">
|
|
<div class="rule-icon">✦</div>
|
|
<h3 class="rule-title">Energy</h3>
|
|
<p class="rule-body">You start your first turn with 1 energy (or 2 if you're the second player). Each subsequent turn you gain one more, up to a maximum of 6. Energy does not carry over between turns.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">✋</div>
|
|
<h3 class="rule-title">Drawing</h3>
|
|
<p class="rule-body">At the start of your turn you draw cards until you have 5 in your hand. You cannot draw more than 5 cards.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">▶</div>
|
|
<h3 class="rule-title">Playing Cards</h3>
|
|
<p class="rule-body">Select a card from your hand, then click an empty slot on your side of the board. The card must have a cost less or equal to your current energy. You can have up to 5 cards in play at once.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">🗡</div>
|
|
<h3 class="rule-title">Sacrificing</h3>
|
|
<p class="rule-body">Click the dagger icon to enter sacrifice mode, then click one of your cards to remove it from play and recover its energy cost. Use this to afford expensive cards.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2 class="section-title">Combat</h2>
|
|
<p class="body-text">When you end your turn, all your cards attack simultaneously. Each card attacks the card directly opposite it:</p>
|
|
<div class="rules-grid">
|
|
<div class="rule-card">
|
|
<div class="rule-icon">⚔</div>
|
|
<h3 class="rule-title">Opposed Attack</h3>
|
|
<p class="rule-body">If there is an enemy card in the opposing slot, your card deals its ATK as damage to that card's DEF. If DEF reaches zero, the card is destroyed.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">❤</div>
|
|
<h3 class="rule-title">Direct Attack</h3>
|
|
<p class="rule-body">If the opposing slot is empty, your card attacks the opponent's life total directly, dealing damage equal to its ATK.</p>
|
|
</div>
|
|
<!-- <div class="rule-card">
|
|
<div class="rule-icon">🛡</div>
|
|
<h3 class="rule-title">No Overflow</h3>
|
|
<p class="rule-body">Excess damage does not carry through. A card that destroys an opposing card does not deal the remaining damage to the opponent's life total.</p>
|
|
</div> -->
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<h2 class="section-title">Winning & Losing</h2>
|
|
<div class="rules-grid">
|
|
<div class="rule-card">
|
|
<div class="rule-icon">💀</div>
|
|
<h3 class="rule-title">Life Total</h3>
|
|
<p class="rule-body">Each player starts with 500 life. Reduce your opponent's life to zero to win.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">🃏</div>
|
|
<h3 class="rule-title">No Cards Left</h3>
|
|
<p class="rule-body">If you have cards in play and your opponent has no playable cards remaining in their deck, hand, or board, you win.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">⏱</div>
|
|
<h3 class="rule-title">Timeout</h3>
|
|
<p class="rule-body">Each player has 2 minutes per turn. If your opponent's timer runs out, you win.</p>
|
|
</div>
|
|
<div class="rule-card">
|
|
<div class="rule-icon">🔌</div>
|
|
<h3 class="rule-title">Disconnect</h3>
|
|
<p class="rule-body">If your opponent disconnects and does not reconnect within 15 seconds, you win.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</main>
|
|
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Crimson+Text:ital,wght@0,400;0,600;1,400&display=swap');
|
|
|
|
main {
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
background: #0d0a04;
|
|
}
|
|
|
|
.content {
|
|
max-width: 1000px;
|
|
margin: 0 auto;
|
|
padding: 2rem;
|
|
}
|
|
|
|
.page-title {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: #f0d080;
|
|
margin: 0 0 2rem;
|
|
letter-spacing: 0.08em;
|
|
}
|
|
|
|
.section {
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.section-title {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: #f0d080AA;
|
|
margin: 0 0 1.25rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 1px solid #f0d08055;
|
|
}
|
|
|
|
.body-text ul {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
.body-text {
|
|
font-family: 'Crimson Text', serif;
|
|
font-size: 17px;
|
|
color: rgba(240, 180, 80, 0.75);
|
|
line-height: 1.7;
|
|
margin: 0 0 1rem;
|
|
}
|
|
|
|
/* ── Card explainer ── */
|
|
.card-explainer {
|
|
display: flex;
|
|
gap: 3rem;
|
|
align-items: flex-start;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.card-annotated {
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.card-display {
|
|
position: relative;
|
|
}
|
|
|
|
.markers {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.marker {
|
|
position: absolute;
|
|
transform: translate(-50%, -50%);
|
|
z-index: 10;
|
|
}
|
|
|
|
.marker-bubble {
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
background: #c8861a;
|
|
border: 2px solid #fff;
|
|
color: #fff;
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.6);
|
|
}
|
|
|
|
/* ── Annotation list ── */
|
|
.annotation-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.9rem;
|
|
flex: 1;
|
|
min-width: 260px;
|
|
}
|
|
|
|
.annotation-list li {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.annotation-number {
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
background: #c8861a;
|
|
color: #fff;
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.annotation-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
.annotation-label {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
color: #f0d080;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.annotation-desc {
|
|
font-family: 'Crimson Text', serif;
|
|
font-size: 14px;
|
|
color: rgba(240, 180, 80, 0.6);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* ── Rules grid ── */
|
|
.rules-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
|
|
.rule-card {
|
|
background: #1a1008;
|
|
border: 1px solid rgba(107, 76, 30, 0.3);
|
|
border-radius: 8px;
|
|
padding: 1.25rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.rule-icon {
|
|
color: #f0d080AA;
|
|
font-size: 20px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.rule-title {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.06em;
|
|
color: #f0d080;
|
|
margin: 0;
|
|
}
|
|
|
|
.rule-body {
|
|
font-family: 'Crimson Text', serif;
|
|
font-size: 14px;
|
|
color: rgba(240, 180, 80, 0.6);
|
|
line-height: 1.55;
|
|
margin: 0;
|
|
}
|
|
|
|
/* ── Demo card ── */
|
|
.demo-card {
|
|
width: 300px;
|
|
border-radius: 12px;
|
|
padding: 7px;
|
|
background: #111;
|
|
border: 2px solid #111;
|
|
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
|
|
font-family: 'Crimson Text', serif;
|
|
position: relative;
|
|
user-select: none;
|
|
}
|
|
|
|
.demo-inner {
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
background: var(--bg);
|
|
border: 2px solid #000;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.demo-header {
|
|
padding: 9px 12px 7px;
|
|
background: var(--header);
|
|
border-bottom: 2px solid #000;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: 8px;
|
|
}
|
|
|
|
.demo-name {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: #fff;
|
|
text-shadow: 0 1px 3px rgba(0,0,0,0.6);
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.demo-type-badge {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 9px;
|
|
color: rgba(255,255,255,0.95);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
background: rgba(0,0,0,0.25);
|
|
padding: 1px 5px;
|
|
border-radius: 3px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.demo-image-wrap {
|
|
position: relative;
|
|
width: 100%;
|
|
aspect-ratio: 4/3;
|
|
overflow: hidden;
|
|
border-bottom: 2px solid #000;
|
|
}
|
|
|
|
.demo-image {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
object-position: top;
|
|
display: block;
|
|
}
|
|
|
|
.demo-rarity {
|
|
position: absolute;
|
|
top: 7px;
|
|
left: 7px;
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 50%;
|
|
border: 2.5px solid #000;
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 3;
|
|
}
|
|
|
|
.demo-wiki {
|
|
position: absolute;
|
|
top: 7px;
|
|
right: 7px;
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 50%;
|
|
background: rgba(255,255,255,0.92);
|
|
border: 1.5px solid #000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 3;
|
|
}
|
|
|
|
.demo-cost-bubbles {
|
|
position: absolute;
|
|
bottom: 6px;
|
|
left: 8px;
|
|
display: flex;
|
|
gap: 3px;
|
|
flex-wrap: wrap;
|
|
max-width: calc(100% - 16px);
|
|
}
|
|
|
|
.demo-cost-bubble {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: #6ea0ec;
|
|
border: 2.5px solid #000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #08152c;
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
font-family: 'Cinzel', serif;
|
|
line-height: 1;
|
|
}
|
|
|
|
.demo-divider {
|
|
height: 2px;
|
|
background: #000;
|
|
}
|
|
|
|
.demo-text {
|
|
padding: 10px 12px;
|
|
font-size: 13px;
|
|
line-height: 1.55;
|
|
color: #1a1208;
|
|
font-style: italic;
|
|
background: #f0e6cc;
|
|
border-bottom: 2px solid #000;
|
|
height: 110px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.demo-footer {
|
|
padding: 7px 12px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.demo-stat {
|
|
font-family: 'Cinzel', serif;
|
|
font-size: 11px;
|
|
color: #2a2010;
|
|
letter-spacing: 0.03em;
|
|
}
|
|
|
|
.demo-stat strong {
|
|
color: #000;
|
|
font-size: 15px;
|
|
}
|
|
|
|
.demo-date {
|
|
font-size: 10px;
|
|
color: rgba(0,0,0,0.5);
|
|
font-style: italic;
|
|
font-family: 'Crimson Text', serif;
|
|
}
|
|
</style> |